Avoid circular dependency in Entity Framework Code First - c#

Assume I have two entities: Nationality and Employee with relationship 1:n.
public class Employee
{
public int Id { get; set; }
// More Properties
public virtual Nationality Nationality { get; set; }
}
public class Nationality
{
public int Id { get; set; }
public string Name { get; set; }
}
In order to use Code-First with Entity Framework, I have to add one more property: Employees which I don't expect into Nationality (it creates circular dependency):
public class Nationality
{
public int Id { get; set; }
public string Name { get; set; }
// How to avoid this property
public virtual List<Employee> Employees { get; set; }
}
So that I can configure the relationship 1: n in Configuration class:
internal class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
public EmployeeConfiguration()
{
HasKey(f => f.Id);
HasRequired(e => e.Nationality)
.WithMany(e => e.Employees)
.Map(c => c.MapKey("NationalityId"));
ToTable("Employees");
}
}
Is there any other approach which I can avoid circular dependency and eliminate property Employees out of Nationality class?

Use the WithMany() overload to configure the mapping.
internal class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
public EmployeeConfiguration()
{
HasKey(f => f.Id);
HasRequired(e => e.Nationality)
.WithMany()
.Map(c => c.MapKey("NationalityId"));
ToTable("Employees");
}
}

Related

Efcore issue with many 2 many on same entity (Products & RelatedProducts)

So i want to add RelatedProducts to my products. So i applied the same relationship type as i did for category. The only difference there is no category class, but we target the same entity. (product). Because the regular many2many works fine, i removed that from my example.
Goal:
Any suggestions?
Exception:
"Exception occured: Cannot create a relationship between 'Product.RelatedProducts' and 'RelatedProduct.Related' because a relationship already exists between 'Product.RelatedProducts' and 'RelatedProduct.Product'. Navigation properties can only participate in a single relationship. If you want to override an existing relationship call 'Ignore' on the navigation 'RelatedProduct.Related' first in 'OnModelCreating'."
Product.cs:
public class Product : IExportable, IEntityBase
{
public int Id { get; set; }
public string Name { get; set;}
public ICollection<RelatedProduct> RelatedProducts { get; set; }
}
ProductEntityTypeConfiguration.cs
internal class ProductEntityTypeConfiguration : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
config.HasKey(p => p.Id);
}
}
RelatedProducts.cs
public class RelatedProduct
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public int RelatedId { get; set; }
public virtual Product Related { get; set; }
}
RelatedProductEntityTypeConfiguration.cs
public class RelatedProductsEntityConfiguration : IEntityTypeConfiguration<RelatedProduct>
{
public void Configure(EntityTypeBuilder<RelatedProduct> builder)
{
builder.HasKey(rp => rp.Id);
builder.ToTable("RelatedProducts");
builder
.HasOne(rp => rp.Product)
.WithMany(p => p.RelatedProducts)
.HasForeignKey(rp => rp.ProductId)
.OnDelete(DeleteBehavior.Restrict);
builder
.HasOne(rp => rp.Related)
.WithMany(p => p.RelatedProducts)
.HasForeignKey(rp => rp.RelatedId)
.OnDelete(DeleteBehavior.Restrict);
}
}
I am unable to test this suggestion but I am sure it might work. Why don't you just define your RelatedProduct as:
public class RelatedProduct
{
public int Id { get; set; }
public virtual ICollection<Product> Related { get; set; }
}
I also suggest you comment out the code in your RelatedProductsEntityConfiguration Configure method

Is it possible to use an owned type for many-to-many relations?

