W jaki sposób skonfigurować relację jeden-do-jeden?

0

Proszę o podpowiedź konfiguracji relacji jeden do jednego. Klucze FirstName i SecondName do encji Name.

public class User
{
    public int UserID { get; set; }
    public int FirstNameFK { get; set; }
    public int SecondNameFK { get; set; }
    //....

    public Name Name { get; set; }
}
public class Name
{
    public int NameID { get; set; }
    public string UserName { get; set; }

    public User User { get; set; }
}

tak próbuje skonfigurować tę relację z pomocą Fluent API

modelBuilder.Entity<User>(u =>
{
    u.HasKey(k => k.UserID);
    u.HasOne(h => h.Name).
        WithOne(w => w.User).
        HasForeignKey<User>(h => h.FirstNameFK);

    u.HasKey(k => k.UserID);
    u.HasOne(h => h.Name).
        WithOne(w => w.User).
        HasForeignKey<User>(h => h.SecondNameFK);
});

Bardzo proszę o pomoc w prawidłowej konfiguracji

1

Załóżmy że masz: User{UserID=1,FirstNameFK=20,SecondNameFK=30,Name{???}}
zaś to Name{???} ma być:
Name{NameID=20,UserName="Piotr",User{???}}
czy też:
Name{NameID=30,UserName="Olczyk",User{???}}
?
A te dwa User{???} to mają być kolejne obiekty takie same jak ten pierwszy?

0
_13th_Dragon napisał(a):

Załóżmy że masz: User{UserID=1,FirstNameFK=20,SecondNameFK=30,Name{???}}
zaś to Name{???} ma być:
Name{NameID=20,UserName="Piotr",User{???}}
czy też:
Name{NameID=30,UserName="Olczyk",User{???}}
?
A te dwa User{???} to mają być kolejne obiekty takie same jak ten pierwszy?

Mam 1000 nazwisk userów którzy mają dwa imiona. Chcę w tabeli User dociągać dane o pierwszym i drugim imieniu z tabeli Name, dlatego zdefiniowałem dwa FK które będą różne bo będą wskazywać na różne rekordy w tabeli Name. W tabeli User jest więcej pól które w przykładowym kodzie jest symbolizowane przez

//...

w tabeli User jest między innymi również pole LastName.

Po zapytaniu bazy

var result = contextDB.User.Include(i => i.Name).Where(w => w.LastName == "Olczyk")

chcę otrzymać odpowiedź Olczyk Piotr Paweł

0
piotrOlczyk napisał(a):
_13th_Dragon napisał(a):

Załóżmy że masz: User{UserID=1,FirstNameFK=20,SecondNameFK=30,Name{???}}
zaś to Name{???} ma być:
Name{NameID=20,UserName="Piotr",User{???}}
czy też:
Name{NameID=30,UserName="Olczyk",User{???}}
?
A te dwa User{???} to mają być kolejne obiekty takie same jak ten pierwszy?

Mam 1000 nazwisk userów którzy mają dwa imiona. Chcę w tabeli User dociągać dane o pierwszym i drugim imieniu z tabeli Name, dlatego zdefiniowałem dwa FK które będą różne bo będą wskazywać na różne rekordy w tabeli Name. W tabeli User jest więcej pól które w przykładowym kodzie jest symbolizowane przez

//...

w tabeli User jest między innymi również pole LastName.

Po zapytaniu bazy

var result = contextDB.User.Include(i => i.Name).Where(w => w.LastName == "Olczyk")

chcę otrzymać odpowiedź Olczyk Piotr Paweł

piotrOlczyk napisał(a):
_13th_Dragon napisał(a):

Załóżmy że masz: User{UserID=1,FirstNameFK=20,SecondNameFK=30,Name{???}}
zaś to Name{???} ma być:
Name{NameID=20,UserName="Piotr",User{???}}
czy też:
Name{NameID=30,UserName="Olczyk",User{???}}
?
A te dwa User{???} to mają być kolejne obiekty takie same jak ten pierwszy?

Mam 1000 nazwisk userów którzy mają dwa imiona. Chcę w tabeli User dociągać dane o pierwszym i drugim imieniu z tabeli Name, dlatego zdefiniowałem dwa FK które będą różne bo będą wskazywać na różne rekordy w tabeli Name. W tabeli User jest więcej pól które w przykładowym kodzie jest symbolizowane przez

//...

w tabeli User jest między innymi również pole LastName.

Po zapytaniu bazy

var result = contextDB.User.Include(i => i.Name).Where(w => w.LastName == "Olczyk")

chcę otrzymać odpowiedź Olczyk Piotr Paweł

