EF Code First One to Many and Reverse One To One Relationship - entity-framework

I am trying to create one-to-many and reverse one-to-one relationship using code first. Here is what I ma trying to do
1) One-to-Many between two classes and it works as expected.
public class X
{
[Key]
public int XId { get; set; }
public ICollection<Y> Y { get; set; }
}
public class Y
{
[Key]
public int YId { get; set; }
public int XId { get; set; }
public X X { get; set; }
}
public class DataContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Y>()
.HasRequired(y => y.X)
.WithMany(x => x.Y)
.HasForeignKey(y => y.XId);
}
}
Now what I want to do is to create Reverse One-to-One optional relationship between Y and X, such that the X will contain a foreign key of Y...How is it possible? Here is what I am trying to do and it throws some Multiplicity Error
public class X
{
[Key]
public int XId { get; set; }
public ICollection<Y> Y { get; set; }
public int YId {get; set; }
[ForiegnKey("YId")]
public Y YOptional { get; set; }
}
public class Y
{
[Key]
public int YId { get; set; }
public int XId { get; set; }
public X X { get; set; }
public X XOptional {get; set; }
}
public class DataContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Y>()
.HasRequired(y => y.X)
.WithMany(x => x.Y)
.HasForeignKey(y => y.XId);
modelBuilder.Entity<X>()
.HasOptional(x => x.YOptional)
.WithOptionalDependent(y=> y.XOptional);
}
}

You can't have a relationship between two entities that is defined differently from either end. So you can't do 1:* from one direction and 1:1 from another.
Let me make a guess that you don't really want it to be 1:1 from the dependent end. From that end it will always only point to one thing.
In mappings, unlike in life, unless you have many to many, a child only has one parent.
You can, however, create a 0..1 : * relationaship (zero or one to many). Where the parent can have one or more children (e.g. "many") but the child can exist without a parent, but it can never have more than one parent (e.g. "zero or one").
Here is the simplest method of making your classes result in a [zero or one] to many relationship. Notice that I made the foreign key in the class Y a nullable int. WIth this setup, EF conventions will result in a mapping that lets a child exist without a parent.
public class X
{
[Key]
public int XId { get; set; }
public ICollection<Y> Y { get; set; }
}
public class Y
{
[Key]
public int YId { get; set; }
public int? XId { get; set; }
public X X { get; set; }
}
public class DataContext : DbContext
{
public DbSet<X> XSet { get; set; }
public DbSet<Y> YSet { get; set; }
}
Here is a screenshot of visual model derived from the above classes and context.
I think this achieves the behavior you are seeking if my guess that you may just be perceiving it differently is correct.

Using the actual class names you mentioned in the comments:
Mapping a User that can have many Singles is not a problem. However, when you want to map a 1:1 association between a User and a Single you have to choose which of the two is the "principle" entity. You can't have a foreign key column in both tables because one entity will always be inserted before the other one. The "dependent" entity is inserted next, and it refers to the principal's primary key value.
So if User is the principal entity, you could have a class model similar to this:
public class User
{
public User()
{
this.Singles = new HashSet<Single>();
}
public int UserId { get; set; }
public string Name { get; set; }
public Single Single { get; set; }
public virtual ICollection<Single> Singles { get; set; }
}
public class Single
{
public int SingleId { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
public int SuperUserId { get; set; }
public User SuperUser { get; set; }
}
And two options for mappings:
Option 1: User as principal
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(u => u.Singles)
.WithRequired(s => s.SuperUser).HasForeignKey(s => s.SuperUserId);
modelBuilder.Entity<User>().HasOptional(s => s.Single)
.WithOptionalPrincipal(s => s.User).Map(m => m.MapKey("UserId"));
}
In the data model, Single now has two foreign keys, UserId and SuperUserId. This is how to create a User and a Single in User.Single and User.Singles:
var superUser = new User { Name = "superUser1" };
var single = new Single { Name = "single" };
superUser.Singles.Add(single);
db.Users.Add(superUser);
superUser.Single = single;
db.SaveChanges();
And EF will first insert the User, then the Single having both foreign keys equal to the User's primary key.
Option 2: Single as principle
You can also make Single the principal entity in the 1:1 association:
modelBuilder.Entity<User>().HasOptional(s => s.Single)
.WithOptionalDependent(s => s.User).Map(m => m.MapKey("SingleId"));
Now there's only one foreign key in Single (SuperUserId) and a foreign key in User (SingleId). If you execute the same code, now EF will throw
Unable to determine a valid ordering for dependent operations.
This is because there is a chicken-and-egg problem: the Single must be created before the dependent User can be created, but the User must be created before the Single can be added to its Singles collection. This could be solved by assigning the Single later:
var superUser = new User { Name = "superUser1" };
var single = new Single { Name = "single" };
superUser.Singles.Add(single);
db.Users.Add(superUser);
db.SaveChanges();
superUser.Single = single;
db.SaveChanges();
You'd want to wrap this in a TransactionScope, so I think this option is less viable.
Note
As you see, in a 1:1 mapping the foreign key can't be mapped to a property in the class model. There is no HasForeignKey in the fluent API after WithOptionalDependent or WithOptionalPrincipal. Also, this association can only be mapped by the fluent API. In data annotations there is not attribute to indicate the principal end of an association.

