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).
Related
In previous versions of EF I can use following code to implement an identifying relationship:
public class Child
{
[Key, Column(Order = 1)]
public virtual int Id { get; set; }
[Key, Column(Order = 2)]
public virtual int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
It's needed to easily remove a child from collection like this:
var parent = _context.Parents.First();
var child = parent.Children.First();
parent.Children.Remove(child);
_context.SaveChanges();
This approach is described in http://www.kianryan.co.uk/2013/03/orphaned-child/ (the method #2).
But in EF7 this code throws exception when migration is creating:
An exception was thrown while executing a resolve operation. See the
InnerException for details. ---> Entity type 'Child' has composite
primary key defined with data annotations. To set composite primary
key, use fluent API.
I also tried to use FluentAPI as described in How to define nested Identifying Relationships Entity Framework code first in following code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithOne(c => c.Parent);
modelBuilder.Entity<Child>()
.HasKey(c => new {c.Id, c.ParentId});
base.OnModelCreating(modelBuilder);
}
This approach allow generate a migration successfully, but when I tried to remove a child from Children collection I got following exception:
System.InvalidOperationException: The association between entity types
'Parent' and 'Child' has been severed but the foreign key for this
relationship cannot be set to null. If the dependent entity should be
deleted, then setup the relationship to use cascade deletes.
But I wouldn't like to use cascade deletes, I would like to use identifying relationship!
Please, help me understand what I do incorrect. Thank you!
Use cascade on delete instead as this is what it's used for:
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithOne(c => c.Parent);
.WillCascadeOnDelete(true);
https://msdn.microsoft.com/en-gb/data/jj591620.aspx
Just in case someone sees this error, let me tell you how I resolved mine:
When you do an update, on EF you need to first query the database and get the data model, then map the Domain layer model with your changes onto it (basically copying fields onto the data), and finally call the DBContext update method, then save changes.
My problem was that my model (not the data model, the domain model) also had the sub objects on it.
So here's the data layer model (for example):
public class Parent
{
public int ChildId {get; set; }
[ForeignKey("ChildId")]
public virtual Child Child { get; set; }
}
And here's how the domain layer model should be:
public class Parent
{
public int ChildId { get; set; }
//public Child Child { get; set; } // this caused the error, keep reading if you want to know more.
}
When I was seeing the error, I had been using Autofac's runtime mapper to map the domain layer model's properties onto the data layer model. However, the child in the domain layer model was null, so it would nullify the data layer, causing the error:
"The association between entity types 'Parent' and 'Child' has been severed but the foreign key for this relationship cannot be set to null. If the dependent entity should be deleted, then setup the relationship to use cascade deletes."
By the way, in the db context class, I have the following relationship defined:
modelBuilder.Entity<Parent>()
.HasOne(a => a.Child)
.WithMany()
.HasForeignKey(p => p.ChildId)
.OnDelete(DeleteBehavior.Restrict);
It's working.
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"));
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);
There are a few questions out there on this topic, but my question is very specific to true self referencing. All the examples for other questions are circular references and that doesn't help me in this case.
Lets say I have this model:
public class User
{
[Key]
public int Id { get; set; }
...
public int CreatedByUserId { get; set; }
}
and this map:
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
this.HasRequired(a => a.CreatedByUser)
.WithMany()
.HasForeignKey(u => u.CreatedByUserId);
}
}
After migrations generates a database with this code I can manually add a User in SQL Management Studio with Id = 1, and CreatedByUserId = 1 so that tells me that self references like this can work.
However when using EF to create a user, I run into a "unable to determine a valid ordering for dependent operations" issue. There are a lot of questions on the matter that involve a new entity that refers another new entity that has a foreign key on the first entity, which is a circular reference. The solution in those cases is either save one of entities first or to have a nullable id on the circular entity foreign key. I can not do either of those because the first would be impossible and the second is a external constraint that I cannot have nullable ids.
So, seeing how I can achieve this by adding a entry manually I can assume it's a limitation of EF5. What are the work arounds?
You can still satisfy your interface and do the save first then set method by adding another property to act as a nullable backer for CreatedByUserId:
public class User : ICreatable
{
[Key]
public int Id { get; set; }
...
public int CreatedByUserId
{
get
{
if (!_CreatedByUserId.HasValue)
//throw new exception, something went wrong.
return _CreatedByUserId;
}
set
{
_CreatedByUserId = value;
}
}
int? _CreatedByUserId { get; set; }
}
You may want to rethink the possibility that a user can create him or herself...
However if you really want to do this then there is a solution. Your main problem is the fact that your column is an IDENTITY column which means that EF doesn't specify the Id, SQL server is giving each row an auto-incrementing Id. Any value you set as the Id is ignored. You don't necessarily know when executing the INSERT what the next Id is going to be so you can't create a reference to a row that doesn't exist yet.
Change your mapping code to something like the following:
this.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.HasRequired(x => x.CreatedByUser)
.WithMany();
You don't need to specify the foreign key if the name pattern matches (eg. CreatedByUser and CreatedByUserId).
Now when you insert a User you can specify the Id and the CreatedById. Although note that you must now always specify the Id to insert a new User. This is common practice if you are using GUIDs as Ids because you can just generate a new GUID without having to first query for the next "available" Id before creating a new object.
I have this class ( Simplified )
public class User {
public string Username { get; set; }
public virtual Section Section { get; set; }
}
public class Section {
public int DepartmentNumber { get; set; }
public virtual ICollection<User> DepartmentMembers { get; set; }
}
What i try to achive is to delete a section. All users related to this section shoulde get a null value. This dosent work now because REFERENCE constraint "FK_Users_Sections_Section".
What woulde be the correct way to do this? woulde i have to remove all users from this section before deleteing it? Is there a more elegant way to do this? Im using EntityFramework.
My fluent API for this field:
modelBuilder.Entity<User>()
.HasOptional(u => u.Section)
.WithMany(s => s.DepartmentMembers)
.Map(m =>
m.MapKey("Section")
)
.WillCascadeOnDelete(false);
The WillCascadeOnDelete i have tried it by setting to false, removing the line and also adding the line with no arguments.
I guess the solution is quit simple but i cant find any good explaination ( or mabye i dont understand the explainations i have been looking at. )
Although SQL Server has a SET NULL option for cascade deletes, which sets all foreign keys referencing a deleted record to null, Entity Framework does not use it.
You can either set this option on the constraint yourself in SQL Server, or you can let Entity Framework take care of it by updating loaded entities.
If you want EntityFramework to do it, you need to make sure the DepartmentMembers collection is loaded, so that all the User objects that will need to be updated are in the context.
Section s = context.Sections.Include(s => s.DepartmentMembers).First();
context.Delete(s);
context.SaveChanges();