Updating a disconnected LINQ object with MVC Framework RC1 - asp.net-mvc

This is a little out there but I have a customer object coming back to my controller. I want to just reconnect this object back to the database, is it even possible? I know there is a datacontext.customers.insertonsubmit(customer), but is there the equivalent datacontext.customers.updateonsubmit(customer)???

This is what I don't like about LINQ-to-SQL.
It generally works fine if you're querying and updating in the same scope, but if you get an object, cache it, and then try to update it later, you can't.
Here's what the documentation says:
Use the Attach methods with entities that have been created in one DataContext, and serialized to a client, and then deserialized back with the intention to perform an update or delete operation. Because the new DataContext has no way of tracking what the original values were for a disconnected entity, the client is responsible for supplying those values. In this version of Attach, the entity is assumed to be in its original value state. After calling this method, you can then update its fields, for example with additional data sent from the client.
Do not try to Attach an entity that has not been detached through serialization. Entities that have not been serialized still maintain associations with deferred loaders that can cause unexpected results if the entity becomes tracked by a second data context.
A little ambiguous IMHO, specifically about exactly what it means by "serialized" and "deserialized".
Also, interestingly enough, here's what it says about the DataContext object:
In general, a DataContext instance is
designed to last for one "unit of
work" however your application defines
that term. A DataContext is
lightweight and is not expensive to
create. A typical LINQ to SQL
application creates DataContext
instances at method scope or as a
member of short-lived classes that
represent a logical set of related
database operations.
So, DataContexts are intended to be tightly scoped - and yet to use Attach(), you have to use the same DataContext that queried the object. I'm assuming/hoping we're all completely misunderstanding what Attach() is really intended to be used for.
What I've had to do in situations like this is re-query the object I needed to update to get a fresh copy, and then do the update.

The customer that you post from the form will not have entity keys so may not attach well, also you may not have every field of the customer available on the form so all of it's fields may not be set.
I would recommend using the TryUpdateModel method, in your action you'll have to get the customer from the database again and update it with the form's post variables.
public ActionResult MySaveAction(int id, FormCollection form)
{
Customer updateCustomer = _Repository.GetCustomer(id);
TryUpdateModel(updateCustomer, "Customer", form);
_Repository.Save(updateCustomer);
}
You will have to add in all your own exception handling and validation of course, but that's the general idea.

You want to use the attach method on the customers table on the data context.
datacontext.customers.Attach(customer);
to reconnect it to the data context. Then you can use SubmitChanges() to update the values in the database.
EDIT: This only works with entities that have been detached from the original data context through serialization. If you don't mind the extra call to the database, you can use the idiomatic method in ASP.NET MVC of retrieving the object again and applying your changes via UpdateModel or TryUpdateModel as #Odd suggests.

Related

An entity object cannot be referenced by multiple instances of IEntityChangeTracker error when trying to save changes to multiple types of entities

I have a controller action where I am attempting to both create a new object in the database, of type FantasyTeam, and also modify an existing object, of type User. However, when the action is called I am receiving a System.InvalidOperationException exception, with the explanation of:
An entity object cannot be referenced by multiple instances of
IEntityChangeTracker.
My code is below.
public ActionResult Create([Bind(Include="ID,FantasyTeamName")] FantasyTeam fantasyteam)
{
if (ModelState.IsValid)
{
var CurrentUser = UserManager.FindById(User.Identity.GetUserId());
fantasyteam.OwnerID = CurrentUser.Id;
CurrentUser.HasTeam = true;
db.FantasyTeams.Add(fantasyteam);
db.Entry(CurrentUser).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Dashboard", "Application");
}
return View(fantasyteam);
}
The Controller Action takes the new FantasyTeam object as a parameter from the View where the team details are selected by user. I then find the currently logged in user and set the OwnerID of the team to match the UserID of the owner, as my Foreign Key. I then go to add the new FantasyTeam to the database and also modify the User record in the database, changing the Boolean HasTeam to true. The adding of the new team works fine on its own... I have been getting the exception once I tried to modify the User object as well.
I have searched on StackOverflow for this exception, and have found many related issues, but I couldn't find a way to apply the other resolutions to my problem. In some cases the issue was that the developers were using two separate data contexts when they should have just been using one, but I don't think that would apply to me here.
Could someone offer insight as to what might be going wrong here?
Thanks!
According to Linq to SQL DataContext Lifetime Management the main causality of the problem comes from the below reason.
Linq to SQL uses a DataContext to manage it's access to the database as well as tracking changes made to entities retrieved from the database. Linq to SQL has a persistent approach to managing its 'connection' to the database via this data context and it basically assumes that you use a single DataContext to make all of your data related access. This doesn't mean that it makes persistent connections to the database, but means that the DataContext instance maintains state about active result sets, which is especially important if change tracking is on which is the default.
The comment posted by haim770 was the answer - I didn't realize that the UserManager object was using a different context than the db object was using. Once I changed the UserManager object to use the same context as db, my code works as intended.