Related

Implementing 1:N as N:M in EF Core 6

I have an application where the domain has several parent entities each to which a list of Comments are linked in a master-detail fashion.
Instead of having a Comment table for each parent entity's comments, I have opted to have a single Comment table with links to the parent entities.
The reason for this choice is as follows:
As the application expands with more parent entities, I don't need to add a Comment table for each of them
In order to implement a search where the user can search through comments, it is likely to simply search through a single table (likely will use Lucene)
This is illustrated below
Happy to change my mind re this design choice
The link between the parent entity and Comment acts like a many-many relationship. The domain objects are defined as:
public class Customer
{
public Customer(string name)
{
Name = name;
}
protected Customer()
{}
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Comment> Comments { get; }
public void AddComment(Comment comment)
{
Comments.Add(comment);
}
}
public class CustomerComment
{
public long Id { get; set; }
public long CustomerId { get; private set; }
public long CommentId { get; private set; }
}
public class Comment
{
public Comment(long createdByUserId, DateTime createdOnDtm, string content)
{
CreatedByUserId = createdByUserId;
CreatedOnDtm = createdOnDtm;
Content = content;
}
protected Comment()
{}
public long Id { get; private set; }
public long CreatedByUserId { get; init; }
public DateTime CreatedOnDtm { get; init; }
public string Content { get; init; }
public virtual ICollection<Customer> Customers { get; private set; }
//public virtual ICollection<Supplier> Suppliers { get; private set; }
//public virtual ICollection<Other> Others { get; private set; }
}
However when setting up the EF Core model, it seems I have to have the Customers/Suppliers/Others parent entity collections in the Comment domain object
because the relationship looks like a N:M in the relational model even though it is 1:N in the domain model.
This is what the fluent model definition looks like:
modelBuilder.Entity<Customer>()
.HasMany(p => p.Comments)
.WithMany(t => t.Customers)
.UsingEntity<CustomerComment>(
j => j.HasOne<Comment>().WithMany().HasForeignKey(p => p.CommentId),
j => j.HasOne<Customer>().WithMany().HasForeignKey(p => p.CustomerId));
modelBuilder.Entity<Customer>()
.Navigation(p => p.Comments)
.UsePropertyAccessMode(PropertyAccessMode.Property);
modelBuilder.Entity<CustomerComment>()
.HasKey(x => x.Id);
I cannot declare the join type CustomerComment if the Comment does not have the Customers collection. It basically means that my Comment domain object gets 'polluted' with all the possible parent entities
so that EF can understand the link table.
Or is there a way around this?

Foreign table using EF core

I have a advertiser model like:
public class Advertiser
{
public int AdvertiserId { get; set; }
public string Name { get; set; } = string.Empty;
public Address AddressId { get; set; }
}
Inside this class I have a builder as:
public class AdvertiserConfiguration : IEntityTypeConfiguration<Advertiser>
{
public void Configure(EntityTypeBuilder<Advertiser> builder)
{
builder.ToTable("Advertisers");
builder.HasKey(x => x.AdvertiserId);
builder.Property(x => x.Name).IsRequired().HasMaxLength(250);
builder.HasOne(x => x.AddressId);
}
}
And address model like:
public class Address
{
public int AddressId { get; set; }
....
}
So that I want to do is a simple foreign key on the Advertiser table so I check msdn reference
And it says that I should use HasOne and WithMany methods in order to use HasForeignKey, but I do not understand why? it is necessary to use them to do a simple foreign key connection? if yes, what fields should I use on HasOne and WithMany? Thanks!
In ef for a relation you define a "navigation property" on both sides of the related objects and a "foreign key property". So your entities should look like this
public class Advertiser
{
public int AdvertiserId { get; set; }
public Address? Address { get; set; }
public int AddressId { get; set; }
...
}
public class Address
{
public int AddressId { get; set; }
public virtual ICollection<Advertiser>? Advertisers { get; set; }
...
}
and your entity configuration
builder
.HasOne(adv => adv.Address)
.WithMany(adr => adr.Advertisers)
.HasForeignKey(adv => adv.AddressId);
That way you define which properties are the connected objects and how ef should resolve this from the database (by using the foreign key).
Now you can use code like this
foreach(var advertiser in address.Advertisers)
{
...
}
or
var street = advertiser.Address.Street;
...
You won't want to do all the navigation manually by requerying the database e. g. for the connected advertisers after you read an address.
Remember to Include navigation properties in your queries, when they will be used after/outside of the queries.

Same Entity referred twice in another Entity is creating issue while saving parent with children

