Use association properties with Entity Framework - c#

I am just to set up a model in which you have news articles and topics they belong to. This is a simple association but now it get's a bit "extended". One of those topic association can be marked as the "mainTopic".
I've set up 3 tables:
avsn_content, containing the newsArticles identified by id
avsn_content_topics, containing the assoc, having assocId, id, topicId and a column indicating the mainTopic
avsn_topics, containing the topic, identified by topicId
Furthermore, I have models for these three tables. My association model is designed as follows:
[Table("avsn_content_topcis")]
public class TopicNewsModel
{
[Key]
[Column("assocId")]
public int Id { get; set; }
public NewsArticleModel NewsArticle { get; set; }
public TopicModel Topic { get; set; }
[Column("mainTopic")]
public bool IsMainTopic { get; set; }
}
My relationship setup looks like this:
modelBuilder.Entity<NewsArticleModel>()
.HasMany(x => x.Topics)
.WithMany()
.Map(m => m.MapLeftKey("id")
.MapRightKey("topicId")
.ToTable("avsn_content_topics"));
I am getting this error:
One or more validation errors were detected during model generation:
NewsArticleModelTopicNewsModel: Name: The EntitySet
'NewsArticleModelTopicNewsModel' with schema 'dbo' and table
'avsn_content_topics' was already defined. Each EntitySet must refer
to a unique schema and table.
Removing the ToTable option does not help it, so what am I doing wrong here?

Well, found the answer right after posting this. Trying to pick the problem from the other end of the rope showed that I had to start off from the TopicNewsModel instead from the NewsModel.
modelBuilder.Entity<TopicNewsModel>()
.HasRequired(topicNews => topicNews.NewsArticle)
.WithMany(news => news.Topics)
.Map(m => m.MapKey("id"));
modelBuilder.Entity<TopicNewsModel>()
.HasRequired(topicNews => topicNews.Topic)
.WithMany()
.Map(m => m.MapKey("topicId"));

Related

EF 6 Configured for one-to-one relationship sometimes violates the constraint

