Can Entity Framework Auto-Update Property Navigation By Id - c#

In Entity Framework, when I've mapped my tables to the corresponding entities through the designer and get to actually using them, I'll find that an entity - Thing, who has a relationship (many to one, or one to one) with another object, say, Bob, for example, would produce the following three properties on Thing:
Bob
BobId
BobReference
And were I to set BobId, and save my entity, the next time I fetch this Thing, I'll be able to navigate the Bob property without trouble. I'm curious, however, if it is possible to configure EF to allow me to navigate the property without having to immediately save.

You can do something like this: (EF 4.1)
//Has to exists a record on Bob table with Id = 1
var thing = new Thing() { BobId = 1 };
var context = new YouContext();
context.Entry(thing ).State = EntityState.Unchanged;
context.Entry(thing ).Reference(x => x.Bob).Load();
and then thing.Bob is != null

Related

Entity Framework Core1.1 - Bulk Insert or Update - InvalidOperationException

I'm running into a problem with inserting OR updating roughly 950 entities.
var coins = JsonConvert.DeserializeObject<List<Currency>>(json);
var sw = new Stopwatch();
sw.Start();
using (var ctx = CryptoContext.Get)
{
var existingCoins = ctx.Coins.ToList();
foreach (var coin in coins)
{
var existing = existingCoins.FirstOrDefault(c => c.CMC_Id == coin.CMC_Id);
if (existing != null)
{
ctx.Entry<Currency>(coin).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
} else
{
ctx.Entry<Currency>(coin).State = Microsoft.EntityFrameworkCore.EntityState.Added;
}
}
ctx.SaveChanges();
var el = sw.ElapsedMilliseconds;
}
The code runs in the background of my netcoreapp1.1, with SQLite, and retrieves a list of currencies. This is done every 5 minutes with FluentScheduler. Because they're not entirely large objects I do all comparisons in memory, and try to add or update each one. My entity has a database-given ID of Id, and the API I'm retrieving from guarantees that CMC_Id is unique.
The initial insertion works fine. I get an error on the second "Update". I believe what's happening is that I'm tracking multiple entities as modified that each have an Id of 0
I was trying to follow this: https://msdn.microsoft.com/en-us/library/jj592676(v=vs.113).aspx
And the error I get is: "The instance of entity type 'Currency' 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."
I am unsure how to proceed with updating each row.
Issue here multiple entities with same key are asked to be tracked.
When you set EntityEntry.State to something then EF Core will start tracking the entity in the specific state. Since in your code, you are querying the database to find out existing entity, EF Core will start tracking the entity with given key therefore it throws above exception while setting the EntityEntry.State because there is already entity with same key being tracked.
More precisely you are trying to AddOrUpdate. There are multiple ways to achieve the behavior. Which one is the best depends on if you are adding one entity without relation or a complex graph.
The simplest method would be to just check existence instead of tracking the entity from database. Options for that would be to use AsNoTracking in your query so that EF does not start tracking it. Even more optimized way would be to just get count from database. If you are querying on PK property then count will be either 0 (non-existent) or 1 (existing entity). If it does not exist then you call Add otherwise Update.
var updatedBlog = new Blog { Id = 1, Title = "Updated" };
var exist = db.Blogs.Count(b => b.Id == updatedBlog.Id) != 0;
if (exist)
{
db.Update(updatedBlog);
}
else
{
db.Add(updatedBlog);
}
db.SaveChanges();
Since Add or Update methods start tracking whole graph, if your graph is in one consistent state, (all entities are new or all are being modified) then it would work just fine.
If your graph is somewhat inconsistent that state of each node in graph can be different (e.g. Updating a blog but it has new posts). Then you should use EntityEntry.State on individual entity. This makes sure that state is applied to only given entity and no other related entity in graph. Though you need to do above kind of check for each node in the graph. Another alternative is to use Attach method to attach whole graph in Unchanged state and then set state for individual node.
If you are having auto-generated Key values then probably you will have PK value set only when it is update else it would be CLR default. For single entity without relations, you can make that check yourself instead of querying database like above code and make decision. For graphs, you can use
db.ChangeTracker.TrackGraph(updatedBlog, n => n.Entry.State = n.Entry.IsKeySet ? EntityState.Modified : EntityState.Added);
This will set state of each node based on PK value being set or not.
Hope this helps :)