Inicjalizacja bazy danymi przykładowy rekord User będzie wyglądał

    new User (UserID = 1, FirstNameFK = 1, SecondNameFK = 2, LastName = "Olczyk")

rekordy Name

    new Name (NameID = 1, UserName = "Piotr")
    new Name (NameID = 2, UserName = "Pawel")

właściwości

    public Name Name { get; set; }

oraz

    public User User { get; set; }

to właściwości nawigacyjne które nie są odzwierciedlane w tabelach bazy wynikają z przyjętych konwencji EF.

0

właściwości

    public Name Name { get; set; }

oraz

    public User User { get; set; }

to właściwości nawigacyjne które nie są odzwierciedlane w tabelach bazy wynikają z przyjętych konwencji EF.

Tych właściwości się nie inicjuje bo służą do wskazania relacji. To są właściwości nawigacyjne.

1
piotrOlczyk napisał(a):

Tych właściwości się nie inicjuje bo służą do wskazania relacji. To są właściwości nawigacyjne.

Nie zupełnie.
Chciałem aby sam do tego doszedłeś, ale tupet ci nie pozwala :/
Masz sprzeczne wymagania do pola User.Name w modelBuilder.Entity<User>(
https://stackoverflow.com/questions/5559043/entity-framework-code-first-two-foreign-keys-from-same-table

0

Nie wiem gdzie popełniam błąd. Proszę o podanie prawidłowej konfiguracji.

_13th_Dragon napisał(a):
piotrOlczyk napisał(a):

Tych właściwości się nie inicjuje bo służą do wskazania relacji. To są właściwości nawigacyjne.

Nie zupełnie.
Chciałem aby sam do tego doszedłeś, ale tupet ci nie pozwala :/
Masz sprzeczne wymagania do pola User.Name w modelBuilder.Entity<User>(
https://stackoverflow.com/questions/5559043/entity-framework-code-first-two-foreign-keys-from-same-table

Proszę o podanie prawidłowej konfiguracji.

0
piotrOlczyk napisał(a):

Nie wiem gdzie popełniam błąd. Proszę o podanie prawidłowej konfiguracji.

piotrOlczyk napisał(a):

Proszę o podanie prawidłowej konfiguracji.

_13th_Dragon proszę podaj prawidłową konfigurację.

0

_13th_Dragon proszę podaj prawidłową konfigurację.

_13th_Dragon ponownie Proszę o podanie prawidłowej konfiguracji relacji o której mowa wyżej.

0

Dobrze widzę? masz 2 FK które wskazują na to samo property?

0
WeiXiao napisał(a):

Dobrze widzę? masz 2 FK które wskazują na to samo property?

public class User
    {
        public int UserID { get; set; }

        public Name FirstName { get; set; }
        public Name SecondName { get; set; }
    }

    public class Name
    {
        public int NameID { get; set; }
        public string UserName { get; set; }

        public int FirstNameFK { get; set; }
        public int SecondNameFK { get; set; }

        public User User { get; set; }
    }

    modelBuilder.Entity<User>(u =>
            {
                u.HasKey(k => k.UserID);
                u.HasOne(h => h.FirstName).
                    WithOne(w => w.User).
                    HasForeignKey<Name>(w => w.FirstNameFK);

                u.HasKey(k => k.UserID);
                u.HasOne(h => h.SecondName).
                    WithOne(w => w.User).
                    HasForeignKey<Name>(w => w.SecondNameFK).
                    OnDelete(DeleteBehavior.NoAction);
            });

poprawiłem konfigurację tak jak wyżej podczas add-migration mam taki komunikat:
Cannot create a relationship between 'Name.User' and 'User.SecondName' because a relationship already exists between 'User.FirstName' and 'Name.User'. Navigations can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'User.SecondName' first in 'OnModelCreating'.

0

Cannot create a relationship between 'Name.User' and 'User.SecondName' because a relationship already exists between 'User.FirstName' and 'Name.User'. Navigations can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'User.SecondName' first in 'OnModelCreating'.

Komunikat jednoznacznie wskazuje że nie można utworzyć relacji między dwoma tymi samymi encjami używając dwu kluczy obcych. Konfiguracja dwu różnych kluczy obcych między dwoma encjami jest niemożliwa.

1

O to chodzi?

using Microsoft.EntityFrameworkCore;

using (var ctx = new Context())
{
    ctx.Database.EnsureDeleted();
    ctx.Database.EnsureCreated();

    var name1 = new Name { UserName = "Test1" };
    var name2 = new Name { UserName = "Test2" };

    ctx.Names.Add(name1);
    ctx.Names.Add(name2);

    var user = new User();
    user.FirstNameId = name1.NameId;
    user.SecondNameId = name2.NameId;

    ctx.Users.Add(user);

    ctx.SaveChanges();
}

using (var ctx = new Context())
{
    foreach (var entry in ctx.Users.ToList())
    {
        Console.WriteLine(entry.UserId);
        Console.WriteLine(entry.FirstNameId);
        Console.WriteLine(entry.SecondNameId);
        Console.WriteLine(entry.FirstName?.UserName);
        Console.WriteLine(entry.SecondName?.UserName);
    }

    Console.WriteLine();

    foreach (var entry in ctx.Users.Include(x => x.FirstName).ToList())
    {
        Console.WriteLine(entry.UserId);
        Console.WriteLine(entry.FirstNameId);
        Console.WriteLine(entry.SecondNameId);
        Console.WriteLine(entry.FirstName?.UserName);
        Console.WriteLine(entry.SecondName?.UserName);
    }

    Console.WriteLine();

    foreach (var entry in ctx.Users.Include(x => x.FirstName).Include(x => x.SecondName).ToList())
    {
        Console.WriteLine(entry.UserId);
        Console.WriteLine(entry.FirstNameId);
        Console.WriteLine(entry.SecondNameId);
        Console.WriteLine(entry.FirstName?.UserName);
        Console.WriteLine(entry.SecondName?.UserName);
    }
}

public class Context : DbContext
{
    public DbSet<Name> Names { get; set; }
    public DbSet<User> Users { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("filename=test.db");
        base.OnConfiguring(optionsBuilder);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>(u =>
        {
            u.HasOne(x => x.FirstName).WithOne().HasForeignKey<User>(x => x.FirstNameId);
            u.HasOne(x => x.SecondName).WithOne().HasForeignKey<User>(x => x.SecondNameId);
        });
        base.OnModelCreating(modelBuilder);
    }
}

public class User
{
    public Guid UserId { get; set; } = Guid.NewGuid();

    public Guid FirstNameId { get; set; }
    public Name FirstName { get; set; }

    public Guid SecondNameId { get; set; }
    public Name SecondName { get; set; }
}

public class Name
{
    public Guid NameId { get; set; } = Guid.NewGuid();
    public string UserName { get; set; }
}
3e764c0c-758b-4acb-8a6d-f9270762c23b
6d51c4bb-25e1-4be0-b0ee-65d5afd7ed4d
e180c538-3304-4691-924b-bc850c295d7a



3e764c0c-758b-4acb-8a6d-f9270762c23b
6d51c4bb-25e1-4be0-b0ee-65d5afd7ed4d
e180c538-3304-4691-924b-bc850c295d7a
Test1


3e764c0c-758b-4acb-8a6d-f9270762c23b
6d51c4bb-25e1-4be0-b0ee-65d5afd7ed4d
e180c538-3304-4691-924b-bc850c295d7a
Test1
Test2
0
    public class Name
    {
        public Name(string userName)
        {
            UserName = userName;
        }  
        
        public int NameID { get; set; }
        
        public string UserName { get; set; }

        public User User { get; set; }
    }
    
    public class User
    {
        public User(int firstNameFK, int secondNameFK, string lastName)
        { 
            FirstNameFK = firstNameFK; 
            SecondNameFK = secondNameFK;
            LastName = lastName;
        }
        public int UserID { get; set; }

        public string LastName { get; set; }
        
        public int FirstNameFK { get; set; }
        public Name FirstName { get; set; }

        public int SecondNameFK { get; set; }
        public Name SecondName { get; set; }
    }

    public class FluentApiDbContext : DbContext
    {
        public FluentApiDbContext(DbContextOptions<FluentApiDbContext> options) : base(options)
        {

        }
        public DbSet<User> Users { get; set; }
        public DbSet<Name> Names { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>(u =>
            {
                u.HasOne(h => h.FirstName).WithOne().HasForeignKey<User>(h => h.FirstNameFK);
                u.HasOne(h => h.SecondName).WithOne().HasForeignKey<User>(h => h.SecondNameFK);       
            });
        }
    }
    
    public static class DbInitializer
    {
        public static void Initialize(FluentApiDbContext context)
        {
            context.Database.EnsureCreated();

            if (!context.Users.Any())
            {
                List<User> users = new()
                {
                    new User (1,2,"Olczyk"),
                    new User (3,1,"Kowalski"),
                };
                context.AddRange(users);
                context.SaveChanges();
            }
            if (!context.Names.Any())
            {
                List<Name> adresses = new()
                {
                    new Name ("Piotr"),
                    new Name ("Pawel"),
                    new Name ("Jan")
                };
                context.AddRange(adresses);
                context.SaveChanges();
            }
        }

        //fragment program.cs
        ... 
        app.MapGet("/getUsers", (FluentApiDbContext contextDB) =>
        {
          var result = contextDB.Users.Include(i => i.FirstName).Include(i => i.SecondName).ToList();
          return result;
        })
        ...
    }

taki mam komunikat w trakcie tworzenia DB

Failed executing DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE [Users] (
[UserID] int NOT NULL IDENTITY,
[LastName] nvarchar(max) NOT NULL,
[FirstNameFK] int NOT NULL,
[SecondNameFK] int NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY ([UserID]),
CONSTRAINT [FK_Users_Names_FirstNameFK] FOREIGN KEY ([FirstNameFK]) REFERENCES [Names] ([NameID]) ON DELETE CASCADE,
CONSTRAINT [FK_Users_Names_SecondNameFK] FOREIGN KEY ([SecondNameFK]) REFERENCES [Names] ([NameID]) ON DELETE CASCADE
);
Microsoft.Data.SqlClient.SqlException (0x80131904): Introducing FOREIGN KEY constraint 'FK_Users_Names_SecondNameFK' on table 'Users' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.

Możliwe że to jest kwestia bazy. Ty robisz to na SQLite a ja na Microsoft LocalDB Visual Studio 2022.

1

@piotrOlczyk:

a próbowałeś w ogóle rozwiązać w/w error? SQL Server chce abyś określił co się stanie po zrobieniu DELETE

W poniższym przypadku gdy spróbujesz usunąć name, to się wywali,
ale gdy usuniesz Usera, to one zostaną. Zdziwiło mnie trochę że nie mogłem tu w obu przypadkach dać DeleteBehavior.Cascade, a ten drugi FK wymagał NoAction

modelBuilder.Entity<User>(u =>
{
    u.HasOne(x => x.FirstName).WithOne().HasForeignKey<User>(x => x.FirstNameId).OnDelete(DeleteBehavior.NoAction);
    u.HasOne(x => x.SecondName).WithOne().HasForeignKey<User>(x => x.SecondNameId).OnDelete(DeleteBehavior.NoAction);
});

screenshot-20230107175223.png
screenshot-20230107175233.png
screenshot-20230107175253.png
screenshot-20230107175309.png

using Microsoft.EntityFrameworkCore;

using (var ctx = new Context())
{
    ctx.Database.EnsureDeleted();
    ctx.Database.EnsureCreated();

    var name1 = new Name { UserName = "Test1" };
    var name2 = new Name { UserName = "Test2" };

    ctx.Names.Add(name1);
    ctx.Names.Add(name2);

    var user = new User();
    user.FirstNameId = name1.NameId;
    user.SecondNameId = name2.NameId;

    ctx.Users.Add(user);

    ctx.SaveChanges();
}

using (var ctx = new Context())
{
    foreach (var entry in ctx.Users.ToList())
    {
        Console.WriteLine(entry.UserId);
        Console.WriteLine(entry.FirstNameId);
        Console.WriteLine(entry.SecondNameId);
        Console.WriteLine(entry.FirstName?.UserName);
        Console.WriteLine(entry.SecondName?.UserName);
    }

    Console.WriteLine();

    foreach (var entry in ctx.Users.Include(x => x.FirstName).ToList())
    {
        Console.WriteLine(entry.UserId);
        Console.WriteLine(entry.FirstNameId);
        Console.WriteLine(entry.SecondNameId);
        Console.WriteLine(entry.FirstName?.UserName);
        Console.WriteLine(entry.SecondName?.UserName);
    }

    Console.WriteLine();

    foreach (var entry in ctx.Users.Include(x => x.FirstName).Include(x => x.SecondName).ToList())
    {
        Console.WriteLine(entry.UserId);
        Console.WriteLine(entry.FirstNameId);
        Console.WriteLine(entry.SecondNameId);
        Console.WriteLine(entry.FirstName?.UserName);
        Console.WriteLine(entry.SecondName?.UserName);
    }
}

public class Context : DbContext
{
    public DbSet<Name> Names { get; set; }
    public DbSet<User> Users { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        //optionsBuilder.UseSqlite("filename=test.db");
        optionsBuilder.UseSqlServer("Server=.\\SQLEXPRESS;Database=myDataBase;Trusted_Connection=True;Encrypt=False;");
        base.OnConfiguring(optionsBuilder);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>(u =>
        {
            u.HasOne(x => x.FirstName).WithOne().HasForeignKey<User>(x => x.FirstNameId).OnDelete(DeleteBehavior.NoAction);
            u.HasOne(x => x.SecondName).WithOne().HasForeignKey<User>(x => x.SecondNameId).OnDelete(DeleteBehavior.NoAction);
        });
        base.OnModelCreating(modelBuilder);
    }
}

public class User
{
    public Guid UserId { get; set; } = Guid.NewGuid();

    public Guid FirstNameId { get; set; }
    public Name FirstName { get; set; }

    public Guid SecondNameId { get; set; }
    public Name SecondName { get; set; }
}

public class Name
{
    public Guid NameId { get; set; } = Guid.NewGuid();
    public string UserName { get; set; }
}
0
    ...
    modelBuilder.Entity<User>(u =>
    {
      u.HasOne(h => h.FirstName).WithOne().HasForeignKey<User>(h => h.FirstNameFK).
      OnDelete(DeleteBehavior.NoAction);
      u.HasOne(h => h.SecondName).WithOne().HasForeignKey<User>(h => h.SecondNameFK).
      OnDelete(DeleteBehavior.NoAction);       
    });
    ...

add-migration Init
Build started...
Build succeeded.
Microsoft.EntityFrameworkCore.Model.Validation[10625]
The foreign key property 'Name.UserID1' was created in shadow state because a conflicting property with the simple name 'UserID' exists in the entity type, but is either not mapped, is already used for another relationship, or is incompatible with the associated primary key type. See https://aka.ms/efcore-relationships for information on mapping relationships in EF Core.
Microsoft.EntityFrameworkCore.Model.Validation[10625]
The foreign key property 'Name.UserID1' was created in shadow state because a conflicting property with the simple name 'UserID' exists in the entity type, but is either not mapped, is already used for another relationship, or is incompatible with the associated primary key type. See https://aka.ms/efcore-relationships for information on mapping relationships in EF Core.
The foreign key property 'Name.UserID1' was created in shadow state because a conflicting property with the simple name 'UserID' exists in the entity type, but is either not mapped, is already used for another relationship, or is incompatible with the associated primary key type. See https://aka.ms/efcore-relationships for information on mapping relationships in EF Core.

po wykonaniu migracji mam ostrzeżenia jak wyżej

po próbie utworzenia DB mam

fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
Failed executing DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
ALTER TABLE [Names] ADD CONSTRAINT [FK_Names_Users_UserID1] FOREIGN KEY ([UserID1]) REFERENCES [Users] ([UserID]) ON DELETE CASCADE;
Failed executing DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
ALTER TABLE [Names] ADD CONSTRAINT [FK_Names_Users_UserID1] FOREIGN KEY ([UserID1]) REFERENCES [Users] ([UserID]) ON DELETE CASCADE;
Microsoft.Data.SqlClient.SqlException (0x80131904): Introducing FOREIGN KEY constraint 'FK_Names_Users_UserID1' on table 'Names' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.

czyli nie pomogło.

1

u mnie się tworzy, a shadow properties chciałem :P

0
WeiXiao napisał(a):

u mnie się tworzy, a shadow properties chciałem :P

U Ciebie się tworzy zdefiniowana baza ma SQLite czy na Microsoft LocalDB? Dodałeś "OnDelete(DeleteBehavior.NoAction)""

0
piotrOlczyk napisał(a):
WeiXiao napisał(a):

u mnie się tworzy, a shadow properties chciałem :P

U Ciebie się tworzy zdefiniowana baza ma SQLite czy na Microsoft LocalDB? Dodałeś "OnDelete(DeleteBehavior.NoAction)""

Działa! WeiXiao pomógł mi. Bardzo Ci dziękuję!

0

Dla wszystkich którym przeszkadza shadow properties podaje konfigurację.

public class Name
{
  public int NameID { get; set; }
  public string UserName { get; set; }
  
  public User User1 { get; set; }
  public User User2 { get; set; }
}

public class User
{ 
  public int UserID { get; set; }

  public string LastName { get; set; }

  public int FirstNameFK { get; set; }
  public Name FirstName { get; set; }
        
  public int SecondNameFK { get; set; }
  public Name SecondName { get; set; }
}

    
  modelBuilder.Entity<User>(u =>
  {
  u.HasOne(h => h.FirstName).WithOne(w => w.User1).HasForeignKey<User>(h => h.FirstNameFK).OnDelete(DeleteBehavior.NoAction);
  u.HasOne(h => h.SecondName).WithOne(w => w.User2).HasForeignKey*User>(h => h.SecondNameFK).OnDelete(DeleteBehavior.NoAction);
  });

pozdrawiam

1 użytkowników online, w tym zalogowanych: 0, gości: 1