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

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.

Related

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.

What is the Unchanged state entity key value

After read MSDN Attaching and detaching object.
Objects are attached to the object context in an Unchanged state . if we called the Attach method. And In the Unchanged state, the Entity Framework treats the entity key values as final.
What is the "final" means?
An object in the "Unchanged" state means that the context is unaware of any changes to the entity compared to the state in the database.
You could artificially get to this state in error (for example) by doing this:
Loading an entity from the database using one context instance.
Detach it from that first context instance.
Change the properties.
Add the entity to a new context instance.
The new context instance will be unaware of changes to the entity with respect to data in the database, and will treat all property values as the final state of the object.
Specifically with regard to the values of entity keys, the assumption is that the keys represent the correct value as currently in the database, so the keys will not be modified / fixed up.
The Key value is the unique identifier for the entity and once it is attached it cannot be altered. All other properties of the entity can be changed after it has been attached.

DbContext's ChangeTracker problem

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.