One-to-One in table per hierarchy - c#

public class BasePage
{
public int Id { get; set; }
...
}
public class Region : BasePage
{
public virtual MapCoordinates Map { get; set; }
...
}
public class Place: BasePage
{
public virtual MapCoordinates Map { get; set; }
...
}
public class MapCoordinates
{
[Key, ForeignKey("BasePage")]
public int Id { get; set; }
public virtual BasePage BasePage { get; set; }
...
}
Getting this exception on SaveChanges:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.

use this code :
public class BasePage
{
public int Id { get; set; }
public virtual MapCoordinate Map { get; set; }
...
}
public class Region : BasePage
{
...
}
public class Place: BasePage
{
...
}
public class MapCoordinate
{
[Key]
public int BasePageId { get; set; }
...
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BasePage>()
.HasRequired(e => e.MapCoordinate)
.WithRequiredPrincipal();
modelBuilder.Entity<BasePage>().ToTable(BasePages);
modelBuilder.Entity<MapCoordinate>().ToTable(MapCoordinates);
}
check out these links :
Associations in EF Code First: Part 4 – Table Splitting
Associations in EF Code First: Part 5 – One-to-One Foreign Key Associations
Code First and Fluent API one to one relationship

Related

ASP.Net Core/EF Core: Stack overflow after add-migration with many-to-many relationship between an Identity User class and a custom defined class

I'm trying to configure many-to-many relationship between Knowledge and ApplicationUser classes. After 'add-migration' build succeeded, but I recieve stack overflow. I can't find any specific details on this issue.
public class Question
{
[Key]
public int Id { get; set; }
public string QuestionString { get; set; }
public Knowledge Knowledge { get; set; }
}
Identity user class
public class ApplicationUser: IdentityUser
{
public virtual ICollection<UserKnowledge> Knowledges { get; set; }
}
public class Knowledge
{
[Key]
public int Id { get; set; }
public string KnowledgeName { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<UserKnowledge> Users { get; set; }
}
Joining entity class
public class UserKnowledge
{
[Key]
public int Id { get; set; }
public string UserId { get; set; }
[ForeignKey("UserId")]
public ApplicationUser User { get; set; }
public int KnowledgeId { get; set; }
[ForeignKey("KnowledgeId")]
public Knowledge Knowledge { get; set; }
}
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(): base()
{}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{}
public DbSet<UserKnowledge> UserKnowledges { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<Knowledge> Knowledges { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserKnowledge>()
.HasKey(ub => ub.Id);
modelBuilder.Entity<UserKnowledge>()
.HasOne(ub => ub.Knowledge)
.WithMany(u =>u.Users)
.HasForeignKey(ub => ub.KnowledgeId);
modelBuilder.Entity<UserKnowledge>()
.HasOne(ub => ub.User)
.WithMany(u => u.Knowledges)
.HasForeignKey(ub => ub.UserId);
modelBuilder.Entity<Knowledge>().HasKey(g => g.Id);
modelBuilder.Entity<Knowledge>().HasMany(q => q.Questions).WithOne(q => q.Knowledge);
modelBuilder.Entity<Question>().HasKey(g => g.Id);
OnModelCreating(modelBuilder);
}
}
Can someone tell me what I am missing?
The concrete stack overflow issue is of course caused by the last line in OnModelCreating override, which calls itself
OnModelCreating(modelBuilder);
It should instead call the base method
base.OnModelCreating(modelBuilder);
and (unrelated) for IdentityDbContext derived classes it should be the first line before your own configuration, so you can override what it does if needed
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Your code goes here
}
Also unrelated, but when using custom identity entities, you should really inherit one of the generic IdentityDbContext<...> base classes with your entity type(s) passed as generic type arguments. In this case, IdentityDbContext<ApplicationUser>
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
// ...
}
otherwise EF will treat the model as using TPH (Table Per Hierarchy) database inheritance strategy with discriminator column, which normally is not what you expect/want.