In Entity Framework Core version 2.2 or 3.0, is it possible to use owned/complex types in such a way that this kind of configuration is possible:
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public ProductProperties Properties { get; set; }
}
public class ProductProperties {
public List<ProductSize> Sizes { get; set; }
}
public class Size {
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductSize {
public int ProductId { get; set; }
public Product Product { get; set; }
public int SizeId { get; set; }
public Size Size { get; set; }
}
modelBuilder.Entity<ProductSize>()
.HasOne(x => x.Product)
.WithMany(x => x.Properties.Sizes)
.HasForeignKey(x => x.ProductId);
modelBuilder.Entity<ProductSize>()
.HasOne(x => x.Size)
.WithMany()
.HasForeignKey(x => x.SizeId);
The error message which is seen for this kind of approach usually ends up in:
'x => x.Properties.Sizes' is not a valid property expression. The expression should represent a simple property access: 't => t.MyProperty'
An earlier found answer is almost exactly matching my question, but this was posted in 2013. By the time it was almost certainly not possible.
HasForeignKey relationship through a Complex Type property
The sources on Microsoft are only giving examples for creating an entity with the complex type itself, not for creating relationships between them.
The cause of the issue
In your sample code it's quite clear there is no specific Many to Many relation. To make my argument a bit more convincing what follows is a model of your entities and their relations:
The new class structure
For a Many to Many relation to work in EF the product and size tables need to have an implicit relation with each other through a singular junction table. In my proposed solution I've chosen the ProductProperty table. There I've added the fields from the productsize junction table:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ProductProperty> Properties { get; set; }
}
public class ProductProperty
{
public int ProductId { get; set; }
public Product Product { get; set; }
public int SizeId { get; set; }
public Size Size { get; set; }
}
public class Size
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ProductProperty> Properties { get; set; }
}
The functions
modelBuilder.Entity<ProductProperty>()
.HasKey(pp => new { pp.ProductId, pp.SizeId });
modelBuilder.Entity<ProductProperty>()
.HasOne(pp => pp.Product)
.WithMany(p => p.Properties)
.HasForeignKey(pp => pp.ProductId);
modelBuilder.Entity<ProductProperty>()
.HasOne(pp => pp.Size)
.WithMany(p => p.Properties)
.HasForeignKey(pp => pp.SizeId);
Additional advice (EDIT)
Make the "Size" class a generic property class. This way the Many-to-Many relation won't get broken and querying will also be very easy:
public class Property
{
public int Id { get; set; }
public PropertyType propType { get; set; }
public string propValue { get; set; }
}
public enum PropertyType
{
Size,
Fontsize,
...
}
As a final argument this change will make it easier to change existing properties or add new ones
Sources
https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration
You can check the owned entity types released in 2019 Check documentation here
An example from the link is the following:
public class Distributor
{
public int Id { get; set; }
public ICollection<StreetAddress> ShippingCenters { get; set; }
}
The owns many function should help you like this:
modelBuilder.Entity<Distributor>().OwnsMany(p => p.ShippingCenters, a =>
{
a.WithOwner().HasForeignKey("OwnerId");
a.Property<int>("Id");
a.HasKey("Id");
});
Let me know if I misunderstood your question.

EF Code First many-to-many: how to specify name of th third table and add some other properties to it?

