How do I remove related objects in linq to entity framework - c#

I have 2 tables:
orders
orderItems
connected to one another threw a relationship(FK orderId in the orderItems table). is there a difference between:
order.orderitems.remove(...)
to just setting the orderId value to null when trying to remove the related entity?

Setting the FK property to null and removing the entity from the navugation property both do the same thing. That is, they both sever the relationship between the entities but do not cause either entity to be deleted. It's the same as setting the FK value in the database to NULL.
To delete the entity you must call Remove on the DbSet for the entity. For example
context.OrderItems.Remove(orderitem);
context.SaveChanges();

Related

How do you delete a single record from EF, when you're using generics?

I'm having a problem in which the deletion of a record in a table works, but only so long as the table has no child foreign key relationships. Now, when we have a parent table and want to delete the record using EF, it fails with an error of, "The relationship could not be changed because one or more of the foreign-key properties is non-nullable." I've deleted cascading delete on the child table referencing the parent table. I've tested it within SSMS. It works fine. But from within C# it fails. We have a view model where we've defined a generic DeleteRow method. This works great, so long as the row being deleted has no child tables. It fails, if the table is a parent table. I tried looking here on Stackoverflow for an answer and came across this question, now closed: stackoverflow.com/questions/17723276/delete-a-single-record-from-entity-framework. The answer to this question used a discrete table in EF. We're using generics. Here's our code:
public void DeleteRow(T row)
{
if (App.MainDataContext != null && row != null)
{
App.MainDataContext.Entry<T>(row).State = EntityState.Deleted;
App.MainDataContext.SaveChanges();
}
}
I don't believe setting the State property to the enumeration EntityState.Deleted is working, as that's only setting it for the record in the parent table, not for any of the related records in the child tables. (MainDataContext is entity from a .EDMX file.) How do we delete a single record from any table using EF 6 when we're using generics to pass in the table type?
The problem is that when you set the state to deleted, the related entity is also loaded (tracked by the context's change tracker). When you delete your row, EF tries to set the parent navigation property to null, but it is a not nullable relationship.
To solve this you have to set a cascading delete in the relationship in you model (EDMX), not only in the database.
It should work also, if by the instance of your context the related entity is not tracked. In this case on SQL-Server side the cascading delete should work.

How to manually load related entities in a N:N relationship?

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.

navigation property to soft-deleted entity

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

Define an association with a filter?

Is there a way to create an association in entity framework that always applies a "where" filter?
Here's an example:
Suppose that I have a database that uses "soft" deletes -- so there is a "deleted" column in every table.
So if I have a Customer entity that is related to Address (1:0..n), I would like for the Addresses navigation property to always be filtered on the deleted flag, so that if I go Customer.Addresses, the collection will only return Addresses where deleted == 0.
Anyone know of some way to do that?
No, there isn't.
Same reason you can't have a conditional foreign key.
A workaround could be to use TPH on the Address table:
Create abstract entity called Address
Create derived entities called AddressDeleted and AddressNotDeleted
Set the discriminator mapping (AddressDeleted maps to Address when Deleted = 0)
Create a navigational property between Customer and AddressDeleted.
So when you do ctx.Customer.Include("AddressDeleted").Single() it will only return the addresses that are flagged as deleted.
Or you could defined an association to Address, and use ctx.Customer.Addresses.OfType<AddressDeleted>().
Does that solve your problem?

Entityframework 4: Problem with ForeignKey when removing a child entity from parents collection

New to the EF, I have an issue.
OK, so, I have an Entity with a related Table (one-to-many relationship). The entity holds a collection of child objects from the related table. I want to remove an object from the related collection but not from the child table.
However, when I call <entity>.myRelatedChildTable.Remove( childEntity ) and then call the _context.SaveChanges( ), I get an exception about ForeignKey Constraints. Now, if I call the _context.DeleteObject() and then _context.SaveChanges() we have no problem. But, now we have no child entity as well--it is deleted from the db.
here is the text of the Exception:
The operation failed: The relationship could not be changed because one
or more of the foreign-key properties is non-nullable. When a change is
made to a relationship, the related foreign-key property is set to a null
value. If the foreign-key does not support null values, a new relationship
must be defined, the foreign-key property must be assigned another non-null
value, or the unrelated object must be deleted.
anyone have an idea/suggestion how I can fix this?
It sounds like you're basically trying to decouple EF from the database. Unfortunately save changes pushes the changes back to the database which obviously you don't want. You can just not call SaveChanges and continue to work with the collection. EF will account for your deletion and not return that information. At the end you can discard your changes and move on.
If you do actually want to delete the item without deleting the child you can't in the current schema. You would have to allow nulls in the DB.

Categories

Resources