EF: configuring many to many relationship (code first) - entity-framework

In my scenario I have Jobs, Companies and Departments.
Single Job may have only one Company; Company may have multiple Jobs (One-To-Many)
Single Job may have multiple Departments; Department may have multiple Jobs (Many-To-Many).
I want to set relations using Foreign Keys only. For that I have property of Foreign Key and lazy navigation property.
These are my classes:
public class JobEntity
{
[Key]
public int Id
{
get;
set;
}
public Companies CompanyId
{
get;
set;
}
//Navigation
[ForeignKey("CompanyId")]
public virtual CompanyEntity Company
{
get;
set;
}
public IList<Departments> Departments
{
get;
set;
}
//navigation
public virtual IList<DepartmentEntity> DepartmentsNavigation
{
get;
set;
}
}
public class DepartmentEntity
{
public Departments Id
{
get;
set;
}
public string Name
{
get;
set;
}
//navigation
public virtual IList<JobEntity> Jobs
{
get;
set;
}
}
public class CompanyEntity
{
public Companies Id
{
get;
set;
}
public string Name
{
get;
set;
}
//navigation
public virtual List<JobEntity> Jobs
{
get;
set;
}
}
Also, I have many-to-many mapping inside my context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<JobEntity>()
.HasMany<DepartmentEntity>(s => s.DepartmentsNavigation)
.WithMany(c => c.JobsNavigation)
.Map(cs =>
{
cs.MapLeftKey("JobId");
cs.MapRightKey("DepartmentId");
cs.ToTable("JobsDepartments");
});
}
When I set CompanyId into Job, everything work as expected: when I getting Job from DB, I have an associated Company lazy loaded.
However, when I setting into Job list of related foreign keys ('Departments') - when I loading Job from DB this list is null and departments navigation property ('DepartmentsNavigation') having count of 0 (I was expected to have collection of department ids that I set + lazy loaded collection of departments).
What I doing wrong?

For many-to-many, you can't use a foreign key association.
There's a reference to this here:
One-to-one relation in EFv4 always uses Foreign Key association and many-to-many relation always uses Independent association.
and here:
Note: In many-to-many (*:*) you cannot add foreign keys to the entities. In a *:* relationship, the association information is managed with an independent object.
and here:
However, if you have a pure many-to-many relationship that is connected by a join table that contains only foreign keys, the EF will use an independent association to manage such many-to-many relationship.
Your foreign keys are already in the JobsDepartments table, so EF won't let you add additional foreign keys for that relationship in the JobEntity and DepartmentEntity entities.

Related

Cascade delete in one to one relationship

