Reattach modified entity with nested child entities - c#

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.

Related

How to transition partial update operation from ObjectContext to DbContext

I am using entity framework 5.0. I am in a rocess od changing my app from ObjectContext to DbContext model. DbContext should be according to microsoft the recommended approach. I use database forst approach and I have generated model form database.
But, at a very first simple task there is a problem. Namely simple update of a record is broken.
Let's have a simple table Item, for ilustration only:
Item
(
ItemId int NOT NULL, -- Primary key
Name nvarchar(50) NOT NULL,
Description NVARCHAR(50)
)
I have noticed that using DbContext does not support updating a record not as ObjectContext does.
In my application I have a simple update method.
public void UpdateItem()
{
MyContext context = new MyContext();
Item item = new Item();
item.ItemId = 666;
context.Items.Attach(item);
// From this point onward EF tracks the changes I make to Item
Item.Description = "Some description";
context.SaveChanges();
}
Using ObjectContext this method correctly updates a record. Using SQL profiler I can see that it generates something like this (greatly simplified!!!)
UPDATE Item
SET Description = 'Some description'
WHERE ItemId = 666
If, however I try to do the same thing in DbContext I get the exception:
System.Exception: Items.aspx.cs - logged from CustomError() ---> System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
And no database UPDATE is issued to Sql server.
I guess that DbContext validates all the properties and the property Name is null. This by design. I do not intend to modify it, I do not even know what is it and I do not need to know what is it.
Only the property Description was changed. Clearly ObjectContext does not track changes correctly.
How can this problem be resolved?
I have researched the issue and found the something on updating records.
For example this link: https://stackoverflow.com/a/15339512/4601078
db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();
But this is horrible code. For every property on should add a line like:
entry.Property(e => e.Email).IsModified = true;
This produces ugly unreadable code, an I suspect lamda expression are not stelar in performance.
Even worse are those who propose to make a roundtrip to DB to fetch existing records with all properties populated, update it and the save changes. This is a no go with regard to performance.
So, how to tackle with simple entity updates or is this DbContext just another item in microsofts collection of dead ends which serve no real purpose?
DbContext doesn't really track changes by watching properties, it compares the values to previously known values. And validation always works on the entire entity so the only real choice if you want to do things this way is to disable validation during this operation. See Entity Framework validation with partial updates
If you know for sure that the changes you apply are valid, or you have custom code to validate them, you can turn off validation by EF altogether:
db.Configuration.ValidateOnSaveEnabled = false;
This works OK as long as you do it your way: attach a new entity with a known Id (aka a stub entity) and then modify its properties. EF will only update the properties it detects as having been modified (indeed by comparing original and current values, not, as ObjectContext did, by change notifications). You shouldn't mark the entity itself as modified.
If you don't want to turn off EF's validation, but neither want to mark individual properties as modified, I think this could be a useful alternative (brought to my attention by Alex's answer).

Update query with NHibernate

I have couple of questions with update functionaliy using NHibernate
I have Customer and location entities with 1:n relationship. Customer has location property. While creating/updating customer entity, I just assigned location property and commited changes.
new Location() { Id = ViewModel.LocationId };
Is it proper way to do it or do I need to retrieve the location entity from db and attach it again like below
newCust.Location = GetlocationfromDB(ViewModel.LocationId);
And how does it work with m:n relationship. I have order and orderitems entities. So, if a newgroup is added/deleted, do I need to check which group is added and get from db and attach it or just groupid will do fine..
This isn't the right way to do it - it might work if you have your unsaved-value mapping right for the primary key, but the proper way to do it is to use session.Load(ViewModel.LocationId) see http://ayende.com/blog/3988/nhibernate-the-difference-between-get-load-and-querying-by-id
There are a number of ways of dealing with this, but it sounds like you want your relationship to be mapped as a set (to prevent duplicates) rather than a bag. If you map it as a set and use ISet for the property type of the relationship, the duplicates will be handled for you. If however you use a bag, you would need to remove duplicates in your own code. Again, you should be using session.Load to get the group if it's an already existing group.

While attempting to traverse relationships, my entity framework table objects are null

I have the following tables represented in my Entity Framework diagram (.edmx file)
Users
- UserID
- Username
- UserGroupID
Groups
- GroupID
- GroupName
In my code, I retrieve a valid instance of the User object and I'm trying to traverse the relationship to get to the Groups table, to retrieve the GroupName, however everytime, the Groups object is null. The UserGroupID exists in the Groups table, so i'm not sure why this is.
The Visual Studio intellisense knows the relationship exists and allows me to attempt it, but at runtime, the 'Groups' instance is null.
Users users= (Users)e.Row.DataItem;
string groupName = users.Groups.GroupName;
In that case, Groups is null and i'm not sure why. What are the possible causes?
Thanks
Kevin
Looks like you are trying to access Groups from within a databound event and the Groups data was not loaded before you binded it. You most likely need to "include" Groups like below.
var users = entity.Users.include("Groups").where(x => x.UserID == 20);
You might want to read this, Entity doesn't seem to act the same as Linq-To-SQL when it comes to Lazy Loading
http://www.singingeels.com/Articles/Entity_Framework_and_Lazy_Loading.aspx
How to load related items: http://msdn.microsoft.com/en-us/library/bb896272.aspx
probably the lazy loading is disabled.
add the below line when you initialize the context
context.ContextOptions.LazyLoadingEnabled = true;
anyway you should think about what strategy you want to use to pull date from your DB

Can Entity Framework Auto-Update Property Navigation By Id

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

Can entities be attached to an ISession that weren't previously attached?

I'm playing around with NHibernate 3.0. So far things are pretty cool. I'm trying to attach an entity that wasn't detached previously:
var post = new Post(){ Id = 2 };
session.Update(post); // Thought this would work but it doesn't.
post.Title = "New Title After Update";
session.Flush();
What I'm trying to write is the following:
var post = new Post(){ Id = 2 };
session.Attach(post);
post.Title = "New Title After Update";
session.Flush(); // Sql should be something like: UPDATE Post SET Title='New Title After Update' WHERE Id=2
Is this possible so that only Title gets updated? This is currently possible in EntityFramework. I'd like to not have to load Post from the database when I just need to update a few properties. Also, I'm trying to avoid a method call that would create the object... since it's moving away from an object oriented approach in my opinion.
EDIT: I know about using transactions, I just used Flush() to make the code simple. Ok so I think we're sort of getting on the right track for what I'm trying to achieve. I'd like to be able to create an entity with a known Id using the constructor, like I have in the 2nd code block above. I don't want to have to make a call to Get<T> or Load<T> since it feels rather wrong constructing objects like this that already exist in the database. For example, in Entity Framework I can write the 2nd code example and it will "just work". It only updates the Title property.
You can session.Save() or session.SaveOrUpdate()
update
Okay, I think I see now what you are trying to do. You are trying to update a single property on a Post that was previously persisted, not a new Post, and to do that you're instantiating a new Post and giving it the Id of one in the database.
I'm not sure what you mean when you say you're trying to avoid a method call that would create the object, but the way to do this with NHibernate is this:
var post = session.Load<Post>(2);
post.Title = "New Title";
session.SaveOrUpdate(post);
In general, you should not be calling Flush() on your sessions.
The important thing to note here is the use of session.Load. Calling Load with an id in and of itself does not load the entity from the database. The entity's property values will only be loaded when/if you access them.
Of course, in this scenario, I believe that NHibernate will load the properties for the Post, (but not collections unless you've specified an eager fetch mode), and that makes sense (frankly, I don't understand why EF would not load the entity). What if the setter for your Title property does something important, like check it against the existing title, validate the title's length, check your credentials, or update another property? Simply sending an UPDATE to the database isn't sufficient.
It's possible to only update changed properties by setting dynamic-update in the mapping. However, as far as I know, it is not possible (without reverting to SQL) to perform an update without retrieving the object from the database at some point.
Use the Merge method. You have to create a new instance variable to accept the attached entity = nhibernate will not do anything else with your detached instance.
var post = new Post(){ Id = 2 };
post.Title = "New Title After Update";
// Must create a new instance to hold final attached entity
var attachedPost = session.Merge(post);
session.Update(attachedPost);
session.Flush();
// Use attachedPost after this if still needed as in session entity
That covers the "attach" functionality you are looking for, but I don't see how you are going to be able to only update the one property. if the object instance has not been populated from the database, the properties will be different. Dynamic mapping will not solve this - NHibernate sees the properties as "updated" to a bunch of nulls, empty strings.
Gotta say, you are creating a new instance but what you are actually doing is updating an existing instance. You are working directly with IDs not objects. And you are setting a single property and now have an instance potentially hanging around and doing more things but it has not enforced any invariants and may in fact bear no resemblence to the real deal other than the id property...
It all feels pretty anti-object oriented to me personally.

Categories

Resources