Entity Framework: One to one issue in TPC hierarchy - entity-framework

I have a TPC hierarchy like this:
public abstract class Base
{
public Guid BaseId { get; set; }
public string BaseName { get; set; }
}
public class Sub1 : Base
{
public Sub2 Vender { get; set; }
}
public class Sub2 : Base
{
public Sub1 Customer { get; set; }
}
public class MyDbContext : DbContext
{
public MyDbContext()
: base("name=db")
{ }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Sub1>().HasRequired(x => x.Vender).WithOptional(x => x.Customer);
modelBuilder.Entity<Sub1>().Map(x =>
{
x.ToTable("Sub1s");
x.MapInheritedProperties();
}).HasKey(x => x.BaseId);
modelBuilder.Entity<Sub2>().Map(x =>
{
x.ToTable("Sub2s");
x.MapInheritedProperties();
}).HasKey(x => x.BaseId);
}
public DbSet<Base> People { get; set; }
public DbSet<Sub2> Venders { get; set; }
public DbSet<Sub1> Customers { get; set; }
}
Then I run the code and EF create tables (Sub1s and Sub2s).
But EF create an aditional Foreign Key for table Sub1, it do not use the Key of Sub1 as the Foreign Key..It generates an additional FK named Vender_BaseId..

Related

EF Core Many-to-Many Relation Table Naming [duplicate]

This question already has answers here:
Change name of generated Join table ( Many to Many ) - EF Core 5
(2 answers)
Closed 1 year ago.
Does EF Core provide a way of naming the many-to-many relations mapping to database tables ?
In a code-first pattern, I have the following 2 Entities:
[Table("Prefix.Users")]
public class User
{
public int ID { get; set; }
public IEnumerable<Role> Roles { get; set; }
}
[Table("Prefix.Roles")]
public class Role
{
public int ID { get; set; }
public IEnumerable<User> Users { get; set; }
}
I've skipped the detailed Entity structure here. The ID properties in User & Role are keys (Database generated Identity)
User and Role entities share a many-to-many relationship.
EF Core generates a third table in Database with Table name UsersRoles
Is there a way I can add a prefix to the 3rd table name so it becomes Prefix.UsersRoles without manually adding a third Entity UserRoles that maps User and Role and giving it the desired name with Prefix
Use fluent API instead of using data annotations
Your model classes should be like this.
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; }
}
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; }
}
public class UserRole
{
public int UserId { get; set; }
public int RoleId { get; set; }
public virtual User User { get; set; }
public virtual Role Role { get; set; }
}
Your fluent api configuration classes like be this
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("User");
builder.HasKey(x => x.Id);
}
}
public class RoleConfiguration : IEntityTypeConfiguration<Role>
{
public void Configure(EntityTypeBuilder<Role> builder)
{
builder.ToTable("Role");
builder.HasKey(x => x.Id);
}
}
public class UserRoleConfiguration : IEntityTypeConfiguration<UserRole>
{
public void Configure(EntityTypeBuilder<UserRole> builder)
{
builder.ToTable("UserRole");
builder.HasKey(x => new { x.UserId, x.RoleId });
builder
.HasOne<Role>(s => s.Role)
.WithMany(r => r.UserRoles)
.HasForeignKey(s => s.RoleId).OnDelete(DeleteBehavior.Restrict);
builder
.HasOne<User>(s => s.User)
.WithMany(r => r.UserRoles)
.HasForeignKey(s => s.UserId).OnDelete(DeleteBehavior.Restrict);
}
}
Your DbContext class should be like this
public class MyDbContext : DbContext
{
public EEGDbContext()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(#"Server=xxxx;Database=DB;User Id=sa;Password=xxxxx;");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new UserConfiguration());
modelBuilder.ApplyConfiguration(new RoleConfiguration());
modelBuilder.ApplyConfiguration(new UserRoleConfiguration());
base.OnModelCreating(modelBuilder);
}
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<UserRole> UserRoles { get; set; }
}

Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints

I am facing the following issue in entity framework core.
Introducing FOREIGN KEY constraint 'FK_MasterCountries_MasterLanguages_LanguageId' on table 'MasterCountries' 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.
I have the following three entities:
1. MasterCountry
public class MasterCountry
{
public long Id { get; set; }
public string Name { get; set; }
public long CurrencyId { get; set; }
public int LanguageId { get; set; }
public bool IsActive { get; set; }
// Navigation Properties
public MasterCurrency MasterCurrency { get; set; }
public MasterLanguage MasterLanguage { get; set; }
}
2. MasterCurrency
public class MasterCurrency
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
// Navigation Property
public MasterCountry MasterCountry { get; set; }
}
3. MasterLanguage
public class MasterLanguage
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
// Navigation Property
public ICollection<MasterCountry> MasterCountries { get; set; }
}
The DbContext class looks like the following:
public partial class NewProjectDbContext : DbContext
{
public DbSet<MasterLanguage> MasterLanguages { get; set; }
public DbSet<MasterCurrency> MasterCurrencies { get; set; }
public DbSet<MasterCountry> MasterCountries { get; set; }
public YouTemaDbContext(DbContextOptions<YouTemaDbContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder builder)
{
builder
.ApplyConfiguration(new MasterCurrencyConfiguration());
builder
.ApplyConfiguration(new MasterLanguageConfiguration());
builder
.ApplyConfiguration(new MasterCountryConfiguration());
}
}
The configuration classes look like the following:
1. MasterCountryConfiguration
public class MasterCountryConfiguration : IEntityTypeConfiguration<MasterCountry>
{
public void Configure(EntityTypeBuilder<MasterCountry> builder)
{
builder.HasKey(m => m.Id);
builder.Property(m => m.Id).UseIdentityColumn();
builder.Property(m => m.Name).IsRequired().HasMaxLength(250).IsUnicode();
builder.Property(m => m.IsActive).IsRequired().HasDefaultValue(0);
builder.Property(m => m.LanguageId).IsRequired();
builder.Property(m => m.CurrencyId).IsRequired();
builder
.HasOne(m => m.MasterLanguage)
.WithMany(a => a.MasterCountries)
.HasForeignKey(m => m.LanguageId);
builder
.HasOne(m => m.MasterCurrency)
.WithOne(a => a.MasterCountry)
.HasForeignKey<MasterCountry>(m => m.CurrencyId);
builder.ToTable("MasterCountries");
}
}
2. MasterCurrencyConfiguration
public class MasterCurrencyConfiguration : IEntityTypeConfiguration<MasterCurrency>
{
public void Configure(EntityTypeBuilder<MasterCurrency> builder)
{
builder.HasKey(m => m.Id);
builder.Property(m => m.Id).UseIdentityColumn();
builder.Property(m => m.Name).IsRequired().HasMaxLength(250).IsUnicode();
builder.Property(m => m.IsActive).IsRequired().HasDefaultValue(0);
builder.ToTable("MasterCurrencies");
}
}
3. MasterLanguageConfiguration
public void Configure(EntityTypeBuilder<MasterLanguage> builder)
{
builder.HasKey(m => m.Id);
builder.Property(m => m.Id).UseIdentityColumn();
builder.Property(m => m.Name).IsRequired().HasMaxLength(250).IsUnicode();
builder.Property(m => m.IsActive).IsRequired().HasDefaultValue(0);
builder.ToTable("MasterLanguages");
}
}
But while running the migration file, I get the following issue for MasterCurrency and MasterLanguage entities.
CONSTRAINT [FK_MasterCountries_MasterCurrencies_CurrencyId] FOREIGN KEY ([CurrencyId]) REFERENCES [MasterCurrencies] ([Id]) ON DELETE CASCADE
CONSTRAINT [FK_MasterCountries_MasterLanguages_LanguageId] FOREIGN KEY ([LanguageId]) REFERENCES [MasterLanguages] ([Id]) ON DELETE CASCADE
I know that I have to set Cascade delete to No Action but since I am new to entity framework core so I am not sure where I have to do this.
Your help on this will be highly appreciated.
Thanks.

EF Core TPH Discriminator ignores base entities

