Entity Framework 4: ObjectContext event on successful save - entity-framework-4

Is there a good way to detect when the ObjectContext changes are actually committed?
SavingChanges occurs before going to the data store but I also need a way to know if those changes where actually committed.
Thanks in advance
John
Update:
What I have is a code first DbContext. This is fed into dynamic data which as I discovered uses the DbContext's internal ObjectContext (to which I have access when casting to IObjectContextAdapter). The dbcontext's SaveChanges is not called, the objectcontext's SaveChanges is used instead. All I want to do is to be notified after the save is complete (i.e. event SavedChanges) so I can invalidate my cache.

There is no build-in event to handle this but you can override SaveChanges method in your derived context and fire any custom event specific to your own context type after you call base.SaveChanges.

pardon me, but I can't find the solution in the answer.
Let me rephrase this question according to my understanding (and my case):
I am using Dynamic Data, which ONLY accepts ObjectContext as configuration; if you use DbContext (which is the way to go with Code First) then you will have to pass the property "IObjectContextAdapater.ObjectContext" like the following:
DefaultModel.RegisterContext(() => { return ((IObjectContextAdapter) new MyDbContext()).ObjectContext; }, new ContextConfiguration() { ScaffoldAllTables = true });
The problem here is that when you save changes, the SaveChanges method of the MyDbContext is NOT called, instead Dynamic Data calls the method SaveChanges in the MyDbContext.ObjectContext.
So overriding the SaveChanges in MyDbContext is useless in this case.
How can we access the SaveChanges in the ObjectContext property and change the behavior so we can write our custom code?
But anyway, the solution I found correct was in a comment by "rene" to the question above, which is adding an event handler for SavingChanges EVENT in the ObjectContext property, here is the link again:
http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.savingchanges.aspx
I hope this clears it

Related

Deserialize Breeze JObject into EntityInfo without saving changes

I have a breeze controller that accepts a JObject, is there an easy way to deserialize that JObject into it's strongly typed source EntityInfo objet without going through Save changes / Before Save changes. I just want to get the object that the JObject payload is referring to.
Thanks for your help.
I ended up using the approach outlined in this related question.
Uninitialised JsonSerializer in Breeze SaveBundleToSaveMap sample
Option 1
Take a look at the code in the Breeze.ContextProvider class's CreateEntityInfoFromJson method. It's protected-internal so you'll need to copy the code or call it using reflection. Use at your own risk.
Option 2
The breeze savechanges code uses a public class called SaveWorkState which is constructed using two arguments: a ContextProvider and a JArray. To get an idea of what's expected for the
JArray, take a look at the "entities" property in the JSON sent to the server during a savechanges.
Once the SaveWorkState is constructed you can access the EntityInfo objects via the EntityInfoGroups property.
I've never tried either option before, found these options by looking at the breeze.server.net code.
Yes it is possible ... and pretty easy too. I answered this question in GREAT detail on the related SO question, Uninitialised JsonSerializer in Breeze SaveBundleToSaveMap sample that you referenced.

How to override EFContextProvider to create a read only OData entity

So I'm pretty sure I'm on the right track, just need some help implementing this the way I want to. So to accomplish this readonly entity, I know I need to subclass the EFContextProvider and override a method. I know I can accomplish this by overriding SaveChanges, but I'm wondering if I should override the Dictionary property instead, leaving the readonly property out of that Dictionary. Will this effect anything? If so, what will it effect?
I assume you have client logic that prevents it from attempting to save changes to a "read-only" entity. That leaves guarding the server which should reject any request that attempts to save a "read-only" entity, right?
If so, I would put logic in the EFContextProvider.beforeSaveEntity that tests if the entity is savable and throws if it is not. I often make my entities inherit from ISaveable (my interface) and throw an exception when I see an entity that is not.

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

NHibernate: how to handle entity-based validation using session-per-request pattern, without controllers knowing about ISession

What is the best way to do entity-based validation (each entity class has an IsValid() method that validates its internal members) in ASP.NET MVC, with a "session-per-request" model, where the controller has zero (or limited) knowledge of the ISession? Here's the pattern I'm using:
Get an entity by ID, using an IFooRepository that wraps the current NH session. This returns a connected entity instance.
Load the entity with potentially invalid data, coming from the form post.
Validate the entity by callings its IsValid() method.
If valid, call IFooRepository.Save(entity), which delegates to ISession.Save(). Otherwise, display error message.
The session is currently opened when the request begins and flushed when the request ends. Since my entity is connected to a session, flushing the session attempts to save the changes even if the object is invalid.
What's the best way to keep validation logic in the entity class, limit controller knowledge of NH, and avoid saving invalid changes at the end of a request?
Option 1: Explicitly evict on validation failure, implicitly flush: if the validation fails, I could manually evict the invalid object in the action method. If successful, I do nothing and the session is automatically flushed.
Con: error prone and counter-intuitive ("I didn't call .Save(), why are my invalid changes being saved anyways?")
Option 2: Explicitly flush, do nothing by default: By default I can dispose of the session on request end, only flushing if the controller indicates success. I'd probably create a SaveChanges() method in my base controller that sets a flag indicating success, and then query this flag when closing the session at request end.
Pro: More intuitive to troubleshoot if dev forgets this step [relative to option 1]
Con: I have to call IRepository.Save(entity)' and SaveChanges().
Option 3: Always work with disconnected objects: I could modify my repositories to return disconnected/transient objects, and modify the Repo.Save() method to re-attach them.
Pro: Most intuitive, given that controllers don't know about NH.
Con: Does this defeat many of the benefits I'd get from NH?
Option 1 without a doubt. It's not counter intuitive, it's how NH works. Objects retrieved using NH are persistent and changes will be saved when the session is flushed. Calling Evict makes the object transient which is exactly the behavior you want.
You don't mention it but another option might be to use Manual or Commit FlushMode.
How about a validation service with an IsValid (or something similar) method which validates the object passed to it, if it fails it could publish a ValidationFailed event. Then when your request finishes instead of calling the session's flush you could publish a RequestEnd event. You could then have a handler that listens for both RequestEnd events and ValidationFailed events - if there is a ValidationFailed event then don't flush the session but if not then flush it.
Having said that I just do Option 2!
As Mauricio and Jamie have pointed out in their answers/comments, it's not easy (and probably not desirable) to do exactly what the question asks. NH returns persistent objects, so exposing those objects to the controllers means the controllers are responsible for treating them as such. I want to use lazy loading, so exposing detached instances won't work.
Option 4: Introduce a new pattern that provides the desired semantics
The point of this question is that I'm introducing NH+Repositories to an existing project using a hand-rolled, Active-Record-like DAL. I want code written NH to use patterns similar to the legacy code.
I created a new class called UnitOfWork that is a very thin wrapper over an ITransaction and that knows how to access the ambient session related to the current HttpRequest. This class is designed to be used in a using block, similar to TransactionScope which the team is familiar with:
using (var tx = new UnitOfWork()) {
var entity = FooRepository.GetById(x);
entity.Title = "Potentially Invalid Data";
if (!entity.IsValid()) {
tx.DiscardChanges();
return View("ReloadTheCurrentView");
}
else {
tx.Success();
return RedirectToAction("Success");
}
}
The tx.DiscardChanges() is optional, this class has the same semantics as TransactionScope which means it will implicitly rollback if it is disposed before the success flag is set.
On a greenfield NH project I think it's preferable to use Option 1, as Jamie indicates in his answer. But I think Option 4 is a decent way to introduce NH on a legacy project that already uses similar patterns.

Updating a disconnected LINQ object with MVC Framework RC1

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.

Resources