DbContext's ChangeTracker problem - code-first

I have a codefirst EF-4.1 based program. The user gets a context and can modify some properties. When the user is done, I do a quick
ChangeTracker.Entries().Any(e => e.State != EntityState.Unchanged);
to determine if a SaveChanges() is required or not. If I do a 'SaveChanges()' call, the changes that have made are persisted to the database.
This works for some properties, and doesn't work for others. Specifically it seems to work with simple types (floats), and with collection hierarchies(ObservableCollections).
Am I doing something wrong?

Yes this is a problem. Some relations are not tracked by DbChangeTracker. There is difference between Independent association and Foreign key association. Changes to relation are tracked in case of:
One-to-one relation which is always Foreign key association in EFv4+
One-to-many relation with Foreign key association - you should set up foreign key property
Changes to relation are not tracked in case of:
One-to-many relation with Independent association
Many-to-many relation which is always Independent association
Not tracked for Independent association is not correct naming. These changes are tracked but DbChangeTracker does not expose access to these changes! You must convert DbContext to ObjectContext and use ObjectStateManager to get access to ObjectStateEntries representing independent associations.
In this case the easiest thing is simply call SaveChanges always. It will not execute any DB commands if no data need to be saved.

Related

Deleted entities show up as modified in Entity Framework Core's ChangeTracker

I'm trying to understand why Entity Framework Core 2's ChangeTracker sets the state of deleted entities to 'modified' if the deletion is done by removing the entity from a list in another entity.
Consider this example:
Class1
List<Class2> MyListOfClass2Objects
Class2
Both classes have their own DbSet in EF's DbContext. If I remove an object from the MyListOfClass2Objects list and then call DbContext.SaveChanges(), the state of the Class2 object in ChangeTracker is set to EntityState.Modified, and not EntityState.Deleted, which I would expect. If I delete the entity by calling Set<T>.Remove() the state in the ChangeTracker is EntityState.Deleted. In both cases however, the entity is removed from the database.
So, why does Entity Framework set different values in ChangeTracker depending on how you delete an entity even though it is really deleted in both cases?
Update: At the end (thanks to #Sebastian finding) it turns out to be a known issue with the current (at the time of writing) EF Core implementation, tracked originally by EF Core marks entities removed From child collections as modified #10093. The short explanation is:
because cascade deletes happens while SaveChanges is running which means in your code will not see this state as it looks at the state before SaveChanges has executed
and
Cascade delete happens as part of SaveChanges.
Fixup of non-deleted entities to no longer reference deleted entities happens after SaveChanges.
The currently associated action item is Allow delete fixup and cascade timing to be configured #10114, unfortunately in backlog, i.e. unknown if and when will be addressed.
Original:
Because when you remove an entity from the corresponding DbSet, you are telling EF that you want to delete it. But when you remove it from the parent entity collection, if the relationship is optinal (as it seems to be in your case), EF treats that as attempt to disassociate the child from the parent (same as setting child.Parent to null), hence it sets the FK and parent navgigation property to null and marks the entity as Modified. If you really want to delete it, you should use the first method.
All this is explained in the Removing relationships section of the documentation:
You can remove a relationship by setting a reference navigation to null, or removing the related entity from a collection navigation.
Removing a relationship can have side effects on the dependent entity, according to the cascade delete behavior configured in the relationship.
By default, for required relationships, a cascade delete behavior is configured and the child/dependent entity will be deleted from the database. For optional relationships, cascade delete is not configured by default, but the foreign key property will be set to null.

Entity Framework Core: how to 'wipe and replace' bridge table?

Imagine we have a many-to-many relationship (e.g. Product and Category where one product can be in many categories and vice versa). They are identified through a composite key (ProductId / CategoryId). How do we update this bridge table in Entity Framework Core when we want to persist the changes to the Product aggregate?
In Entity Framework 6, I'd simply wipe out all related data for that product in the bridge table and then re-populate them. This simplifies life as we don't need to manually synchronize changes (inserts, updates, deletes). Just wipe and replace.
How do we do this in Entity Framework Core? Here's what I tried:
context.ProductCategories.RemoveRange(product.Categories);
context.ProductCategories.AddRange(updatedProductCategories);
This results in an exception due to duplicate composite key:
InvalidOperationException: The instance of entity type 'ProductCategory' cannot be tracked because another instance of this
type with the same key is already being tracked. When adding new
entities, for most key types a unique temporary key value will be
created if no key is set (i.e. if the key property is assigned the
default value for its type). If you are explicitly setting key values
for new entities, ensure they do not collide with existing entities or
temporary values generated for other new entities. When attaching
existing entities, ensure that only one entity instance with a given
key value is attached to the context.
How do we remove a composite key entity from the context so that we can add another one?
The only way I got this to work so far is to synchronize all changes (deletes, updates, inserts), but it would be much simpler to just wipe and replace.

How to find out which property is used as a Foreign Key between two entities in code first approach

I'm using Entity Framework 5, code first approach. As there's no built in support for updating child entities in disconnected scenario, I'm building my own mechanism to do that. At some point I need to get the property of an entity with which it has a Foreign Key relationship with another (principal) entity. I've tried to get access to CSpace through
((IObjectContextAdapter)dbContext).ObjectContext.MetadataWorkspace.GetItems<MyEntity>(System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace)
but here I got a warning that said there's no implicit conversion between MyEntity and System.Data.Entity.Core.Metadata.Edm.GlobalItem.
I can't look for a property that has Foreign Key attribute because in most of my entities I use EF convention to get foreign keys automatically. So how one would go about finding which property is used for foreign key relationship.
Thanks to #octavioccl's post I was able to do what I want. So I was in the right path to look inside ObjectContext.

Deleting an Entity with related data and Cascading Deletes defined in SQLServer

I'm getting the following error when I SaveChanges after Removing an entity that has related entities containing data in the context as well. (The entity I'm deleting has the unique Primary Key). I have Cascading Delete configured at the SQL Server database level for the relation between the primary key table and the foreign key table.
"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 su...
The primary entity has its related data loaded explicitly prior to me removing the primary from the context. I assumed that EF and SQL Server would take care of the cascaded delete for me. If the related entity has no data the delete (of the primary entity) works fine. If there is data in the related entity, I get the error above.
Any suggestions?
The lesson is that EF has a learning curve. Keep exploring.
Anyway, I was "deleting" entities by setting the state to "Deleted" then calling SaveChanges. This seems to work fine if the entity has no related data. However, if you have an entity that has related "child" entities, you need to call Remove on the parent entity for the "delete" to cascade through the graph. Live and learn. I'm pretty sure this is the answer. I think for the time being I'm going to stop setting State for deletes and use Remove instead.