I want to have cascade delete in 1:1 relationship, where i reference multiple entities to one. Problem is throws me an error on database update
Introducing FOREIGN KEY constraint 'FK_dbo.CategoryArticles_dbo.Articles_Article_Id' on table 'CategoryArticles' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
RoutingSeo entity is for storing seo friendly url in database for later usage. My problem is clearly M:N relationship between Article and Category. Is there something how can I deal with this problem?
Here are my entities of my model
public class Article : IEntity<int>
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Category> Categories { get; set; }
[Required]
public virtual RoutingSeo RoutingSeo { get; set; }
public int RoutingSeoId { get; set; }
}
public class Category : IEntity<int>
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Article> Articles { get; set; }
[Required]
public virtual RoutingSeo RoutingSeo { get; set; }
public int RoutingSeoId { get; set; }
}
public class SpecificProduct : IEntity<int>
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public RoutingSeo RoutingSeo { get; set; }
public int RoutingSeoId { get; set; }
}
public class RoutingSeo : IEntity<int>
{
public int Id { get; set; }
public string SeoRoute { get; set; }
public Article Article { get; set; }
public SpecificProduct SpecificProduct { get; set; }
public Category Category { get; set; }
}
Here is my fluent api code where i specify cascade delete
modelBuilder.Entity<Article>()
.HasRequired(x => x.RoutingSeo)
.WithOptional(x=>x.Article)
.WillCascadeOnDelete();
modelBuilder.Entity<Category>()
.HasRequired(x => x.RoutingSeo)
.WithOptional(x=>x.Category)
.WillCascadeOnDelete();
modelBuilder.Entity<SpecificProduct>()
.HasRequired(x => x.RoutingSeo)
.WithOptional(x=>x.SpecificProduct)
.WillCascadeOnDelete();
You are right, it is your many-to-many relation ship between Article and Category: one Article has zero or more Categories and every Category may be used by zero or more Articles.
If you delete an Article, its Categories can't be deleted automatically, because the Category might be used by other Articles, and even if it isn't used right now, entity framework doesn't know whether you want to use it tomorrow. After all, you specified that every Category might be used by zero or more Articles.
Similarly, if you remove a Category, entity framework can't automatically remove the Articles belonging to this category.
This differs from a one-to-many relationship. For example, if you have a one-to-many relationship of a Book and its Pages, then every Book has zero or more Pages and every Page belongs to exactly one Book.
If you remove the Book, then entity framework knows that it should automatically remove all Pages of the Book, which are all Pages with a foreign key BookId. If Entity Framework would only remove the Book, then we would have a bunch of Pages with foreign key value pointing to a non-existing Book. So in one-to-many relations, entity framework can cascade on delete.
Alas, in many-to-many this is not possible.
On the bright side, you have the advantage that you can delete the last Article of a Category, and keep the Category intact. Tomorrow you can add a new Article that uses this Category.
So if you want to remove an article, you manually have to remove it from the 'Categories` it uses:
many-to-many following the standard naming conventions:
class Article
{
public int Id {get; set;}
// an Article belongs to zero or more Categories:
public virtual ICollection<Category> Categories {get; set;}
...
}
class Category
{
public int Id {get; set;}
// a Category is used by zero or more Articles:
public virtual ICollection<Article> Articles{get; set;}
...
}
Don't forget to declare your ICollections virtual!
class MyDbContext : DbContext
{
public class DbSet<Article> Articles {get; set;}
public class DbSet<Category> Categories {get; set;}
}
You don't have to mention the junction-table, entity framework will make it automatically for you, but you won't have to use it for joins if you want Articles with their Categories, or Categories with their Articles, just use the ICollections
Note: As Categories is not the expected plural of Category, you'll have to tell entity framework the proper table name. Out of scope of this question.
Delete an Article, but keep all Categories it belongs to alive:
using (var dbContext = new MyDbContext(...))
{
Article articleToRemove = ...
dbContext.Articles.Remove(articleToRemove);
dbContext.SaveChanges();
}
Entity framework will automatically perform the proper joins, and remove the articleToRemove from every Category. However, the Categories won't be removed.
In fact, internally the Categories table doesn't change at all. All records with Article.Id will be removed from the junction table.

how to setup foreign key relationship in entity framework code first?

I have three entities, User, Domain and Role.
I have this class to define the relationship of the three entities:
public class UserDomainRole
{
[Key]
public int UserId { get; set; }
[Key]
public int DomainId { get; set; }
[Key]
public int RoleId { get; set; }
}
So how to I setup a foreign key relationship between UserDomainRole and other three table?
Is it something like this?
public class DeniedDomainRole
{
[Key]
public int UserId { get; set; }
public virtual User User { get; set; }
[Key]
public int DomainId { get; set; }
public virtual Domain Domain { get; set; }
[Key]
public int RoleId { get; set; }
public virtual Role Role { get; set; }
}
Then entity framework will work the relationship out?
This code will give you an exception with a message "Unable to determine composite primary key ordering"
In code first if you want to create a composite primary key then you have to use the to give the key/column order, like that:
[Key, Column(Order=1)]
Where
Order = The composite Primary key/column order on the table
public class UserDomainRole
{
[Key, Column(Order=1)]
public int UserId { get; set; }
[Key, Column(Order=2)]
public int DomainId { get; set; }
[Key, Column(Order=3)]
public int RoleId { get; set; }
}
So how to I setup a foreign key relationship between UserDomainRole and other three table? Is it something like this?
It is depending on what kind of the relationships you have:
Configure One-to-Zero-or-One Relationship:
Here, we will configure One-to-Zero-or-One relationship between two entities, e.g. Entity1 can be associated with zero or only one instance of Entity2.
http://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-first.aspx
Configure One-to-Zero-or-One Relationship:
Here, we will configure One-to-Zero-or-One relationship between two entities, e.g. Entity1 can be associated with zero or only one instance of Entity2.
http://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-first.aspx
Configure Many-to-Many relationship:
Here, we will learn how to configure Many-to-Many relationship between the Student and Course entity classes. Student can join multiple courses and multiple students can join one course.
http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
In this way you can relation with the user model
public int? UserUserId{get;set;}
public virtual User User{get;set;}
? makes nullable your foreignkey.

One-to-one in EF only producing one Foreign Key