Using this.Context inside BeforeSaveEntity

I was looking for a good way to organize validation rules within BeforeSaveEntity method and I have found this comment in the file: TodoContextProvider.cs within the project: BreezeMvcSPATemplate:
// A second DbContext for db access during custom save validation.
// "this.Context" is reserved for Breeze save only!
Why this.Context can not be used?
Excellent question. The answer isn't obvious and it's not easy to cover briefly. I will try.
The EFContextProvider takes the save data from the client and (ultimately) turns these data into entities within the EFContextProvider.Context. When the save is approved, the EFContextProvider calls the SaveChanges method on this EF Context and all of its contents are saved as a single transaction.
There are two potential problems.
1. Data integrity and security
Client data can never be fully trusted. If you have business rules that limit what an authorized user can see or change, you must compare the client-derived entity to the corresponding entity from the database.
An EF Context cannot contain two copies of the "same entity". It can't hold two entities with the same key. So you can't use the EFContextProvider.Context both to fetch the clean copy from the database and to hold the copy with changes.
You'll need a second Context to get the clean copy and you'll have to write logic to compare the critical values of the entity-to-save in the EFContextProvider.Context with the values of the clean entity in the second Context.
2. Cross-entity validation
Many validation do not require comparison of values with a clean entity.
For example, the out-of-the-box System.ComponentModel.DataAnnotations attributes, such as Required and MaxLength are simple data validations to determine if an entity is self-consistent. Either there is a value or there is not. The value is less than the maximum length or it is not. You don't need a comparison entity for such tests.
You could write your own custom System.ComponentModel.DataAnnotations attributes that compare data values within a single entity. You might have a rule that says that order.InvoiceDate must be on-or-before order.ShipDate. that is also a self-consistency test and you won't need a comparison entity for that one either.
If these are the only kinds of validation you care about - and you're using an EF DbContext - you can let EF run them for you during its save processing. You won't need a second Context.
But cross entity validations are another story. In a cross-entity validation, entity 'A' is valid only when some condition is true for entity 'B' (and perhaps 'C', 'D', 'E', ...). For example, you may require that an order item have a parent order that is already in the database.
There is an excellent chance that the parent order is not in the EFContextProvider.Context at the time you are validating the order item.
"No problem," you say. "I'll just navigate to the parent with someItem.Order."
No you cannot. First, it won't work because lazy loading is disabled for the EFContextProvider.Context. The EFContextProvider disables lazy loading mostly to break circular references during serialization but also to prevent performance killing "n+1" bugs on the server.
You can get around that by loading any entity or related entities at will. But then you hit the second problem: the entity you load for validation could conflict with another entity that you are trying to save in this batch.
The EFContextProvider doesn't populate its Context all at once. It starts validating the entities one-by-one, adding them to the Context as it goes.
Continuing our example, suppose we had loaded the parent order for someItem during validation. That order is now in EFContextProvider.Context.
The save process continues to the next entity and ... surprise, surprise ... the next entity happens to be the very same parent order. The EFContextProvider tries to attach this copy to the Context which already has a copy (the one we just loaded) ... it can't.
There's a conflict. Which of the two orders belongs in the EFContextProvider? The clean copy we just loaded for validation purposes ... or the one that came from the client with modifications to be saved?
Maybe you think you know the answer. Maybe I agree. But the fact is, the EFContextProvider throws an exception because there is already an order with that key in the Context.
Conclusion
If all your validations are self-consistency checks, the EFContextProvider.Context is all you need. You won't have to create a second Context
But if you have data security concerns and/or business logic that involves other entities, you need a second Context ... and you'll need sufficient EF skills to use that Context.
This is not a limitation of Breeze or the Entity Framework. Non-trivial business logic demands comparable server-side complexity no matter what technology you choose. That's the nature of the beast.

Attaching object to EF 4 context from MVC view?

I am not sure if that makes any sense, but here is an example.
I have a Category object, that my Service hands to the Controller, which uses AutoMapper to create a CategoryViewModel. Hand that off to the view, serve it to the client.
Now when that gets posted back, AutoMapper creates a Category from the Model sent back, and I hand it to the Service that gives it to the Repository to persist to the database.
My question is, what is the correct way of doing this? I assume the object is a detached object when posted back and I need to attach it to the context, mark it dirty and save changes?
Basically two ways of doing the update of the entity:
Attach the entity to the context, mark it as modified using ObjectStateManager.ChangeObjectState Method, call ObjectContext.SaveChanges Method
Load the original entity from DB, apply changes to the original using ObjectContext.ApplyCurrentValues<TEntity> Method, call ObjectContext.SaveChanges Method
Each of those have their own pros and cons. For example the 1st one does not make round trip to get the original entity but fails to address concurrency as well as tries to update every property of the entity, while the 2nd one works best when employing optimistic concurrency, updates only changed properties, but it does make extra trip to Db to get the original entity.
"I assume the object is a detached object when posted back and I need to attach it to the context, mark it dirty and save changes?"
Yes.
Any one of the links on this page should help:
http://www.google.com/search?rlz=1C1CHFX_enUS410US410&sourceid=chrome&ie=UTF-8&q=working+with+dicsonnected+entities+entity+framework

