Entity Framework Inheritance and Relationships - c#

I'm trying to implement TPH inheritance with Entity Framework 6 Code First and am having problems with a relationship from my inherited types.
My code is
public abstract class Base...
public class Inherited1 : Base
{
public virtual Type1 Rel { get; set; }
...
public class Inherited2 : Base
{
public virtual Type1 Rel {get;set;}
...
So the inherited types have the "same" relationship. The inheritance itself works fine, but the problem I'm having is that the relationship to the table Type1 will be added twice (logical...) and the other relationship is from Inherited2.Id to Type1.Id instead of Inherited2.Type1Id to Type1.Id that the first relationship is (correctly).
I'm not sure if I made any sense explaining this and with the partial code sample with changed type names, but I hope you got the point. Ask for more details if you need any.
I probably could implement this correctly with
UPDATE
I've created a sample Github repo to demonstrate the issue. Feel free to tell me what I'm doing wrong. https://github.com/antsim/EntityFrameworkTester

Try to use the following
1- if you want TPT
modelBuilder.Entity<Inherited1>()
.ToTable("Inherited1s")
.HasKey(x => x.YourKey)
.HasRequired(x=>Type1)
.WithMany()
.HasForeignKey(x=>Type1Id)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Inherited2>()
.ToTable("Inherited2s")
.HasKey(x => x.YourKey)
.HasRequired(x=>Type1)
.WithMany()
.HasForeignKey(x=>Type1Id)
.WillCascadeOnDelete(false);
2 - if you want TPH
modelBuilder.Entity<Base>()
.ToTable("YourTableName")
.HasRequired(m=>m.Type1)
.WithMany()
.HasForeignKey(m=>m.Type1Id)
.WillCascadeOnDelete(); // true or false as you want
for more details you might check this article
based on the sample you provided
Attachment and Document are inherited from File and you are using TPH which means One table will be created with a Discriminator field.
Document and FileContainer has a relation of type 0..1 which means a Foreign Key FileContainerId should be created in the Document hence in the File table
FileContainer and Attachment has a relation of type 0..n, then another nullable foreign key will be created in the table File
in the example you provided, I made the following changes
Add FileContainerId to the table Document
Add FileContainerAttachmentId to the table Attachment
The changes made on the TestContext was
modelBuilder.Entity<FileContainer>()
.HasOptional(x => x.Document)
.WithMany()
.HasForeignKey(t => t.DocumentId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Document>()
.HasRequired(t => t.FileContainer)
.WithMany()
.HasForeignKey(t => t.FileContainerId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Attachment>()
.HasRequired(t => t.FileContainer)
.WithMany()
.HasForeignKey(t => t.FileContainerAttachmentId)
.WillCascadeOnDelete(false);
the output was correct ( File table contains discriminator field in addition to two relations one for the document with the container and the other for the attachment with the container).
A better solution in my opinion is:
To add a class FileType ( Id, Name) with values Attachment, Document, and add it as a foreign key in File
To add only one relation 0..n between the FileContainer and File
To validate that only one record in the File of type document to same container
Hope this will help you

Related

Add id column in self referencing many-to-many table in entity framework

I have a Person class which can have Relatives which is a self reference many-to-many relationship(Relative is also a Person). I wanted to maintain this Person->Relatives mapping in a separate table. So, to create this relationship in EF, I did this:
modelBuilder.Entity<Person>()
.HasMany(e => e.Relatives)
.WithMany()
.Map(c =>
{
c.MapLeftKey("PersonId");
c.MapRightKey("RelativeId");
c.ToTable("PersonRelative");
});
Now, this created a new table PersonRelative with two columns PersonId and RelativeId automatically as I planned. It also have composite primary key on PersonId and RelativeId.
Now, there're two things I wish to do here:
I want to add a new identity "Id" column in this new PersonRelative table for better performance. How can I do that?
Now, can I create a separate class for PersonRelative table, the way we have a class for every table. Because that would try creating the table again. So, I think I can either keep that fluent api mapping or the PersonRelative class. Is this correct?
I would really appreciate any help here.
you can achieve two points by the following code:
modelBuilder.Entity<PersonRelative>().HasKey(x => x.Id);
modelBuilder.Entity<PersonRelative>()
.HasOne<Person>(x => x.Person)
.WithMany(s => s.PersonRelative)
.HasForeignKey(x => x.PersonId);
modelBuilder.Entity<PersonRelative>()
.HasOne<Realtive>(x => x.Realtive)
.WithMany(s => s.PersonRelative)
.HasForeignKey(x => x.RelativeId);
it's how to make many-to-many relation in entity framework core, as it's not supported yet in core, you can use it in non-core entity framework too.
Please Note, if you want a new table mapping just for accessing data with navigation properties, then you can add property of type "ICollection" for Students in Relative Class, and the same for Student Class you can add ICollection for access Relatives from Student

EF/Code First Migration - implicit table mappings?

I am using a code first migration approach to creating new tables in an existing database, and I'd like to know if this is auto-creating a property-to-field mapping somewhere in the project.
For example: the "Category" table pre-existed in the database. It was created directly in SQL, and my MCV project has a CategoryMap.cs file that explicitly maps the Category entity properties to the Category table fields:
CategoryMap.cs
this.ToTable("Category", "ctt");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.ClientId).HasColumnName("ClientId");
this.Property(t => t.CategoryTypeId).HasColumnName("CategoryTypeId");
etc.
The db context class explicitly points to this mapping in the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new CategoryMap());
etc.
With this set-up, I can save data to the Category table, as long as I keep the mapping pieces in place.
On the other hand, I've created a new table using the code first approach:
1) Create a "MyTable" entity class
2) Add this code to my context class:
public DbSet<MyTable> MyTable { get; set; }
3) Run the add-migration and update-database commands
That created a "MyTable" table in the database schema, and I can save data to this table, despite the fact that there is no mapping file, and no explicit mapping code in the OnModelCreating method of my db context class.
Now, if I comment out the "modelBuilder.Configurations.Add(new CategoryMap());" line of code in the context class, I can't save data to that table any more; I get a "table 'Category' not found" error message when I try to do db.SaveChanges().
So I guess my question is this: when I created "MyTable" using the add-migration and update-database commands, did a class-to-table map get auto-generated somewhere behind the scenes? If so, can I access it and view it?
There are no auto generated mappings as such but a number of mapping conventions that get applied if there are no specific mappings. For example if you have no explicit table mapping then by convention it will map to a table that matches the class name.
So for a class Category it will by convention map to a table called Category.
This is also true of properties so Property(t => t.Id).HasColumnName("Id"); doesn't actually do anything as the property will have that mapping by convention. You only need explicit mappings when you go outside the conventions. So if you wanted to map you property Id to a column called Category_id then you would need a column mapping Property(t => t.Id).HasColumnName("Category_id");
Read this MSDN article for more info on the default conventions.