Entity Framework TPC Foreign Key relationships

So I've implement Table Per concrete Class to deal with an inheritance hierarchy, but I'm having trouble with Navigation properties.
My Model is structure as follows:
I have an abstract BaseEntity class, with Multiple derived classes, so:
public abstract class BaseEntity
{
public virtual ICollection<Comment> Comments { get; set; }
public EntitytType EntitytType { get; set; }
}
public class FirstDerivedEntity : BaseEntity
{
//EntitytType == First;
}
public class SecondDerivedEntity : BaseEntity
{
//EntitytType == Second;
}
public class Comment
{
public long BaseEntityId { get; set; }
public EntitytType BaseEntityType { get; set; }
}
public enum EntitytType
{
First,
Second
}
The Comments navigation property here doesn't work because each derived(Concrete) class has it's own set of Ids. In my mind the EntityType column in each Table would serve as some sort of Discriminator, but I don't know how to tell EF to use it as such.
Any help would be greatly appreciated!
The solution in order for TPC to work would be the Ids in the derived classes to be unique not only in their table but in both tables.
You can use a database solution like an auto increment primary key with different initial seeds or GUID keys like the ones in SQL server.
Another approach would be to generate unique GUID keys in you application code.
You can see same sample code of how to model the entities below :
namespace tpc_so
{
public class tpc_soContext : DbContext
{
public tpc_soContext()
{
}
public DbSet<BaseEntity> BaseEntities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseEntity>().HasKey(b => b.BaseEntityId);
modelBuilder.Entity<BaseEntity>()
.Property(b => b.BaseEntityId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<FirstDerivedEntity>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("FirstDerivedEntities");
});
modelBuilder.Entity<SecondDerivedEntity>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("SecondDerivedEntities");
});
modelBuilder.Entity<Comment>().ToTable("Comments");
base.OnModelCreating(modelBuilder);
}
}
public abstract class BaseEntity
{
public Guid BaseEntityId { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class FirstDerivedEntity : BaseEntity{}
public class SecondDerivedEntity : BaseEntity{ }
public class Comment
{
public long CommentId { get; set; }
public Guid BaseEntityId { get; set; }
public string Text { get; set; }
}
}
And to create some entities use the code below :
using (var ctx = new tpc_soContext())
{
ctx.BaseEntities.Add(new FirstDerivedEntity()
{
BaseEntityId = Guid.NewGuid(),
Comments = new List<Comment>() { new Comment() { Text = "First Derived Comment" } }
});
ctx.BaseEntities.Add(new SecondDerivedEntity()
{
BaseEntityId = Guid.NewGuid(),
Comments = new List<Comment>() { new Comment() { Text = "Second-Derived Comment" } }
});
ctx.SaveChanges();
}
Some good sources for TPC would be here :
[Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC)]
[Entity Framework - Table Per Concrete]

Does EF Core support an abstract base entity which is generic?

I remember there were problems with generic entities in previous EF. How about EF Core? I can't find docs related to this matter.
For example:
public abstract class Parent<TEntity> {
public int EntityId { get; set; }
public TEntity Entity { get; set; }
}
public class Child : Parent<Foo> {
}
public class OtherChild : Parent<Bar> {
}
// config for child entities includes this:
config.HasKey(c => c.EntityId);
Though this throws stating that child entities do not define a primary key, when they clearly do!
I can fix this by making Parent non-generic.
Are there official docs for this? Am I doing something wrong, or is this the expected behavior?
I can use this model in ef-core 1.1.0:
public abstract class Parent<TEntity>
{
public int EntityId { get; set; }
public TEntity Entity { get; set; }
}
public class Child : Parent<Foo>
{
}
public class OtherChild : Parent<Bar>
{
}
public class Foo
{
public int Id { get; set; }
}
public class Bar
{
public int Id { get; set; }
}
With this mapping in the context:
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Child>().HasKey(a => a.EntityId);
mb.Entity<Child>().HasOne(c => c.Entity).WithMany().HasForeignKey("ParentId");
mb.Entity<OtherChild>().HasKey(a => a.EntityId);
mb.Entity<OtherChild>().HasOne(c => c.Entity).WithMany().HasForeignKey("ParentId");
}
Which leads to this fantastic model:

TPT inheritance with Abstract base doesn't reference children in related table

I'm struggling with TPT inheritance in MVC - EF.
I have an AAnimal abstract class and two classes that inherit it, Zebra and Lion. There is a Cage class which holds an AAnimal.
My problem is that because AAnimal is abstract, the EF cannot create an instance of it when I load all Cages. So what I want is a way to override this behavior and make it understand whether it needs to load a Zebra or a Lion.
Zebra and Lion have a Primary key which is also foreign key to Animal table. This is done by the EF (TPT inheritance model).
public abstract class AAnimal
{
[Key]
public int AnimalId { set; get; }
public string Name { set; get; }
}
public class Lion : AAnimal {}
public class Zebra : AAnimal {}
public class Εmployee
{
[Key]
public int EmployeeId { set; get; }
public string Name { set; get; }
}
public class Cage
{
[Key]
public int CageId { set; get; }
[ForeignKey("CagedAnimal")]
public int CagedAnimalId { set; get; }
public AAnimal CagedAnimal { set; get; }
[ForeignKey("CageEmployee")]
public int CageEmployeeId { set; get; }
public Employee CageEmployee { set; get; }
}
// Model mapping
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AAnimal>().ToTable("Animal");
modelBuilder.Entity<Lion>().ToTable("Lion");
modelBuilder.Entity<Zebra>().ToTable("Zebra");
modelBuilder.Entity<Εmployee>().ToTable("Εmployee");
}
// Load all cages
public ActionResult Index()
{
var allCages = db.Cages.ToList();
}
At this point all cages are loaded, all fields have values except the CagedAnimal which is null. Even the CagedAnimalId has value.
How can I tell the EF to follow the same procedure while saving data, in order to load entities?
Note that this is just an example. Also, TPT inheritance model has been selected over other inheritance models.

EF 4.1 Code First Mapping Problem

All my attempts to map the ID of SitePage to the database column ID (SitePages table, ID column of type bigint) has failed. It keeps looking for column SitePage_ID to map it.. Can you see where I am doing wrong? All related code is below;
public class Site : EntityBase<Int64>
{
public virtual string Url { get; set; }
public virtual IList<SitePage> Pages { get; set; }
}
public class SitePage : EntityBase<Int64>
{
public virtual Site Site { get; set; }
public virtual string Url { get; set; }
public virtual string Html { get; set; }
public virtual string Text { get; set; }
public virtual string Language { get; set; }
}
public abstract class EntityBase<T> : IComparable
{
public virtual T ID { get; set; }
protected EntityBase() : this(default(T))
{
}
protected EntityBase(T id)
{
this.ID = id;
if (this.ID == null)
this.ID = default(T);
}
}
public class SpellCrawlerContext : DbContext
{
public SpellCrawlerContext(){}
public DbSet<Site> Sites { get; set; }
public DbSet<SitePage> SitePages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Site>()
.HasMany(s => s.Pages)
.WithRequired(p => p.Site)
.Map(s => s.MapKey("SiteID"));
modelBuilder.Entity<SitePage>()
.HasKey(p => p.ID);
modelBuilder.Entity<SitePage>()
.Property(p => p.ID)
.HasColumnName("ID");
}
}
You are not doing anything wrong. The code you shown does everything correctly. You even don't need to explicitly define the name of ID in SitePage because it will be defined like ID anyway.
SitePage_ID is used by default naming convention for foreign keys created for independent associations. So do you have any other one-to-many relation between SitePage and any other entity? If you didn't map foreign key in dependent entity it will be defined as SitePage_ID by default.

Categories