ASP.NET MVC -> WCF -> NHibernate, how to efficiently update entity with data from viewmodel?

A week back, I had an ASP.NET MVC application that called on a logical POCO service layer to perform business logic against entities. One approach I commonly used was to use AutoMapper to map a populated viewmodel to an entity and call update on the entity (pseudo code below).
MyEntity myEntity = myService.GetEntity(param);
Mapper.CreateMap<MyEntityVM, MyEntity>();
Mapper.Map(myEntityVM, myEntity);
this.myService.UpdateEntity(myEntity);
The update call would take an instance of the entity and, through a repository, call NHibernate's Update method on the entity.
Well, I recently changed my logical service layer into WCF Web Services. I've noticed that the link NHibernate makes with an entity is now lost when the entity is sent from the service layer to my application. When I try to operate against the entity in the update method, things are in NHibernate's session that shouldn't be and vice-versa - it fails complaining about nulls on child identifiers and such.
So my question...
What can I do to efficiently take input from my populated viewmodel and ultimately end up modifying the object through NHibernate?
Is there a quick fix that I can apply with NHibernate?
Should I take a different approach in conveying the changes from the application to the service layer?
EDIT:
The best approach I can think of right now, is to create a new entity and map from the view model to the new entity (including the identifier). I would pass that to the service layer where it would retrieve the entity using the repository, map the changes using AutoMapper, and call the repository's update method. I will be mapping twice, but it might work (although I'll have to exclude a bunch of properties/children in the second mapping).
No quick fix. You've run into the change tracking over the wire issue. AFAIK NHibernate has no native way to handle this.
These may help:
https://forum.hibernate.org/viewtopic.php?f=25&t=989106
http://lunaverse.wordpress.com/2007/05/09/remoting-using-wcf-and-nhibernate/
In a nutshell your two options are to adjust your service to send state change information over the Nhibernate can read or load the objects, apply the changes and then save in your service layer.
Don't be afraid of doing a select before an update inside your service. This is good practice anyway to prevent concurrency issues.
I don't know if this is the best approach, but I wanted to pass along information on a quick fix with NHibernate.
From NHibernate.xml...
<member name="M:NHibernate.ISession.SaveOrUpdateCopy(System.Object)">
<summary>
Copy the state of the given object onto the persistent object with the same
identifier. If there is no persistent instance currently associated with
the session, it will be loaded. Return the persistent instance. If the
given instance is unsaved or does not exist in the database, save it and
return it as a newly persistent instance. Otherwise, the given instance
does not become associated with the session.
</summary>
<param name="obj">a transient instance with state to be copied</param>
<returns>an updated persistent instance</returns>
</member>
It's working although I haven't had time to examine the database calls to see if it's doing exactly what I expect it to do.

Entity Framework creating new record instead of modifying existing one

I'm using Entity Framework with an AS.NET MVC application. I need to allow the user to create new records and modify existing ones. I am able to fetch existing records no problem, but when I pass back in the edited entity and try to save it it creates a new one and saves it and leaves the original unmodified.
I am getting the object from EF using the primary key (e.g. ID number for an employee record). I successfully retrieve it, and set the MergeOption like so:
Context.Sector.MergeOption = MergeOption.NoTracking;
I am able to trace that the object has the correct data (using the key of the original record) all the way down to the point where I call:
Context.SaveChanges();
However, after that, the new record is created instead of modifying the existing one.
Is there something obvious I am missing here? I would have thought that retrieving the object and changing some of its values (not the ID) and saving it would just work, but obviously not.
Thanks,
Chris
"NoTracking means that the ObjectStateManager is bypassed and therefore every access to the Entity Objects results in a fetch from the database and the creation of new objects."
-- http://blog.dynatrace.com/2009/03/11/adonet-entity-framework-unexpected-behaviour-with-mergeoptions/
I don't think NoTracking is what you want.
From your comment: "distributed across various tiers and some proprietary libraries"
Are you new()ing up a ObjectContext, closing it or losing the reference to it, and then trying to save your object to a new() or different ObjectContext?
If so your losing all of your change tracking information. If this is the case then you want to call the Attach() method to reattach the entity to the context, ApplyPropertyChanges() and then finally SaveChanges().
Julie Lerman has a pretty good blog post that outlines all the different change tracking options and techniques that are available. You should also check out this MSDN article on the same subject.

Resources