I am having the data model as follows.
class KnowledgeDocument
{
public int? Id {get; set;}
public virtual ICollection<KDValueCreation> KDValueCreations { get; set; }
}
class KDValueCreation
{
public int? Id{get; set;}
public int? KDReferenceId { get; set; }
public virtual KnowledgeDocument KDReference { get; set; }
public int KnowledgeDocumentId { get; set; }
public virtual KnowledgeDocument KnowledgeDocument { get; set; }
public decimal Amount {get; set;}
}
Now, when I am trying to create a new KnowledgeDocument along with KDValueCreations as follows.
KnowledgeDocument kd = new KnowledgeDocument();
kd.KDValueCreations.Add(new KDValueCreation{ Amount = "500000"});
When I save the kd, kd is saved without any issue and in KDValueCreation, 1 record is created and both KDReferenceId and KnowledgeDocumentId are populated with the same kdId. But, I want to populate only KnowledgeDocumentId and stop KDReferenceId from populating and set it to null.
As both the fields are pointing to the same  reference, Entity framework is populating the Id on both the fields.
How can I achieve this still by saving the KnowledgeDocument with its children?
Please suggest. Thanks in advance. 
As I can see, the reason is in two identical relationships. You need to specify what properties are related. You can do it by InverseProperty attribute or by FluentAPI like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// configures one-to-many relationship
modelBuilder.Entity<KDValueCreation>()
.HasRequired<KnowledgeDocument>(c => c.KnowledgeDocument)
.WithMany(d => d.KDValueCreations)
.HasForeignKey(c => c.KnowledgeDocumentId);
}
}
or this:
modelBuilder.Entity<KnowledgeDocument>()
.HasMany<KDValueCreation>(d => d.KDValueCreations)
.WithRequired(c => c.KnowledgeDocument)
.HasForeignKey(c => c.KnowledgeDocumentId);

Entity framework Multiple Parent for a child

How can I implement a child that has multiple parents in Entity Framework?
The resulting tables must be as follows:
1.Courses:
CourseID int identity
CourseTitle nvarchar
.
.
.
OtherColumns as neede
2.CoursePreRequisites:
CourseID (FK to Course.CourseID)
PreRequisiteCourseID (FK to Course.CourseID)
or is there any better way to achieve multiple parent for a child record?
You just need two navigation properties in the child class refering to the same parent class and - optionally - two corresponding foreign key properties:
public class Course
{
public int CourseID { get; set; } // PK property
public string CourseTitle { get; set; }
}
public class CoursePreRequisite
{
public int CoursePreRequisiteID { get; set; } // PK property
public int CourseID { get; set; } // FK property 1
public Course Course { get; set; } // Navigation property 1
public int PreRequisiteCourseID { get; set; } // FK property 2
public Course PreRequisiteCourse { get; set; } // Navigation property 2
}
If one or both of the two relationships are optional, use int? instead of int for the foreign key properties.
If you use the property names as indicated in the example above you don't need to configure anything. EF will recognize the two one-to-many relationships by naming conventions.
You can also use collections as inverse properties in the Course entity if you need or want them:
public class Course
{
public int CourseID { get; set; } // PK property
public string CourseTitle { get; set; }
public ICollection<CoursePreRequisite> PreRequisites1 { get; set; }
public ICollection<CoursePreRequisite> PreRequisites2 { get; set; }
}
However, in that case you must specify which navigation property pairs belong together in a relationship. You can do this with data annotations for example:
[InverseProperty("Course")]
public ICollection<CoursePreRequisite> PreRequisites1 { get; set; }
[InverseProperty("PreRequisiteCourse")]
public ICollection<CoursePreRequisite> PreRequisites2 { get; set; }
Or with Fluent API:
modelBuilder.Entity<Course>()
.HasMany(c => c.PreRequisites1)
.WithRequired(p => p.Course) // Or WithOptional
.HasForeignKey(p => p.CourseID);
modelBuilder.Entity<Course>()
.HasMany(c => c.PreRequisites2)
.WithRequired(p => p.PreRequisiteCourse) // Or WithOptional
.HasForeignKey(p => p.PreRequisiteCourseID);

EF4 CTP5 self-referencing hierarchical entity mapping

Okay, this should be really easy, but I've been tearing my hair out. Here's my POCO (which has to do with machine parts, so a part can be contained within a parent part):
public class Part
{
public int ID { get; set; }
public string Name { get; set; }
public Part ParentPart { get; set; }
}
When the database table is created, the column names are "ID", "Name", and "PartID". How do I change the name of that last column to "ParentPartID"?
Basically, you want to rename the foreign key in an Independent Association and this is the fluent API code that will do it:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Part>()
.HasOptional(p => p.ParentPart)
.WithMany()
.IsIndependent()
.Map(m => m.MapKey(p => p.ID, "ParentPartID"));
}
However, due to a bug in CTP5, this code throw as exception in self referencing associations (which is your association type). The workaround would be to change your association to a Foreign Key Association as follows:
public class Part
{
public int ID { get; set; }
public string Name { get; set; }
public int ParentPartID { get; set; }
[ForeignKey("ParentPartID")]
public Part ParentPart { get; set; }
}