Reattach modified entity with nested child entities

I'm using EF 6.1
Model
I have entries, containing an user which itself contains a city. The entry also has a supplier.
I retrieve my entries with .AsNoTracking():
return dbContext.Set<entry>()
.Include(x => x.user.city)
.Include(x => x.supplier).AsNoTracking().ToList();
I have even more properties but they are all similar.
View
The user sees one entry and can change all member variables of all properties. He can change the entry id (not the PrimaryKey). He can change the user's last name. He can change the city's name or zip code. And so on...
Problem
Think about this example:
entry: id = 123
user: first_name = "Foo", last_name = "Bar"
city: name = "FooCity", zip_code = "4711"
The user sees this. He can now change the first_name to "Peter" and click save. Then I'm doing this:
dbContext.Set<entry>().Add(modifiedAndNotTrackedEntry);
dbContext.Entry(modifiedAndNotTrackedEntry).State = EntityState.Modified;
dbContext.SaveChanges();
But then EF duplicates all child entites. So in the DB I have a new user, which might be what I want if the user does not yet exist. But this user points to a new city altough the city wasn't changed. Furthermore the supplier now exists two times in the database.
Why AsNoTracking()
I have used AsNoTracking() because the user sees the real entity. If he changes something it directly affects all entities: Setting first_name to "Peter" all entries which had "Foo" "Bar" as user, then have "Peter" "Bar" as user. But only the modified entity should have this change. I think that happens because I directly modify the member variable of the "real" entity in the context.
Question
How can I reattach a modified entity without recreating existing child entities.
OR
How can I achieve what I want without using AsNoTracking()
I´m trying same thing as you, i´m not sure to understand quite well your reasson thought, but probably this document will help you.
https://msdn.microsoft.com/en-us/library/jj592676(v=vs.113).aspx
Also, i´m almost sure that you don´t need to execute .Add() in your example, that will result in new record most of the cases, or in "Unchanged" if apply.
I can achieve to attach same entity with new values just by attaching and applying state = modified, still researching how to re-attach related properties (my worst problem is the 1toN relationship)
Check under "Attaching an existing entity to the context", probably it leads you a solution.
I´m also researching an interesting post here in code project:
https://www.codeproject.com/Articles/33088/Reattaching-Entity-Graphs-with-the-Entity-Framewor
It seems this guy invented an extension to re-attach node graphs on entity framework, like "here you have the missing feature from EF." I´ll give it a try.
Regards.

Navigation property is NULL after insert in Entity Framework

I saw this post that suggests an answer but my situation is a bit different.
// Create a new Lunch entity with these two properties.
Lunch lunchEntity = new LunchEntity();
lunchEntity.UserId = userId;
lunchEntity.MealId = mealId;
// Add the entity to the DbContext and save the changes.
restaurantEntities.Lunches.Add(lunchEntity);
restaurantEntities.SaveChanges();
// Get the Lunch entity that we inserted above.
Lunch mySavedLunchEntity = restaurantEntities.Lunches.Find(lunchEntity.Id);
Now, after inserting the Lunch entity, I need to have its instance with all of its navigation properties included. That's why I use the Find() method to select the newly created entity. The problem is that the User navigation property is null, while the Meal navigation property has a reference to the correct object.
Moreover, if I execute this statement
Lunch mySavedLunchEntity = restaurantEntities.Lunches.Find(lunchId);
separately in another method that is supposed to retrieve a Lunch entity for a particular Id, all navigation properties are included correctly.
So, my question is why all my navigation properties are included when I just query a given element, and some of them are not, if I query the element only after it has been inserted?
You could try:
Lunch mySavedLunchEntity = restaurantEntities.Lunches.Where(l => l.LunchId == lunchId).Include(l => l.User).Include(l => l.Meal)
This forces EF to load the two navigation properties instead of Lazy loading them.