How can do I use fluent nhibernate to map this relationship?

I have the following database tables:
Project table
Dependency table with columns (Id, ProjectId, DependentProjectId)
on my Project domain object i have the following mapping to be able to take a project and read its dependencies
public virtual IList<Dependency> Dependencies { get; set; }
HasMany(x => x.Dependencies).AsBag().Inverse().Cascade.AllDeleteOrphan().Fetch.Select().BatchSize(80);
I now want to create another property on the Project object to read all of the items where it is the dependent project (to find out the list of projects that has "me" as a dependency.
what is the correct way to do that mapping in fluent nhibernate?
I'd say you would just have to specify the .KeyColumn for the one to many relation, e.g.
HasMany(x => x.Dependencies)....KeyColumn("ProjectId")
HasMany(x => x.DependentProjects)....KeyColumn("DependentProjectId")

Entity Framework Code First: Custom Mapping

public class User
{
public int Id {get;set;}
public string Name {get;set}
public ICollection<User> Followers {get;set;}
public ICollection<User> Following {get;set;}
}
My Model looks like above, Entity framework automatically creates A table and UserUser with rows User_ID and User_ID1 in DB to map this model. I want to map that table and rows myself.
How can i do that, Thanx!!
From Scott Gu's blog about Many-valued Associations:
Many-to-Many Associations
The association between Category and Item is a many-to-many
association, as can be seen in the above class diagram. a many-to-many
association mapping hides the intermediate association table from the
application, so you don’t end up with an unwanted entity in your
domain model. That said, In a real system, you may not have a
many-to-many association since my experience is that there is almost
always other information that must be attached to each link between
associated instances (such as the date and time when an item was added
to a category) and that the best way to represent this information is
via an intermediate association class (In EF, you can map the
association class as an entity and map two one-to-many associations
for either side.).
In a many-to-many relationship, the join table (or link table, as some
developers call it) has two columns: the foreign keys of the Category
and Item tables. The primary key is a composite of both columns. In EF
Code First, many-to-many associations mappings can be customized with
a fluent API code like this:
class ItemConfiguration : EntityTypeConfiguration<Item> {
internal ItemConfiguration()
{
this.HasMany(i => i.Categories)
.WithMany(c => c.Items)
.Map(mc =>
{
mc.MapLeftKey("ItemId");
mc.MapRightKey("CategoryId");
mc.ToTable("ItemCategory");
});
} }
Register this configuration in your DbContext's (you using the DbContext api right?) like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ItemConfiguration());
}
Good luck, hope this help!
To map an entity to itself, you would do something like this
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(u => u.Followers)
.WithMany().ForeignKey(u => u.FollowerId);
base.OnModelCreating(modelBuilder);
}
its hard to tell without seeing your database model though, and how you actually relate the followers to the user.

EF4 CTP5 - Many To One Column Renaming

How do I override the default convention for the foreign key column in EF4 to specify a different column name?
For example, I have one entity with a property called Parent that references to other one of the same type.
EF4 tries to resolve the relation by looking for the foreign key named EntityId, but in my DB schema it is Entity_Id. How do I tell EF that the FK column name is not EntityId?
I've tried the following:
modelBuilder.Entity<SomeEntity>()
.HasOptional(m => m.Parent)
.WithMany()
.IsIndependent()
.Map(m => m.MapKey(k => k.Id, "Entity_Id")));
But I get an exception saying: Sequence contains more than one matching element.
Any help on this?
Thanks!
First: Upgrade to EF 4.1 RTW. CTP 5 is outdated and contains potentially many bugs which are fixed now.
If you have done the upgrade the following should work:
modelBuilder.Entity<SomeEntity>()
.HasOptional(m => m.Parent)
.WithMany()
.Map(c => c.MapKey("Entity_Id"));

Categories

Resources