Entity Framework Model first: adding an association without creating foreign key properties?

I'm playing with the Entity Framework model designer, and I've got a question about creating entity associations:
In the "create association" dialog, when I create a 1:many association, it offers this checkbox:
"Add foreign key properties to the [entityname] entity"
I've been checking this box and I get results that are expected and make sense to me: Clicking the navigation property in the diagram highlights the related field in both entities that tie them together.
But, what would it mean not to check this box? I've tried this, and I then see no place in the entity to store a reference to the parent table's primary id. Am I correct that the navigation properties don't store any data in the database? If so, how could this work? Am I, perhaps, expected to manually map the navigation property to an Int32 field on the entity?
Associations represent relationship between entities. In the database (relational model) these relations are modeled by using foreign keys and - in the case of many-to-many - a join table. In the object model relations are typically modeled as references to the related object (in EF they are often referred to as Navigation Properties). The problem arises when you need to create or modify a relationship in the object model - you always need to have a reference of the related object you would like to set. In a pure object model this usually is not a problem but in case of ORM it means that if you don't have the related entity you need to send a query to the database to get the object to be able to set the reference to. However oftentimes - even if you don't have the related entity - you know the Id of the related entity. So, if the foreign key properties were exposed (and handled) in your object model you could create or modify a relationship without having to send additional queries to the database. This is what the checkbox is about. If you check it your entities will have (extraneous from object model perspective) properties mapped to foreign key columns in the database which you can use to manipulate relationships.