I'm using Fluent API in EF 6.1.3 to define a one-to-one relationship between the entity User and the entity EcommerceCart. Everything seems to work fine 99.9% of the time, but every once in a while our exception logger tells us that while trying to access User.Cart the following exception is thrown:
A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object.
We checked the database, and it seems that Entity Framework managed to create 2 Carts for the same User, despite the one-to-one relationship of the entity types.
And what strikes me the most is that the exception never occurs when the entities are created, but when the code tries to access User.Cart, finding more than one result in the database.
Any idea of how this could happen?
PS: I'm using Lazy Loading, although I don't think this should make a difference.
These are my entities:
public class User
{
public Guid Id { get; set; }
public virtual EcommerceCart Cart{ get; set; }
}
public class EcommerceCart
{
public Guid Id { get; set; }
public virtual User User { get; set; }
}
These are my configuration files:
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasKey(x => x.Id);
Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
HasOptional(x => x.Cart).WithOptionalPrincipal(y => y.User).Map(x => x.MapKey("User_Id"));
}
}
public class EcommerceCartConfiguration : EntityTypeConfiguration<EcommerceCart>
{
public EcommerceCartConfiguration()
{
HasKey(a => a.Id);
Property(a => a.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
}
}
And this is how the EcommerceCarts table looks like:
Following adam0101 advice, I created the following migration, which forces
the foreign key User_Id to be unique:
public partial class EcommerceCart_User_Index : DbMigration
{
public override void Up()
{
DropIndex("dbo.EcommerceCarts", new[] { "User_Id" });
Sql(
#"CREATE UNIQUE NONCLUSTERED INDEX IX_User_Id
ON dbo.EcommerceCarts(User_Id)
WHERE User_Id IS NOT NULL");
}
}
Unfortunately, I couldn't find any other solution using Fluent API, because it seems like there is no way to create a unique index on Navigation properties without using SQL.
If anyone knows a better solution, please let me know.

One to Many fluent with additional fields

Let's say I have a manufacturer which produces lots of models. Each model has several attributes, and these attributes should come in a certain sort order. Here is what I did before:
Table "ManufacturerModel" with
public virtual ICollection<VehicleAttribute> Attributes { get; set; }
Table "VehicleAttribute" with
public virtual ICollection<ManufacturerModel> Models { get; set; }
and finally ModelBuilder fluent api to connect these two tables so I get a 1:n relationship:
modelBuilder.Entity<ManufacturerModel>()
.HasMany(mm => mm.Attributes)
.WithMany(a => a.Models)
.Map(mv =>
{
mv.ToTable("Model_VehicleAttribute", SchemaNames.Config);
mv.MapLeftKey("ManufacturerModel_Id");
mv.MapRightKey("VehicleAttribute_Id");
});
Works perfect. BUT: where do I put the SortOrder column? In my opinion it should be inside mapping table "Model_VehicleAttribute" so I can select a ManufacturerModel and get all Attributes in a sorted order. How can this be achieved?

One-to-one relationship in Entity Framework states that Multiplicity is not valid

In our MVC4 application we have a WeekDay Model:
public class WeekDay
{
[Key]
public int WeekDayId { get; set; }
[Required]
[Display(Name = "Dag")]
public string Day { get; set; }
[ForeignKey("Shop")]
public int ShopId { get; set; }
public virtual Shop Shop { get; set; }
}
And Shop model:
public class Shop
{
public int ShopId { get; set; }
[Display(Name = "Winkel")]
public string Name { get; set; }
public int WeekDayId { get; set; }
public virtual WeekDay WeekDay { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
We need a one-to-one relationship from WeekDay to Shop and the other way around (so that every shop has a weekday and vice versa). Unfortunately the code above gives the following error:
Multiplicity is not valid in Role 'Shop_WeekDay_Target' in relationship 'Shop_WeekDay'. Because
the Dependent Role properties are not the key properties, the upper bound of the multiplicity of
the Dependent Role must be '*'.
We've tried various things found on the internet to solve this issue but none seem to work (it did build when we added two primary keys, but then it fails while running the seed method and we get this error: The index 'IX_ShopId' is dependent on column 'ShopId'.
ALTER TABLE ALTER COLUMN ShopId failed because one or more objects access this column.).
Does anyone know how a one-to-one relationship can be made in this case?
/Edit Adding:
modelBuilder.Entity<WeekDay>()
.HasRequired(a => a.Shop)
.WithMany()
.HasForeignKey(u => u.ShopId);
modelBuilder.Entity<Shop>()
.HasRequired(a => a.WeekDay)
.WithMany()
.HasForeignKey(u => u.WeekDayId);
To the DbContext makes a succesful migration, but on update-database gives the error:
Introducing FOREIGN KEY constraint 'FK_dbo.WeekDays_dbo.Shops_ShopId'
on table 'WeekDays' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other
FOREIGN KEY constraints. Could not create constraint. See previous
errors.
And setting WillCascadeOnDelete to false gives the error:
The index 'IX_ShopId' is dependent on column 'ShopId'. ALTER TABLE
ALTER COLUMN ShopId failed because one or more objects access this
column.
/Edit 3
My database structure looks as follows:
Now I "solved" the issue by not making the one-to-one relationship only one way, so that to match the Shop with the WeekDay I need to query the WeekDay table first and then see which shop matches. Then I can use the ShopId in further queries to only select the Categories from the current shop. It would have been cleaner if you could go from Shop to WeekDay so that I only need to query once.
I do not care if it is one to many if it is hard to make (I will only use one from the many), but it should be the other way around (from shop to weekday instead of weekday to shop). The one to many is the wrong way around as can be seen in the upper right.
The first issue I notice is here:
modelBuilder.Entity<WeekDay>()
.HasRequired(a => a.Shop)
.WithMany()
.HasForeignKey(u => u.ShopId);
modelBuilder.Entity<Shop>()
.HasRequired(a => a.WeekDay)
.WithMany()
.HasForeignKey(u => u.WeekDayId);
Each of the configurations above defines a one-to-many relationship. If you want a one-to-one relationship it should look something like this (check my syntax as I am not at my dev workstation to verify):
modelBuilder.Entity<WeekDay>()
.HasRequired(a => a.Shop)
.WithRequiredDependent();
Or, optionally, you can do this as well:
modelBuilder.Entity<WeekDay>()
.HasRequired(a => a.Shop);
modelBuilder.Entity<Shop>()
.HasRequired(a => a.WeekDay);

Entity Framework Parent Child - Child refers to parent more than once

I have a situation with EF5 and a complex object. The basics is that I have a parent to child complex object, but the child refers back to the parent, more than once. I have tried various options but am not finding a solution that answers the question. The closest I have got is this answer (option 2c)
My model looks like below:
public class StaffMember
{
public virtual Guid StafId { get; set; }
// other props
// List of leave apps (Approved/Cancelled etc)
public virtual ICollection<StaffLeaveApp> LeaveApps { get; set; }
}
//Staff Leave Application
public class StaffLeaveApp
{
public virtual Guid LeaveId { get; set; }
public virtual Guid StaffId { get; set; }
// other props...
// Leave approved by? (2 approvals required)
public virtual StaffMember ApprovedBy1 { get; set; }
public virtual StaffMember ApprovedBy2 { get; set; }
}
my mappings look like this
public class StaffMap : EntityTypeConfiguration<StaffMember>
{
public StaffMap()
{
ToTable("tblStaffMembers");
HasKey(x => x.StaffId);
// other mappings...
HasMany(x => x.LeaveApps);
}
}
public class StaffLeaveAppMap: EntityTypeConfiguration<StaffLeaveApp>
{
public StaffLeaveAppMap()
{
ToTable("tblStaffMembersLeaveApps");
HasKey(x => x.LeaveId);
Property(x => x.StaffId).HasColumnName("StaffID");
//Child Relationships
HasOptional(x => x.ApprovedBy1).WithMany().Map(m => m.MapKey("LeaveApprovedBy1"));
HasOptional(x => x.ApprovedBy2).WithMany().Map(m => m.MapKey("LeaveApprovedBy2"));
}
}
Table (sorry, no images)
StaffID uniqueidentifier (FK - tblStaffMembers)
LeaveID uniqueidentifier (PK)
LeaveApprovedBy1 uniqueidentifier (FK - tblStaffMembers)
LeaveApprovedBy2 uniqueidentifier (FK - tblStaffMembers)
The business rule says: a staff member has "many" leave applications and a leave application belongs to a single staff member. Each application requires the approval of 2 staff members (managers) before it is "approved".
How would I map the above using EF so that a single staff member has a "many" leave applications (working already) and a leave application is mapped back to a staff member whom approved it for the first approval and then again for the seond approval. If I use the one mapping for "ApprovedBy1" only then EF is happy and all works as expected. The moment I add the second approval mapping EF struggles with the SQL queries it generates.
I am not sure how to tell EF to map back to the StaffMembers table to specify whom approved the application at level 1 and whom approved it at level 2. It almost ends up being a many to many relationship.
Any ideas?
You are looking for the inverse property, which is the property at the other end of an association. In EF, there are two way to mark a property as inverse.
Data annotations: InversePropertyAttribute.
Fluent mapping
As you already have fluent mapping I'll show you how you'd do it there:
HasOptional(x => x.ApprovedBy1).WithMany(x => x.LeaveApps)
.HasForeignKey(s => s.StaffId);
HasOptional(x => x.ApprovedBy2).WithMany()
.Map(m => m.MapKey("LeaveApprovedBy2"));
The HasOptional(...).WithMany(...) pair is a way to map inverse properties. Coming from the other side you can use e.g. HasMany(....).WithOptional(...).

A relationship is in the Deleted state

When I am trying to clear a collection (calling .Clear) I get the following exception:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
The inner exception is:
A relationship from the 'User_Availability' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a corresponding 'User_Availability_Target' must also in the 'Deleted' state.
User looks like this:
....
ICollection<Availability> Availability { get; set; }
Availability looks like this:
int ID { get; set; }
User User { get; set; }
DateTime Start { get; set;
DateTime End { get; set; }
Configuration is as follows:
HasMany(x => x.Availability).WithRequired(x => x.User);
HasRequired(x => x.User).WithMany(x => x.Availability);
The code causing the problem is:
user.Availability.Clear();
I've looked at other alternatives such as using the DbSet to remove items, but I don't feel my code will be as clean. Is there a way to accomplish this by clearing the collection?
The only way that I'm aware of to make it work is defining the relationship as an identifying relationship. It would required to introduce the foreign key from Availability to User as a foreign key into your model...
public int ID { get; set; }
public int UserID { get; set; }
public User User { get; set; }
...and make it part of the primary key:
modelBuilder.Entity<Availability>()
.HasKey(a => new { a.ID, a.UserID });
You can extend your mapping to include this foreign key (just to be explicit, it isn't required because EF will recognize it by convention):
modelBuilder.Entity<Availability>()
.HasRequired(a => a.User)
.WithMany(u => u.Availability)
.HasForeignKey(a => a.UserID);
(BTW: You need to configure the relationship only from one side. It is not required to have both these mappings in your question.)
Now you can clear the collection with user.Availability.Clear(); and the Availability entities will be deleted from the database.
There is one trick. You can delete entities without using special DbSet:
(this.dataContext as IObjectContextAdapter).ObjectContext.DeleteObject(entity);
Execute this for each item in Availability collection before clearing it. You don't need 'identifying relationships' for this way.
In case someone has the same problem using SQLite:
Unfortunately the accepted answer does not work with SQLite because SQLite does not support auto increment for composite keys.
You can also override the SaveChanges() Method in the Database context to delete the children:
//// Long Version
//var localChilds = this.SubCategories.Local.ToList();
//var deletedChilds = localChilds.Where(w => w.Category == null).ToList();
//foreach(var child in deletedChilds) {
// this.SubCategories.Remove(child);
//}
// Short in LINQ
this.SubCategories.Local
.Where(w => w.Category == null).ToList()
.ForEach(fe => this.SubCategories.Remove(fe));
#endregion
See this great Blogpost as my source (Unfortunately written in german).

Categories