Entity Framework Add or Update in one round trip - entity-framework

Using Entity Framework Core, how can I save either one or many entities to the store such that those which already exist (i.e. match an existing primary key) are updated and new ones are inserted, in a single round trip?
The entities I'm saving are not loaded from the store in the first place, but rather imported from somewhere else.

Related

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.

Entity Framework 6 AsNoTracking<TEntity>(), but for a single entity

I'm working on a unit of work and repository set-up for Entity Framework, where I want to make sure only changed entities, that have been through an Update method on the repository, are actually saved to the database.
The IQueryable extension, AsNoTracking, makes sure that all entities fetched from the IDbSet are not being tracked, but if I want to read a single entity, by it's primary key (using Find()), there seems to be no way to make sure the entity isn't tracked?
I don't want to Detach/Attach, as that ruins the options to, lazily, fetch complex properties.
What am I missing?
Nothing.
Find has no AsNoTracking. That's because Find first tries to find the requested entity in the (tracked!) context cache (the state manager). It goes to the database if it's not there, so for a subsequent second time Find it doesn't have to do this roundtrip again.
So, Find is all about tracked entities.
Use Single(OrDefault) (+ AsNoTracking) if you want to get one entity without tracking.

EF 4.1 Commit changes stored in a dictionary

I am using EF 4.1 and in a certain point in my application, I read all the data satisfying a condition from an entity by performing:
context.Entity.Where(<condition>)
then, I iterate over them through a loop, and in specific situations I store some of them into a Dictionary (key value is the Id of the entity, and value is the entire object).
Later, in another point of my app, I read all the objects stored in the dictionary and I update them.
After updating them, I perform SaveChanges on the context, (the context is the same that was used when reading the entire entity and when items where stored in the dictionary.
So at this point, I would like to know if when I perform the savechanges, the data is sent to the database and udpated correctly since I am not sure as the objects modified come from the dictionary and I do not know if EF is so intelligent to know it should update database.
The dictionary as well as the EF context hold references to the objects. As long as the context isn't disposed, it will be able to track the changes.
In other words, putting your entities in a Dictionary (or List or any other means of collection) has no influence on the entities and their context.

Add or update in Entity Framework, complex deserialized objects

We're creating a WebAPI using Entity Framework in MVC 4. Our client wants to send complex objects containing related objects - both new and updated. The root object maybe new or existing one too. The client generates primary keys - we're using Guids for that. So on server we really can't tell that we got an existing object update or a new one. What would be the best way to handle this situation? We need some sort of add or update functionality and it's not yet clear to us how to proceed with Entity Framework for this.
EF doesn't have any build in support for discovering changes in detached object graph. You either have to include some field into every object describing if the object is new, not modified, updated or deleted (you will also need similar behavior to track changes in many-to-many relationships). If you don't use such field you have no other way than querying database and comparing current DB state with data received from client to find what has changed.

iPhone and Core Data: how to retain user-entered data between updates?

Consider an iPhone application that is a catalogue of animals. The application should allow the user to add custom information for each animal -- let's say a rating (on a scale of 1 to 5), as well as some notes they can enter in about the animal. However, the user won't be able to modify the animal data itself. Assume that when the application gets updated, it should be easy for the (static) catalogue part to change, but we'd like the (dynamic) custom user information part to be retained between updates, so the user doesn't lose any of their custom information.
We'd probably want to use Core Data to build this app. Let's also say that we have a previous process already in place to read in animal data to pre-populate the backing (SQLite) store that Core Data uses. We can embed this database file into the application bundle itself, since it doesn't get modified. When a user downloads an update to the application, the new version will include the latest (static) animal catalogue database, so we don't ever have to worry about it being out of date.
But, now the tricky part: how do we store the (dynamic) user custom data in a sound manner?
My first thought is that the (dynamic) database should be stored in the Documents directory for the app, so application updates don't clobber the existing data. Am I correct?
My second thought is that since the (dynamic) user custom data database is not in the same store as the (static) animal catalogue, we can't naively make a relationship between the Rating and the Notes entities (in one database) and the Animal entity (in the other database). In this case, I would imagine one solution would be to have an "animalName" string property in the Rating/Notes entity, and match it up at runtime. Is this the best way to do it, or is there a way to "sync" two different databases in Core Data?
Here's basically how I ended up solving this.
While Amorya's and MHarrison's answers were valid, they had one assumption: that once created, not only the tables but each row in each table would always be the same.
The problem is that my process to pre-populate the "Animals" database, using existing data (that is updated periodically), creates a new database file each time. In other words, I can't rely on creating a relationship between the (static) Animal entity and a (dynamic) Rating entity in Core Data, since that entity may not exist the next time I regenerate the application. Why not? Because I have no control how Core Data is storing that relationship behind the scenes. Since it's an SQLite backing store, it's likely that it's using a table with foreign key relations. But when you regenerate the database, you can't assume anything about what values each row gets for a key. The primary key for Lion may be different the second time around, if I've added a Lemur to the list.
The only way to avoid this problem would require pre-populating the database only once, and then manually updating rows each time there's an update. However, that kind of process isn't really possible in my case.
So, what's the solution? Well, since I can't rely on the foreign key relations that Core Data makes, I have to make up my own. What I do is introduce an intermediate step in my database generation process: instead of taking my raw data (which happens to be UTF-8 text but is actually MS Word files) and creating the SQLite database with Core Data directly, I introduce an intermediary step: I convert the .txt to .xml. Why XML? Well, not because it's a silver bullet, but simply because it's a data format I can parse very easily. So what does this XML file have different? A hash value that I generate for each Animal, using MD5, that I'll assume is unique. What is the hash value for? Well, now I can create two databases: one for the "static" Animal data (for which I have a process already), and one for the "dynamic" Ratings database, which the iPhone app creates and which lives in the application's Documents directory. For each Rating, I create a pseudo-relationship with the Animal by saving the Animal entity's hash value. So every time the user brings up an Animal detail view on the iPhone, I query the "dynamic" database to find if a Rating entity exists that matches the Animal.md5Hash value.
Since I'm saving this intermediate XML data file, the next time there's an update, I can diff it against the last XML file I used to see what's changed. Now, if the name of an animal was changed -- let's say a typo was corrected -- I revert the hash value for that Animal in situ. This means that even if an Animal name is changed, I'll still be able to find a matching Rating, if it exists, in the "dynamic" database.
This solution has another nice side effect: I don't need to handle any migration issues. The "static" Animal database that ships with the app can stay embedded as an app resource. It can change all it wants. The "dynamic" Ratings database may need migration at some point, if I modify its data model to add more entities, but in effect the two data models stay totally independent.
The way I'm doing this is: ship a database of the static stuff as part of your app bundle. On app launch, check if there is a database file in Documents. If not, copy the one from the app bundle to Documents. Then open the database from Documents: this is the only one you read from and edit.
When an upgrade has happened, the new static content will need to be merged with the user's editable database. Each static item (Animal, in your case) has a field called factoryID, which is a unique identifier. On the first launch after an update, load the database from the app bundle, and iterate through each Animal. For each one, find the appropriate record in the working database, and update any fields as necessary.
There may be a quicker solution, but since the upgrade process doesn't happen too often then the time taken shouldn't be too problematic.
Storing your SQLite database in the Documents directory (NSDocumentDirectory) is certainly the way to go.
In general, you should avoid application changes that modify or delete SQL tables as much as possible (adding is ok). However, when you absolutely have to make a change in an update, something like what Amorya said would work - open up the old DB, import whatever you need into the new DB, and delete the old one.
Since it sounds like you want a static database with an "Animal" table that can't be modified, then simply replacing this table with upgrades shouldn't be an issue - as long as the ID of the entries doesn't change. The way you should store user data about animals is to create a relation with a foreign key to an animal ID for each entry the user creates. This is what you would need to migrate when an upgrade changes it.