Save large entities and children with entity framework

I need to update an entity that has two children which also has two children that both are dependent on both parents.
Job(PK: Jobid)
Holes(PK: Holeid, FK: Jobid) / Orders(PK: Orderid, FK: Jobid)
Tools(PK: Toolid, FK: Holeid, FK: Orderid) / ToolHoles(PK: Holeid, Orderid)
Tools also has 7 children that inherit from it.
The job will already exist on save. The job may or may not already contain 1 or more of each child entity.
I need to be able to save all this information in one transaction so that partial info is not saved to the database.
My current attempt has been to build up the Job entity with all the relevant information and call SaveChanges. If I'm adding new entities, keys will have to be generated on save for all but Jobid. Is what I'm trying to accomplish even possible?
Making some assumptions here, let me know if I'm off base. If your scenario looks like the below, then you should be fine.
var myHoles = new Holes();
var myOrders = new Orders();
var myTools = new Tools();
var myToolHoles = new ToolHoles();
myJob.Holes.Add(myHoles); //myJob already exists
myJob.Orders.Add(myOrders);
myHoles.Tools.Add(myTools);
myOrders.Tools.Add(myTools);
myHoles.ToolHoles.Add(myToolHoles);
myOrders.ToolHoles.Add(myToolHoles);
db.SaveChanges();
You say "one transaction" but the reality is that several transactions take place.
myHoles and myOrders will be inserted to the database with their JobId set appropriately.
EF will find out what their IDs are
myTools and myToolHoles will be inserted with the HoleId and OrderId set with the values found in the second step.

POCO - Entity Framework - Persisting only scalar properties

This may be little weird, but I want to persist only the scalar properties of an object to the database. eg. let's say I have 2 tables/entities "User" and "UserOrder". "User" entity has "FirstName", "LastName", "UserOrders" (collection of "UserOrder"). Now, I get a "User" object which has some UserOrders in it. I need to persist only the scalar properties of the the "User" i.e.FirstName & LastName.
If I do context.Users.AddObject(user), it is adding "UserOrders" also to the db (which I don't want). Does creating a new "User" object and copying all the scalars in to that is my only option (or) can we explicitly tell EF to persist only the scalars even though there are other navigational properties in it?
The reason I ask is I have to persist "User" and "UserOrders" separately in 2 different steps.
Very bizarre - i'm sure you have your reasons for doing this.
You could remove the navigational properties (e.g non-scalar) from the model, so you can persist the two objects seperately.
Step 1 - Adding a User:
var user = new User { };
user.UserOrders = new UserOrders { }; // compiler error! no property exists. good!
ctx.Users.AddObject(user); // only user scalar properties added
Step 2 - Adding a UserOrder - which still needs to "relate" to a User:
var relatedUser = ctx.Users.First(); // grab the user related to this order
var userOrder = new UserOrder { UserId = relatedUser.UserId }; // explicitly set the FK
ctx.UserOrders.AddObject(userOrder); // UserOrder added with appropriate FK
That should work - i haven't tried it before, but give it a go.
Keep in mind the drawback of this approach is you will not be able to eager/lazy load UserOrders when you request a User - you will have to manually join on the FK using LINQ.
Can you explain why you need to do this in 2 separate steps? Either way, if you have a limited number of navigational properties, you could simply store in another variable & remove:
var orders = user.Orders;
user.Orders = null;
context.Users.AddObject(user);
...
context.Orders.AddObject(orders); // You might have to do these one by one

Categories

Resources