I have two DbContext. A BaseDbContext and one that inherits from the BaseDbContextcalled FemaleDbContext.
public class BaseDbContext : DbContext
{
public BaseDbContext(DbContextOptions options) : base(options) { }
public virtual DbSet<Person> Person { get; set; }
public virtual DbSet<House> House { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().ToTable("Person", "dbo");
modelBuilder.Entity<House>().ToTable("House", "dbo");
modelBuilder.Entity<Person>().HasOne(e => e.House).WithMany(e => e.Persons);
modelBuilder.Entity<House>().HasMany(e => e.Persons).WithOne(e => e.House);
}
}
The goal is to extend the Person entity with another property. I do not want to use shadow properties because its too dynamic. So I am trying to make it work using TPH. Here is my other context:
public class FemaleDbContext : BaseDbContext
{
public DbSet<Female> Female { get; set; }
public FemaleDbContext(DbContextOptions<FemaleDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Female>().HasBaseType<Person>();
base.OnModelCreating(modelBuilder);
}
}
As you can see, my sub-context should use the Female entity instead of the Person. The problem is that when I run this.Context.Female.ToList() on my SubDbContext, only entities with the value Female inside the Discriminator field inside my database are returned. Entities with the value Person in that table are returned. But I want to get every entity.
Also, here are my entities:
public class Person
{
public int Id { get; set; }
public string Firstname { get; set; }
public string Middlename { get; set; }
public string Lastname { get; set; }
}
public class Female : Person
{
public bool? IsPregnant { get; set; }
}
How can I configure my DbContext that this.Context.Female.ToList() returns both Females and Persons. Note that this.Context.Person.ToList() already returns everything, not only Persons

How to map many-to-many with Code First to same class?

I use Code First with Entity Framework 5.
I have User class, where one user can be friends with many people.
public class User
{
[Key]
public Guid UserID { get; set; }
public virtual ICollection<User> Friends { get; set; }
}
This however maps 0..1-to-many. How should I map many-to-many relationship with the same class in Code First?
Add configuration class:
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasMany(u => u.Friends).WithMany();
}
}
then, this needs to be added to context class
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserConfiguration());
base.OnModelCreating(modelBuilder);
}
}
public class Organization : Entity
{
public string Name { get; set; }
public string Description { get; set; }
public virtual Organization Parent { get; set; }
public virtual ICollection<Organization> Children { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class OrganizationConfiguration : EntityMapperBase<Organization>
{
public OrganizationConfiguration()
{
HasKey(f => f.Id);
Property(f => f.Name).HasMaxLength(20).IsRequired();
HasMany(f => f.Children).WithOptional(f => f.Parent).Map(m => m.MapKey("ParentId")).WillCascadeOnDelete(false);
HasMany(f => f.Users).WithRequired(f => f.Organization).Map(m => m.MapKey("OrganizationId"));
}
}
may it help you
You should have tow navigation properties
public class User
{
[Key]
public Guid UserID { get; set; }
public virtual ICollection<User> FriendsOfMine { get; set; }
public virtual ICollection<User> FriendsWithMe { get; set; }
}

How do I force generated many-to-many Relation tables to the correct schema?

I have tables parkpay.User and parkpay.Role. EF Code First automatically generates a third table linking the two for a many-to-many relationship, but it generates dbo.UserRole. How do I get it to make that table `parkpay.UserRole'?
Use EntityTypeConfiguration<> config many-to-many mappings.
public class User
{
public long Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
public class Role
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class UserMapping : EntityTypeConfiguration<User>
{
public UserMapping()
{
ToTable("User", "parkpay");
HasKey(e => e.Id).Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(e => e.Roles).WithMany(e => e.Users).Map(m => m.ToTable("UserRole", "parkpay").MapLeftKey("RoleId").MapRightKey("UserId"));
}
}
public class RoleMapping : EntityTypeConfiguration<Role>
{
public RoleMapping()
{
ToTable("Role", "parkpay");
HasKey(e => e.Id).Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class DatabaseContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserMapping());
modelBuilder.Configurations.Add(new RoleMapping());
base.OnModelCreating(modelBuilder);
}
}