I need to implement the many-to-many relationship in the Entity Framework Code First, and map this relationship to the third table. And I want to add to this table some other fields such as autoincremented Id and the AppointmentDateTime for exaple:
public class User {
public int Id { get; set; }
// some other properties ......
public virtual List<Role> Roles { get; set; }
}
public class UserTypeConfiguration : EntityTypeConfiguration<User> {
public UserTypeConfiguration() {
HasKey(k => k.Id);
Property(p => p.Email).IsRequired();
//[∞ — ∞]
HasMany<Role>(u => u.Roles).WithMany(r => r.Users).Map(m => m.MapLeftKey("UserId").MapRightKey("RoleId").ToTable("UserRoles"));
}
}
But Entity Framework generates a table with the wrong name that which I have passed, and wrong names navigation proiperties I have passed to the mapping.
The name of table "RoleUsers" and the names of navigation proiperties are "User_Id" and "Role_Id".
How to implement correct names of mapping and how to add some other properties to the UserRoles table?
Since you need to add additional properties to describe the relationship, you have to consider many-to-many association as two one-to many ones:
public class User
{
// other properties omitted
public virtual List<UserRole> UserRoles { get; set; }
}
public class Roles
{
// other properties omitted
public virtual List<UserRole> UserRoles { get; set; }
}
public class UserRole
{
public int Id { get; set; }
public DateTime AppointmentDateTime { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public int RoleId { get; set; }
public Role Role { get; set; }
}
Configuration:
public class UserRoleConfiguration : EntityTypeConfiguration<UserRole>
{
public UserRoleConfiguration()
{
// scalar properties config omitted
HasRequired(_ => _.User)
.WithMany(_ => _.UserRoles)
.HasForeignKey(_ => _.UserId);
HasRequired(_ => _.Role)
.WithMany(_ => _.UserRoles)
.HasForeignKey(_ => _.RoleId);
ToTable("UserRoles");
}
}

Extra foreign key in Entity Framework 6

I designed employee and employee relation tables as shown below:
public class Employee
{
public int EmployeeId { get; set; }
public EmployeeType EmployeeType { get; set; }
public int? ReportingManagerId { get; set; }
public Employee ReportingManager { get; set; }
public virtual List<EmployeeRelation> Colleagues { get; set; }
}
public class EmployeeRelation
{
public int EmployeeRelationId { get; set; }
public int EmployeeId { get; set; }
public Employee Employee { get; set; }
public int ColleagueId { get; set; }
public Employee Colleague { get; set; }
}
And corresponding configuration classes as:
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
public EmployeeConfiguration()
{
HasKey(e => e.EmployeeId);
Property(e => e.EmployeeId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
HasOptional(e => e.ReportingManager).WithMany().HasForeignKey(e => e.ReportingManagerId);
}
}
public class EmployeeRelationConfiguration : EntityTypeConfiguration<EmployeeRelation>
{
public EmployeeRelationConfiguration()
{
HasRequired(e => e.Employee).WithMany().HasForeignKey(e => e.EmployeeId).WillCascadeOnDelete(false);
HasRequired(e => e.Colleague).WithMany().HasForeignKey(e => e.ColleagueId).WillCascadeOnDelete(false);
}
}
With the above code, everything is fine except that, for the table EmployeeRelations, EF 6.0.2 is creating one additional ForeignKey for EmployeeRelation as Employee_EmployeeId (in addition to EmployeeId & ColleagueId ForeignKeys). But if, I use Data Annotations instead of Fluent API, this extra ForeignKey is gone. I don't like Annotations and want to use Fluent API only. Can someone help me what is wrong with my code?
Thanks in advance.
UPDATE:
I resolved it myself.
Problem is multiple relations. If we look at EmployeeRelation class, it has 2 properties of type Employee which are binding to the Employee class and this is where EF is getting confused. To avoid this problem, we need to use overloaded version of WithMany API:
HasRequired(e => e.Employee).WithMany().HasForeignKey(e => e.EmployeeId).WillCascadeOnDelete(false);
HasRequired(e => e.Colleague).WithMany(e => e.Colleagues).HasForeignKey(e => e.EmployeeId).WillCascadeOnDelete(false);

Entity Framework with Condition Relationship

Is it possible to have a relationship that is based on a condition in Entity Framework? My model looks something like this...
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
public OwnerType OwnerType { get; set; }
public int OwnerId { get; set; }
public virtual Organization OrganizationOwner { get; set; }
public virtual User UserOwner { get; set; }
}
public enum OwnerType
{
Organization = 1,
User = 2
}
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
//[other properties specific to Organization]
public virtual List<Documents> Documents { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
//[other properties specific to User]
public virtual List<Documents> Documents { get; set; }
}
So, what I'd like to is set up a relationship so that the OrganizationOwner property of a Document instance automatically gets populated when the OwnerType == OwnerType.Organization, and the UserOwner property is populated when OwnerType == OwnerType.User.
Is this possible to set up this kind of relationship in EntityFramework - Code First? Something like this in the mapping...
EntityTypeConfiguration<Document>.HasOptional(d => d.OrganizationOwner)
.WithMany(o => o.Documents)
.HasForeignKey(d => d.OwnerId)
.Where(d => d.OwnerType == OwnerType.Organization);
EntityTypeConfiguration<Document>.HasOptional(d => d.UserOwner)
.WithMany(u => u.Documents)
.HasForeignKey(d => d.OwnerId)
.Where(d => d.OwnerType == OwnerType.User);
I would like to be able to leverage joins on the OrganizationOwner and UserOwner when setting up my Linq queries on the context so that I don't have to do a separate selects on those entities for each Document. Is this type of relationship supported or is there a better way to do this? Thanks.

Categories