I have a Mentorship entity, which has Student and Mentor as FKs:
[Required]
public int MentorId { get; set; }
public virtual User Mentor { get; set; }
[Required]
public int StudentId { get; set; }
public virtual User Student { get; set; }
User model:
public virtual ICollection<Mentorship> Mentorships { get; set; }
Fluent API:
modelBuilder.Entity<Mentorship>()
.HasRequired(c => c.Mentor)
.WithMany()
.HasForeignKey(u => u.MentorId);
modelBuilder.Entity<Mentorship>()
.HasRequired(c => c.Student)
.WithMany()
.HasForeignKey(u => u.StudentId);
In my database, I see StudentId and MentorId columns which have been populated correctly, but I also see a User_UserId column that is not being used by anything. What have I done wrong?
You have used the WithMany() overload that configures the relationship to be required:many without a navigation property on the other side of the relationship - but you do have a navigation property on the other side of the relationship.
Try this:
modelBuilder.Entity<Mentorship>()
.HasRequired(c => c.Mentor)
.WithMany(d => d.Mentorships)
.HasForeignKey(u => u.MentorId);
modelBuilder.Entity<Mentorship>()
.HasRequired(c => c.Student)
.WithMany(d => d.Mentorships)
.HasForeignKey(u => u.StudentId);//oops! just realised that we just
//specified that Mentorships is using MentorId
//as the FK
References:
Required WithMany Method
Why do I get an extra foreign key?
Edit
Scratch that. Just realised you are trying to create two relationships with only one navigation property on the many side. You can't have a navigation property with 2 foreign keys. You need to introduce inheritance on the User side or remove the Mentorships navigation property from the User class or introduce separate StudentMentorships and MentorMentorships navigation properties
Edit 2
Finding a user once you've defined separate navigation properties
int userId = 123;//the one we want to find
var user = Users.Where(x => x.StudentMentorships.Any(s => s.StudentID == userId)
|| x.MentorMentorships.Any(s => s.MentorID == userId);
Related
A product can have many other related products. The following creates the join table perfectly but when I add a product to another product's RelatedProducts I get the following error on save: 'The value of 'ProductAssociation.ProductID' is unknown when attempting to save changes. This is because the property is also part of a foreign key for which the principal entity in the relationship is not known.'
modelBuilder.Entity<Product>()
.HasMany(x => x.RelatedProducts)
.WithMany(r => r.RelatedProducts)
.UsingEntity<ProductAssociation>(
x => x.HasOne(p => p.RelatedProduct).WithMany().HasForeignKey(f => f.RelatedProductID),
x => x.HasOne(p => p.Product).WithMany().HasForeignKey(f => f.ProductID).OnDelete(DeleteBehavior.NoAction),
x => x.ToTable("ProductAssociations"));
I think this is a similar answer
Entity Framework Core 2.0 many-to-many relationships same table
I also solved it with that provide link above like this in core 3+.
Product table
public class Product
{
// Other properties members ....
public ICollection<Product> Products { get; set; }
public ICollection<Product> ProductOf { get; set; }
}
Product Association table "ProductSet"
public class ProductSet
{
public int ProductId { get; set; }
public int SubProductId { get; set; }
}
OnModelCreating()
modelBuilder.Entity<Product>()
.HasMany(e => e.Products)
.WithMany(e => e.ProductOf)
.UsingEntity<ProductSet>(
e => e.HasOne<Product>().WithMany().HasForeignKey(e => e.ProductId),
e => e.HasOne<Product>().WithMany().HasForeignKey(e => e.SubProductId));
I need to make a one-to-one relation between two tables, but only one-way.
[Accounts]
account_id
name
etc...
[SalesReps]
sales_rep_id
account_id
account
etc...
So, I want the SalesReps table to have a relation to Accounts, but I don't want Accounts to have a reference to SalesReps.
I tried this:
modelBuilder.Entity<sales_rep>()
.HasRequired(a => a.account)
.WithMany()
.HasForeignKey(a => a.account_id);
But that gives me:
sales_rep_account_Source: : Multiplicity is not valid in Role 'sales_rep_account_Source' in relationship 'sales_rep_account'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
The way you do this is with WithRequiredDependent() configuration. Your entities will be:
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SalesRep
{
public int Id { get; set; }
public Account Account { get; set; }
}
In OnModelCreating() method you should do this:
modelBuilder.Entity<Account>().HasKey(x => x.Id);
modelBuilder.Entity<SalesRep>().HasKey(x => x.Id);
modelBuilder.Entity<SalesRep>().HasRequired(x => x.Account).WithRequiredDependent().Map(x => x.MapKey("AccountId"));
It turns out, that all I had to do was:
modelBuilder.Entity<sales_rep>()
.HasRequired(a => a.account)
.WithOptional();
I have an issue to implement many-to-many relationship with same entities. Here's my class:
public class District
{
[Key]
public int DistrictId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Abbreviation { get; set; }
public List<District> SubDistricts { get; set; }
}
My goal is to have all districts within same table, and to have them correlated, many districts to many districts.
If I don't specify mappings, EF Code First acts as if it is one-to-many relationship.
I've tried to give directions to model builder, but it's not working:
modelBuilder.Entity<District>()
.HasMany(d => d.SubDistricts)
.WithMany(d => d.SubDistricts)
.Map(mc => { mc.ToTable("DistrictLinks", "dbo");
mc.MapLeftKey("ParentId");
mc.MapRightKey("ChildId");
});
Is there any way to do this with WF? Thanks in advance!
You must use the WithMany overload which doesn't take a parameter:
modelBuilder.Entity<District>()
.HasMany(d => d.SubDistricts)
.WithMany()
.Map(mc => { mc.ToTable("DistrictLinks", "dbo");
mc.MapLeftKey("ParentId");
mc.MapRightKey("ChildId");
});
It is not possible that the same navigation property is start and end of an association at the same time. They either must be different or the end is "unvisible" and not exposed in the model - which is the case in your model.
Your solution works well, thank you! In the meanwhile, I've came up with another way to resolve issue. Basically I've created two navigational properties in class:
public List<District> ChildDistricts { get; set; }
public List<District> ParentDistricts { get; set; }
so my mapping looks like this now:
modelBuilder.Entity<District>()
.HasMany(d => d.ParentDistricts)
.WithMany(d => d.ChildDistricts)
.Map(mc => { mc.ToTable("DistrictLinks", "dbo");
mc.MapLeftKey("ParentId");
mc.MapRightKey("ChildId");
});
As a result, I get exactly the same kind of table in SQL Server, but I believe I can navigate better like this. I actually forgot to mention that hierarchy is of importance here as well, not just links between districts.
Thank you once again :)
In my project I have following DomainModel.
public class Login
{
public Guid Id { get; set; }
public Login CreatedBy {get; set; }
}
I am using fluent configuration as below:
modelBuilder.Entity<Login>()
.HasKey(x => x.Id)
.ToTable("Login");
modelBuilder.Entity<Login>()
.HasOptional(x => x.CreatedBy)
.WithMany()
.HasForeignKey(x => x.CreatedBy);
My code in repository to get all Logins data is as below:
return from d in Db.Logins.Include("CreatedBy")
select d;
When I execute the code I am getting following error:
The foreign key component 'CreatedBy' is not a declared property on type 'Login'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.
Can anyone suggest what I am doing wrong here?
Thanks in advance
.. has not been explicitly excluded from the model and that it is a
valid primitive property
Your foreign key mapping .HasForeignKey(x => x.CreatedBy) does not use a primitive property.
public class Login
{
public Guid Id { get; set; }
public virtual Login CreatedBy {get; set; }
}
Then map it like
modelBuilder.Entity<Login>()
.HasKey(x => x.Id)
.ToTable("Login");
modelBuilder.Entity<Login>()
.HasOptional(x => x.CreatedBy)
.WithMany()
.Map(x => x.MapKey("ForeignKeyColumn"));
I would like to enable CASCADE DELETE on a table using code-first. When the model is re-created from scratch, there is no CASCADE DELETE set even though the relationships are set-up automatically. The strange thing is that it DOES enable this for some tables with a many to many relationship though, which you would think it might have problems with.
Setup:
Table A <- Table B.
Table B's FK points to Table A's PK.
Why would this not work?
Possible reason why you don't get cascading delete is that your relationship is optional. Example:
public class Category
{
public int CategoryId { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public Category Category { get; set; }
}
In this model you would get a Product table which has a foreign key to the Category table but this key is nullable and there is no cascading delete setup in the database by default.
If you want to have the relationship required then you have two options:
Annotations:
public class Product
{
public int ProductId { get; set; }
[Required]
public Category Category { get; set; }
}
Fluent API:
modelBuilder.Entity<Product>()
.HasRequired(p => p.Category)
.WithMany();
In both cases cascading delete will be configured automatically.
If you want to have the relationship optional but WITH cascading delete you need to configure this explicitely:
modelBuilder.Entity<Product>()
.HasOptional(p => p.Category)
.WithMany()
.WillCascadeOnDelete(true);
Edit: In the last code snippet you can also simply write .WillCascadeOnDelete(). This parameterless overload defaults to true for setting up cascading delete.
See more on this in the documentation
modelBuilder
.Entity<Product>()
.HasRequired(p => p.Category)
.WithMany(x => x.Products)
.WillCascadeOnDelete(true);