I am using the Northwind sample database. I have this code:
var db = new NorthwindEntities();
int id = 2; // Example
var delObject = (from o in db.Orders.Include("Order_Details")
where o.OrderID == id
select o).First();
db.Orders.DeleteObject(delObject);
db.SaveChanges();
I have an (1-to-many) association in Order - Order Details, with cascading deletes. (If I delete one Order, all Order_Details with the same OrderID will be deleted).
I have LazyLoading enabled.
If I delete the .Include("Order_Details") in the from clause, the cascade delete won't work.
Why does this happen? Isn't lazy initialization supposed to "include" the Order_Details for me, and eventually let me cascade delete?
The cascading deletes is defined in your EF model.
EF will therefore generate delete statements for data that it has loaded. EF will not go to the database to check what it should delete.
You can define cascading deletes (depending on your database) at the database level. In this case EF will delete the top node and the database will delete the related rows.
Do you have the cascading delete defined in both the database and the entity configuration? I've seen where having it defined in only one and not the other can cause this problem.
Cascade deletions in EF model deletes only those details which has been loaded in context. In case if you do use Include Order_Details are loaded during query along with Orders. If you enabled LazyLoading and not use Include then they are loaded on as needed basis, i.e. when you reference navigation properties.
Thus if you don't care about details and agree that they will be silently deleted with master record you have to define cascade deletion both in EF model and DB schema.
Related
I have a SQL Server database that has two tables with a 1:N relationship. In the relationship I have set the on delete, do nothing, because in general I want to control this in the logic business, I mean, in my repository.
So in my repository I get the rows that I need to delete from both tables, and in theory I get all the parents rows of the main table that are parents of the rows of the child table. So I would have no problems.
But I know that when I do saveChanges, EF not ensure the order of the deletings, so it is possible that try to delete a parent before deleting all the childs, so this is the problem.
So my quesetion is if there is any way to configure the dbContext, the relation of both entities to delete the rows without this problem?
My code is:
using(MyEntities myDbContext = new myEntities())
{
//Here I could configure myDbContext to perform a correct deleting?
//code to get the rows to delete
myDbContext.SaveChanges(); //here is the error.
}
Thank you.
The entity should contain a navigation property of the entity/table that it's related to. Assuming table a was the parent table, you would then delete all the record(s) in the navigation property that points to the child table/table b. Then you would delete the record(s) from table a. Then call myDbContext.SaveChanges() and EF would take care of all the cascade on delete stuff for you.
I am using EF5 and when the the relationship is 1:N, if I want to load related entities I do the following:
With T-SQL I load from database the main entities with a T-SQL like that:
select *
from MainEntities
where ...
with T-SQL I load the related entities
select *
from RelatedEntities
where IDMainEntity IN (---)
At this point EF populate the property navigation of the main entities with the related entities. Also, in the local property of the type of each entity in the dbContext I have all the entities of each type.
However, if i do the same with a N:N relationship, I don't have the entity of the middle table of the relation, and when I execute the queries I have in the local of the dbContext the entities of each type, but the property navigation is not populated.
I would like to know why and if it exists some alternative.
I use this way because I want to use T-SQL for create dynamic queries. If I use eager loading I don't have the same flexibility to dynamic queries than when I use TSQL, and it is less efficient. If I use explicit loading I to do N additional queries, one of each record in the results of the main entity With my way, I only one additional query, because I get all the related entities at once. If I use lazy loading I have the same problem, N additional queries.
Why EF does not populate the related properties when the relation is N:N?
Thanks.
The feature you are talking about is called Relationship Span or Relationship Fixup and indeed - as you have noticed - it does not work for many-to-many relationships. It only works if at least one end of the association has multiplicity 1 (or 0..1), i.e. it works for one-to-many or one-to-one relationships.
Relationship Span relies on an entity having a foreign key. It doesn't matter if it has an explicit foreign key property (foreign key association) or only a foreign key column in the corresponding database table without a property in the model (independent association). In both cases the FK value will be loaded into the context when the entity gets loaded. Based on this foreign key value EF is able to figure out if a related entity that has the same primary key value as this FK value is attached to the context and if yes, it can "fixup the relationship", i.e. it can populate the navigation properties correctly.
Now, in a many-to-many relationship both related entities don't have a foreign key. The foreign keys are stored in the link table for this relationship and - as you know - the link table does not have a corresponding model entity. As a result the foreign keys will never be loaded and therefore the context is unable to determine which attached entities are related and cannot fixup the many-to-many relationship and populate the navigation collections.
The only LINQ queries where EF will support you to build the correct object graph with populated navigation collections in a many-to-many relationship are eager loading...
var user = context.Users.Include(u => u.Roles).First();
...or lazy loading...
var user = context.Users.First();
var rolesCount = user.Roles.Count();
// Calling Count() or any other method on the Roles collection will fill
// user.Roles via lazy loading (if lazy loading is enabled of course)
...or explicit loading with direct assignment of the result to the navigation collection:
var user = context.Users.First();
user.Roles = context.Entry(user).Collection(u => u.Roles).Query().ToList();
All other ways to load the related entities - like projections, direct SQL statements or even explicit loading without assignment to the navigation collection, i.e. using .Load() instead of .Query().ToList() in the last code snippet above - won't fixup the relationship and will leave the navigation collections empty.
If you intend to perform mainly SQL queries rather than LINQ queries the only option I can see is that you write your own relationship management. You would have to query the link table in addition to the tables for the two related entities. You'll probably need a helper type (that is not an entity) and collection that holds the two FK column values of the link table and probably a helper routine that fills the navigation collections by inspecting the primary key values of the entities you find as attached in the DbSet<T>.Local collections and the FK values in the helper collection.
To add on #Slauma answer:
I faced the same problem recently, getting frustrated that the navigation property is not being set after calling Query().Where().Load(), although I can see that the objects are loaded into the DbContext.
I needed the collection to be part of my main object and use it as you would any other navigation property and not just manage a separate collection, so I did this:
project.Labels = this.Context
.Entry (project)
.Collection (p => p.Labels)
.Query ()
.Where (l => l.CreateUserName == this.UserId)
.ToList();
The problem with this is that EF thinks I added new relationships, which I can't blame it, but it is not what I wanted. As a result, when trying to save the Project object I got an exception when EF tried to insert the relationship into the link table because a row with the same key (projectId + labelId) already exists.
So, the final was to reset the state of the relationships between the project and the labels:
foreach (Label l in project.Labels)
{
((System.Data.Entity.Infrastructure.IObjectContextAdapter)this.Context.AsDbContext ()).ObjectContext.ObjectStateManager.ChangeRelationshipState<Project> (project, l, p => p.Labels, EntityState.Unchanged);
}
After that I was able to use the Labels property just like any other navigation property, not caring that behind the scenes it's a many-to-many relationship.
I have 2 entites:
Im my DB they look like:
Vehicles(Id, VehicleNumber, IsDeleted, WorkerId)
Workers(Id, Name, Address)
And in my edmx:
VehicleId: Id, VehicleNumber, IsDeleted, WorkerId, Worker
Workers: Id, Name, Address, VehiclesList
As you can see, Vehicles table contains soft deleted rows. Now when I get Worker with id=2, I got all his vehicles, including the one I soft deleted. How can I retrieve only the undeleted vehicles?
Badly. EF has very limited support for soft deletes. Actually the only possibility is using conditional mapping where you explicitly hardcode (it cannot be changed at runtime) to your mapping condition saying that you don't want to load entities having IsDeleted = 0. Check mapping details:
But it has very bad consequences:
IsDeleted column cannot be mapped - it already defines mapping internally
Your model can never be used to load soft deleted entities even if you want
The first problem can be solved by mapping stored procedure to delete operation for the Vehicle entity and the second problem can be solved by separate model for auditing and retrieving deleted entities.
Also conditional mapping is not supported by code first - it requires EDMX file.
is lazyloading enabled? then try to limit result set with a where:
worker.VehiclesList.Where(x=>!x.IsDeleted)
also you can put a condition to a vehicles table mapping in model desiner isdeleted = false. Soft deleted vehicles will not be retrived at all
On a previous project we had EF4 performing cascading deletes (Delete a parent record and the child records are deleted, too). On this project (different company), EF4 is not performing cascading deletes. What do I need to do to make EF4 perform a cascading delete?
Using just EF4's cascading delete is not enough; you should set up cascading deletes on your database as well, in case not all children are loaded into the object context. That being said, the cascade delete properties are set on the assocation. Go to the model browser, select an assocation and view properties.
I am on the same boat. I only have cascade on delete in EDM/EF4 and not (yet) in the database. Try this...
In the relationship, set the OnDelete of parent's end (1 multiplicity) to Cascade . Then in your code load all children before saving the changes (deletion).
var parent = context.Parents.SingleOrDefault(p => p.Id == parentId);
parent.Children.Load();
if (parent != null)
{
context.Parent.DeleteObject(parent);
context.SaveChanges();
}
You have two ways of cascade deleting implementation:
Set up it on the database layer (triggers or relationships)
Try to use extension methods. You can define delete logic for current table and call methods for related table updating.
I am using the Ado.Net Entity Framework with ASP.NET MVC.
In my MSSQL 2008 Database I have for example the following simplified tables and relations:
(Song) 1--* (Version) 1 -- 1 (VersionInfo)
Is it possible to automatically have the linked Versions and their VersionInfo's deleted when I delete a Song?
Currently I am using something like the following code, which is a lot of manual work, since some tables have up to 8 relations and those relations have subrelations too sometimes:
db = new Database() //Entities
Song song = db.Song.First();
Song.Version.Load();
foreach(Version version in Song.Version.ToList())
{
//Remove Song-Version Reference.
song.Version.Remove(version);
//Get The VersionInfo so we can delete it after we deleted the Version object.
version.VersionInfoReference.Load();
VersionInfo versionInfo = version.VersionInfo;
//Now we can delete the Version Object.
db.DeleteObject(version);
//Now we can also delete the versionInfo, since the reference is gone.
db.DeleteObject(versionInfo);
}
db.DeleteObject(song);
There must be an easier way to get cascading deletions. I already tried setting the relationship setting in MSSQL to Cascade when Deleting, but it didn't do a thing... Did I miss something there?
Anyway, how do other people solve this problem?
You should not be doing this in the Entity Framework. All popular relational databases support ON CASCADE DELETE on foreign keys which is a lot more efficient as well. I suggest you just go with that.
It appears in your case you may have to cascade deletes in song to version and deletes in version to version info. Just load up the table designer in SQL Manager and you should see the relevant options under relationships.
I tried it with a simpler database, 2 tables, and found out that cascade is only from the 1 side of a 1-many.
table A
id - int
b_id - int
table B
id - int
Relationship is set between A.b_id and B.id.
Delete rule is cascade.
When I delete A, B is not deleted.
When I delete B, A is deleted.
Problem is, I want to have B deleted when I delete A. I guess that is only possible manually.
If you set it correctly in the database then it should definitely cascade the deletes. I think they improved this in the next version of the Entity Framework but I am not certain. I just remember seeing cascade somewhere. I'd recommend you have a look in the database again.
For instance, are there any other relations that also needs to be cascaded?
in the RTM of EF, select a relationship in the designer, press F4 to see properties. you'll see two OnDelete properties (one for each side of the relationship) set to "none". you can set either side to "cascade" (I think you have to set it on the parent)