Deleting many-to-many relationship in Entity Framework throws error - c#

I've got the below model.
I am trying to delete a given productType associated with an ActivityType; But when I try to instigate a delete via my generic methods, its not only deleting the relationship but its trying to delete the ProductType as well!! with a primary key of Zero in the where clause causing the erorr
"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
code to delete below;
public void DeleteEntityAndSaveChanges<T, T1>(T entity, List<T1> subEntities)
where T : class
where T1 : class
{
Set<T>().Attach(entity);
DeleteEntitiesAndSaveChanges(subEntities);
}
public void DeleteEntitiesAndSaveChanges<T>(List<T> entities) where T : class
{
foreach (var entity in entities)
{
Set<T>().Attach(entity);
Set<T>().Remove(entity);
}
SaveChanges();
}
Usage:
DbContext.DeleteEntityAndSaveChanges(request.ActivityType, request.ActivityType.ProductTypes);
This is the sql thats being generated:
exec sp_executesql N'delete [dbo].[ProductTypeActivityTypes]
where (([ProductType_ProductTypeId] = #0) and ([ActivityType_EntityObjectId] = #1))',N'#0 bigint,#1 bigint',#0=1,#1=20
This is the offending SQL that dont want sent out, but is being generated by EF:
exec sp_executesql N'delete [dbo].[ProductTypes]
where (([ProductTypeId] = #0) and [TimeStamp] is null)',N'#0 bigint',#0=1
any ideas as to how I can get it to only delete the relationship?
Cheers.

When you call Remove on the DbSet, this means delete that entity from the database when SaveChanges is called. As I understand it, what you really want to do is remove the association between the two entities. In general you do this by removing one entity from the collection navigation property of the other entity. For example:
product.Activities.Remove(activity);
But there is a wrinkle here. In your sample code you are calling Attach to attach both types of entities to the context. Calling Attach will not setup or restore any relationships between the entities. This is because, for many-to-many relationships, there is no FK in the entity to provide information about how it is related to other entities. The FKs are handled by the join table, which is not exposed in an EF many-to-many mapping.
There are a couple of approaches to dealing with this. First, if possible and sensible for your architecture, you can let the EF context track the entities from the time they are queried until the time you call SaveChanges. This way EF will keep track of the relationships for you, including keeping track of deletes.
Second, if the entities need to be attached to a new context, then you will need to keep track of the relationships such that those can be restored as well. There are a few ways to restore the relationships. One way is to build the graph of related entities before calling Attach. EF will then traverse and attach the entire graph, including relationships, when Attach is called. For example:
// Restore the graph
product.Activities.Add(activity1);
product.Activities.Add(activity2);
context.Products.Attach(product);
// Delete the relationship
product.Activities.Remove(activity1);
context.SaveChanges();
(I'm not using the generic methods you have just to make the code a bit clearer. It should work the same way with generics.)

Related

Entity not returned from query

I have this problem where I have 2 entities connected by foreign key.
AEntity: id, idOfEntityB (foreign key, constraint), fields...
BEntity: id, fields...
I save both of them to the database with SaveChanges(), later when I try to get AEntity's idOfEntityB, I succeed but when I try to get BEntity according to the id I got from AEntity, I get nothing:
context.AEntities.Add(new AEntity {
BEntity = new BEntity { ... }
});
context.SaveChanges();
.
.
.
var id1 = context.AEntities.Select(x => x.idOfEntityB);
var bEntities = context.BEntities.Where(x => id1.Contains(x.id));
bEntities has nothing in it. but the fact I was able to have values in id1 is even more confusing since they have foreign key relations (with constraint) and furthermore, id could not be created if it was not saved to the DB.
Later, when I look in the DB I see both entities as should be.
It happens sometimes and I cant reproduce the problem, I cant give more then this as an example since there's a lot of code, I believe it has something to do with caching, and therefore would like to ask if something like that is possible or not and how.
is there a way entities are saved to the DB while the context (a different one used from the context that saved) does not hold all of them in completion?
This is likely the issue you are encountering if you are relying on seeing changes between state changes between different DbContext instances. When a DbContext has loaded entities, then another DbContext instance makes changes to those records or the records change behind the scenes in the database, that original DbContext will not refresh the entities from the database.
EF does support the ability to reload entities from the database, but when dealing with child collections it gets a bit more complicated to perform a full refresh. You effectively need to tell the DbContext to forget all of the child collections, stop tracking the parent, clear the parent's child collection, then re-attach and reload the child collection. I recently covered this in the answer for this question: Replacing a entity collection in Entity Framework Core causes DbContext to fetch the new values when not saved to db. How to reload the collection?
Ultimately a DbContext lifespan should be kept as short as possible.

The ChangeRelationshipState method is not supported for relationships that are defined by using foreign-key values

In EF 6, to remove entities from a collection without actually fetching the collection, I am using:
manager.ChangeRelationshipState(entity, wrappedIdOfRelatedEntity, e => e.Collection, EntityState.Deleted);
It works for M:N relationships. But in case of 1:N, it throws the following error:
The ChangeRelationshipState method is not supported for relationships that are defined by using foreign-key values.
Just to clarify, I do not want to fetch the collection, since it may be really huge, moreover I do not want to fetch the entities that should be removed.
I have the key (id) of the entity that 'contains' the collection and keys (ids) of the entities that should be removed.
Any hints, how to do it correctly?
NOTICE: Adding is possible by just adding wrapped entity keys to a collection. But removing is causing the problem.
Do you use row versioning/timestamp on your entities? If you do, EF needs to version because it's doing to send an update statement containing both the id and version of the entity to delete.
If you're not using row versioning/timestamp on your entities, you can do the following using the DbContext API but you need to use proxies and change detection.
Recreate the entities you want to remove from the collection by using DbSet.Create
Populate the recreated entities with the id of the entities you want to remove
Attach the entities using DbSet.Attach
Add the entities to the collection and then call AcceptAllChanges on the ObjectContext to reset the state of the entites
Remove the entities from the collection
Call save changes and it should issue update statements to remove the entities from the collection.
If you're using row versioning/timestamp on your entities, you can use the steps above, but in step 2, you need to set both the version and the id.

Entity Framework, database wide Guid table, insert order

We have a synchronization framework that uses a global SyncEntity table to keep track of which entities have been updated at what time, meaning we have a global table with a structure something like this:
<dbo.SyncEntity>
ID int
EntityType int
EntityGuid uniqueidentifier
The EntityType is an enum that corresponds to the specific entity so that we know in which table to look for this entity.
All our tables have an ID (PK) and a GUID.
I have created a Foreign Key constraint from the different Entity tables and to the EntityGuid in the SyncEntity table.
This works perfect for existing data however when we use EntityFramework to insert new data it doesnt insert the data in the "correct" order resulting in an error because the SyncEntity with the required EntityGuid is not yet inserted.
I guess we could add a property SyncEntity on all of our entities however i really dont want to pollute our domain model with that property.
So my question, is there anyway to ensure that specific Entity types are inserted as the first entities?
Or is there anyway to map the relation from Guid (on the specific Entity) to EntityGuid (on SyncEntity) without a navigation property.
I see two ways you could potentially alleviate this issue.
First, you could use domain events to recognize that a new entity has been created and raise an event, passing in the entity itself as a parameter and then allow that event to create a new SyncEntity insert it and then save it. This will populate your ID and then you can assign it to the new entity creating the relationship.
Second, you could override the SaveChanges method of the DbContext to look at added entities and then create a new record for each of them, then assign your new SyncEntity Ids to the entity.
WHy would you bother EF with something like that?
Have the SyncEntity entry created by a trigger on the tables. Finished. EF does not have to bother with it.
And it is save for direct SQL usage, too.
EF is a good tool - though only a very very mediocre ORM. But it is not a solution for everything. DB internal logic, like a logging table, should be handled in the database.

Why does LoadProperty not load the related entities for 1:N relationships? (CRM2011, early binding entity classes)

Suppose I have a custom entity new_someentity which has 2 other related entities: an "owner" entity which I'll call new_ownerentity (this is a N:1 relationship) and a "child" entity which I'll call new_childentity (1:N relationship).
I'm attempting to populate the related entities by calling LoadProperty:
new_someentity en = context.new_someentitySet.First();
context.LoadProperty(en, "new_someentity_new_ownerentity");
context.LoadProperty(en, "new_someentity_new_childentity");
Afterward, en.new_someentity_new_ownerentity is populated as I expect it to be with a reference to the owner entity, but en.new_someentity_new_childentity is simply still null. No errors are produced.
What's the deal?
On a side note, is there really not a concise way to load a related entity for an IEnumerable of entities without needing to use LoadProperty on each entity individually? This seems like a pretty classic case of an N+1 queries issue.
Just because you think it should return an empty list when the object doesn't have any child entities doesn't mean that's the way LoadProperty works.
So for anyone else who comes upon this:
LoadProperty will leave the property null when there aren't any related records for that record, even on 1:N relationships.

ObjectStateManager error while adding second data (Entity Framework)

When I adding more than one consecutive data an error occurred in SaveChanges() method.
EXCEPTION
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.
My baseservice
public void Delete(T entity)
{
ObjectSet.DeleteObject(entity);
Context.SaveChanges();
}
public void Add(T entity)
{
ObjectSet.AddObject(entity);
Context.SaveChanges();
}
public void Attach(T entity)
{
ObjectSet.Attach(entity);
Context.SaveChanges();
}
public void Update(Expression<Func<T, bool>> where, T entity)
{
var ent = First(where);
ent = entity;
Context.SaveChanges();
}
I had this problem and found out that I was doing the following operations, making EntityFramework to become not in sync with the data in the database:
1) Make a query on the rows of a table through Entity Framework's Context. Doing so, EntityFramework context preserves a copy of those objects in its Local view.
2) Truncate the table through an SQL Query (so the Entity Framework context has not idea this happened. The entities are still in its local view even if they were truncated in the database).
Since the primary key of the table is auto incrementing (IDENTITY (1,1)), the truncation call makes the primary key counter of the table to reset to 1.
3) Add rows to the table through Entity Framework, and then call SaveChanges(). Because of the table truncation, the primary key of the new row is 1. After creating the row, EntityFramework queries for the database for the row values, creates a new Entity, populates the values in the Entity and adds the Entity to its local view.
Because the context already had another object with primary key = 1 stored in its local view (from Step 1), an exception is thrown as it tries to add a second Entity with the same primary key to the local view.
To avoid this situation, Entity Framework must be remain in sync with the database content before making new operations.
In my case, I had to fix this by calling:
Context.MyTableEntities.Local.Clear();
Context.SaveChanges();
So the Entities were deleted first, and the context was told about it. Then I truncated the table with an SQL Query to reset the auto increment counter.
Since the objects were deleted from the local view first, and the table truncation done afterwards (so it resets the auto increment counter), primary key conflicts were avoided.
Hope that helps.
Are you certain you're adding different entities to EF? The inner exception states that AcceptChanges() is failing because the current entity that you're trying to add shares a key with an entity that's already being tracked.
For more on AcceptChanges(), take a look at: http://msdn.microsoft.com/en-us/library/system.data.objects.objectstateentry.acceptchanges.aspx

Categories