I'm attempting to build a 1-1 relationship - a Tenant has a Url, and vice versa:
Models
public class Tenant {
[Key]
[Required]
public int TenantId { get; set; }
public string Name { get; set; }
public virtual Url Url { get; set; }
public int UrlId { get; set; }
}
public class Url {
[Key]
[Required]
public int UrlId { get; set; }
public string Name { get; set; }
public virtual Tenant Tenant { get; set; }
public int TenantId { get; set; }
}
Configs
public class UrlConfiguration : EntityTypeConfiguration<Url> {
public UrlConfiguration() {
HasKey(s => s.UrlId);
}
}
public class TenantConfiguration : EntityTypeConfiguration<Tenant>
{
public TenantConfiguration() {
HasRequired(s => s.Url).WithRequiredPrincipal(s => s.Tenant);
}
}
Result:
I'd expect there to be a foreign key on both models... why is this not the case?
A one-to-one relationship with both ends having required foreign keys cannot exist in a relational database. If saving a new Tenant record requires a URL record, but in order to create that URL record, that URL requires a Tenant record, where will you begin?
Even though on a database level it can't practically exist, this model will still work. From my experience, Entity Framework will enforce the dependency on application level, and will throw an EntityException when it detects that one of the entities you're trying to save has no relationship to one of the other.
It creates this database model so that it can still save your entities, and enforce relationships on an application level.
No, this isn't nice on a database level as the one-to-one constraint won't be enforced there. If you need the database constraints as well, consider merging the tables or redesigning your data structures so that a one-to-one relationship isn't necessary.

How do I override entity framework code first convention for creating a foreign key

I have a property on my items class called vend_id which of course EF thinks is a foreign key to the vendor table. It actually should be a foreign key in the database but for reasons unknown to me the designers of the db chose not to make it a foreign key.
I am using EF to create a copy of the db schema on the local machine. When EF creates the database I want to tell it not to create a foreign key on the vend_id column. How do I do that? Ideally I do not want to rename the property because there are several such instances in my db and it just makes it confusing.
Thank you,
Sam
You can't have a navigation property to a Vendor entity in your Item entity class if the Items table does not have a foreign key to table Vendor. If you did not specify a navigation property in entity class Item, EF would not infer that vend_id is a foreign key.
Update:
Unable to reproduce with the following:
[Table("EntityA")]
public partial class EntityA
{
public int Id { get; set; }
public Nullable<int> EntityBId { get; set; }
public string Description { get; set; }
[ForeignKey( "EntityBId" )]
public virtual EntityB EntityB { get; set; }
// this is not created as a FK
// nor does EntityCId cause a FK
public int EntityC_Id { get; set; }
}
[Table("EntityC")]
public class EntityC
{
public EntityC()
{
EntitiesD = new HashSet<EntityD>();
}
public int EntityCId { get; set; }
public string Name { get; set; }
public virtual ICollection<EntityD> EntitiesD { get; set; }
}

Mapping entities with fluent api on entity framework 5

I have a question.
I have these two tables:
The principal table is User with Customer dependence.
The reverse engineer code first generated classes as follows:
public class User
{
public User()
{
this.Customers = new List<Customer>();
}
...
public virtual ICollection<Customer> Customers { get; set; }
}
public class Customer
{
public Customer()
{
}
...
public int UserID { get; set; }
public virtual User User { get; set; }
}
I made the following modification in the user class:
public class User
{
public User()
{
}
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
Because the relationship is One-to–Zero-or-One.
The original mapping is this:
// Relationships
this.HasRequired(t => t.User)
.WithMany(t => t.Customers)
.HasForeignKey(d => d.UserID);
And the modified mapping is this :
this.HasRequired(t => t.User)
.WithOptional(t => t.Customer)
.Map(m => m.MapKey("UserID"));
Is That correct?
If not, how would this mapping?
Thanks.
No, it's not correct.
The best thing you can do - supposed you can change the database schema - is removing the UserID foreign key from the Customer table and then create the relationship in the database between the two primary keys so that Customer.CustomerID is the foreign key in the association.
Reverse Engineering should then automatically create the expected one-to-one relationship, like so:
public class Customer
{
public int CustomerID { get; set; }
public virtual User User { get; set; }
//...
}
public class User
{
public int UserID { get; set; }
public virtual Customer Customer { get; set; }
//...
}
//...
this.HasRequired(t => t.User)
.WithOptional(t => t.Customer);
If you can't change the database schema, your best bet is to only remove the collection ICollection<Customer> Customers from the User class and keep the relationship as one-to-many.
The reason for all this is that EF only supports shared primary key one-to-one associations, but not foreign key one-to-one associations. (The latter one you can only "fake" by removing the collection, but it's still one-to-many from EF viewpoint.)
You can read more about one-to-one associations with EF and its limitations here:
One-to-one Shared Primary Key Associations
One-to-one Foreign Key Associations