NHibernate & Cancelling Changes to Entities - c#

This seems like it would be a common issue to be but I don't know the best way to solve it. I want to be able to send an Entity to a view, have changes be made to the entity in the view, but then cancel (remove) those changes if the user cancels out of the view. What is the proper way to do this.
Here are two options I have but I think there should be others that are better
1) Take an entity, create a clone, send the clone to the view...if changes are accepted, update the original entity with the clone's values
2) Send the entity to the view, if the user cancels, remove the entity from NHibernate's cache and reload it from the database
For (2), the issue for me would be that the old entity could still be referenced throughout my project after it has been removed from the cache.
Edit:
Ok, so the evict method is the way to go if I am implementing method (2). Thanks, I could not remember the details of that one. However, the issue of view objects referencing my old evicted entities makes the issue tough to deal with. I can't just have my view automatically update to a new entity without having custom code in each one to rebind when my custom eviction event is raised. And rebinding may not be trivial in certain cases. I need to think on this some more as I may be over complicating but at the moment, this method seems trickier.
I suspect I am going to be stuck with method (1) which has its own set of problems but will wait a bit longer to see if anyone else has some ideas.
Edit 2: Just found this. I think it pretty much covers the answer in detail and comes with a great demo project - Building a Desktop To-Do Application with NHibernate - http://msdn.microsoft.com/en-us/magazine/ee819139.aspx
In addition to this, NHibernate has a Session.Refresh(Object entity) function which seems to solve the exact problem. So, when an entity is changed but then cancelled before save, I can just call Session.Refresh to reload it from the database and discard the changes.

I'll go for option 1 and use what is called a ViewModel instead of your entity.
The ViewModel is representation of you model for a specific view. In the ViewModel you can mix data from different entities and pre-format values to fit the view. Is an elegant way of passing data to a view and you can accomplish what you want easily.
Using ViewModels is becoming the preferred way of working in ASP.net MVC and Silverlight / WPF.
To read more about Viewmodels: http://blogs.msdn.com/dphill/archive/2009/01/31/the-viewmodel-pattern.aspx

The best way to do this is to call the Evict method on the ISession used to load the object. This will remove the object from the session cache. and you can then reload and redisplay it.
Evicting the object from the session makes it transient detached so if there are still references to it in the project they will not be persisted when the session is flushed. How you deal with that depends on your application but I would suggest raising an event to notify subscribers that they need to re-load the object.

Related

In EF, call SaveChanges from SavingChanges event?

I'm using EF 4.1 in an MVC 3 application.
My goal is to save all changes in another database for logging purposes.
I have implemented the SavingChanges event for an EntityContext. In that event, I'm extracting changes via the GetObjectStateEntries() , that works fine.
Then, I have another EntityContext, that works against a different model and database, for the logging. I create that Context, create logging Entities, adding them to the logging Context. All seems to work fine. Then when I call SaveChanges() on the logging context, Exception occurs, that says that I cant insert because the Id is null (which I know for sure that it isn't).
Does anyone know if it's impossible to achieve this? Am I missing some important detail?
The case was that I had used "Model first" in my logging model, and "DB first" in all other models. That resulted in the mistake that the EF configuration was expecting take care of the Id (GUID) itself, but I was trying to supply one myself. I deleted the entities from the model, and instead, imported them from the DB, and that solved the problem!
Thanks to #Eranga for setting me in the right direction.

Intercepting NHibernate Lazy-Load behaviour to return null if not connected to a session?

This seems like it should be an obvious thing but I've been searching for the answer for hours now with no success.
I'm using NHibernate to persist a domain model, with a service layer that serves an ASP.NET MVC front end (the 'service layer' is currently just a standard class library but may be converted to WCF in the future). The web app asks for the data it wants and specifies the collections on the domain object that it needs, the service layer takes the request, loads the object and required collections (using lazy loading) and passes the object back where it is transformed using AutoMapper to a viewmodel friendly representation.
What I want to be able to do is load the required collections, detach the object from the session and pass it to the front end. However, when AutoMapper tries to map the object this causes a an exception because it's trying to access collections that haven't been initialized and the session is no longer available. I can leave the object connected but in this case the AutoMapper transformation ends up causing all the properties on the object to be lazy-loaded anyway and this won't be an option is we go down the WCF route.
What I want to do is alter this behaviour so that instead of throwing an exception, the collection returns null (or better yet empty) when it is not connected to a session. This was the default behaviour in Entity Framework V1 (which admittedly didn't do auto lazy loading), which I worked with previously but I can't find a way to do it in NH.
Any ideas? Am I on the wrong track here?
EDIT- To be a bit clearer on what I'm trying to achieve, when accessing a collection property I want this behaviour:
Connected to session: lazy-load collection as normal.
No session: property is null (rather than throw exception)
UPDATE - Following this post by Billy McCafferty, I've managed to implement a solution using IUserCollectionType that seems to work so far. Rather than use the provided PersistentGenericBag as he does though, I had to create new types that changed the behaviour when not connected to the session. It's not perfect and requires some very ugly mappings but at least I don't need to touch my domain objects or client mappings to get it working.
The most appropriate solution in this case is probably to check in AutoMapper for lazy-loadable fields if they were indeed loaded with NHibernateUtil.IsInitialized(). Not sure how/if possible to make Automapper use this check for all implicit property mappings though.
Old question but this is what we did to solve the same issue, hopefully it helps to set you on correct path if somebody stumbles upon this problem.

storing entity framework self tracking entities in asp.net session, good idea?

let us assume that I have a reusable business layer that further makes use of a data access layer that was implemented using Entity Framework 4.0. The entities returned/consumed by the business layer are self-tracking, allowing us all the goodies that come with those type of entities.
I would like to be able to store the entities I work with across post backs (on order to avoid re-querying every time). Basically let us assume I have a paged GridView with 10 items in it, and something like a DetailsView to edit those items. Every time you select a new row on the grid, the details view updates with the information of the selected row. My preference would be to only query for the entities on the initial request of the page and store it in session. Then subsequently I have a list of entities that I can work with and eventually modify and send back to the business layer with all of the changes.
I really want to use session instead of view state to reduce the page payload (self tracking entities are heavy) however I really like view state for this because of the fact that when the user navigates away from the page there is no residual effect.
Some of the things that worry me are:
When a user navigates away from the page to another page, the entities from the previous page are still in session. I could always do something on load of a page to do housekeeping type of work. Not sure if that's good practice.
I am worried about people opening browser tabs and having two views into the same page, it seems like that might pose a problem.
Is this even a good approach? Seems like I am trying to have the best of all worlds, it would definitely be much easier to simply re-query on every post back for the entities and pay the 50-100ms hit of the database trip.
Your thoughts are greatly appreciated.
Take the hit of going back to the database. If your site is low trafficked enough to let you consider sticking this into the session, you'll be fine for the query hit.
There are also advantages to this, in that you could have simpler concurrency checking and you'll also not run into the problems you alude to above, of session 'cross-over' when somebody opens two tabs.
If you are going to the database then you are pretty much loosing the benefits of Self Tracking entity which has the ability to track changes when objectcontext is not around. Thus asp.net stateless scenario is a good option for STE. I think u only need to only keep one entity in viewstate that is the one that u are editing which should not be that heavy. You have the option of Session but i never liked session for the same reason you mentioned. It causes so many bugs down the road when something is left hanging.

c#.NET with Entity Framework 4 - how to handle context? best practices?

We are undergoing a migration from classic ASP with SQL and Sprocs. Our choice fell upon c#.net 4 Webforms with Entity Framework 4.
My question is how to handle the context. Example:
Calling repository function GetProductById(), which open up a new context (using) and then we change something on the object and we save it.
When we save it we wont be in the same context as when we fetched the object.
The above didn't really work for us. We then tried to send the context around in our application. While this worked we didn't really want to work this way if we didn't have to.
We are currently using a third option, storing the current context in a global variable. We dispose the context when we have saved it. However we're not sure if this is a viable way in the long run or if we're to hit a wall with this method.
We've tried to search for best practices on this topic but not really been able to find any. Would appreciate and help on this topic.
The Unit of Work pattern described in this article might help, although the examples in the article are with an MVC app rather than an WebForms one.
http://msdn.microsoft.com/en-us/ff714955.aspx
Use object keys
When you get an object from the DB and then manipulate it and after some time (after context has been discarded) want to save it back, use primary key values.
create a new instance of the correct entity object and set key properties (and those that have to be changes) and then save it against newly created context.
Or you can get it again before saving. It's a waste of resources but it is the most bullet proof way of doing it. Eve though I wouldn't recomend it.
Using ASP? Go with MVC then
I highly recomend you rather switch to Asp.net MVC if you're used to ASP, then you'll be better and easier at home in MVC.
Another option available to you is to have the context be put on the thread request. This is done by creating the context on the BeginRequest event of a HTTPModule. You then need to be sure you handle any resources you have created that need to be disposed of in the EndRequest event.
After getting a label on our way to work (Unit of work pattern) we found this link that is pretty much exactly how we work and it might be helpful for anyone with the same thoughts as we had:
http://dotnet.dzone.com/news/using-unit-work-pattern-entity?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+zones%2Fdotnet+%28.NET+Zone%29

ASP.NET Requests... what do to with a Linq DataContext?

Sorry, if this is a duplicate. Please point me to the appropriate question if this is but I could not find exactly what I am looking for.
So I am using a Linq to SQL datacontext for entity tracking and persistence in an ASP.NET web application. It is for an Intranet application that does not have a ton of users at a time. Right now I cam storing the datacontext in session state, which makes me feel dirty! It seems like I need the context always to be present though because I need to preserve the change tracking on the entities that are being modified. All of our screens have a Save button that would then call SubmitChanges() on the DataContext and persist all of the pending changes in memory.
Should I be storing the DataContext? Should I be disposing of it at the end of each request and then recreate it somehow and get the pending changes? If I should recreate it every time, I dont understand how the context could know what has changed without a ton of redundant database hits on each request.
First, I would say to stop putting things in Session altogether. Especially if you don't have a lot of users, just load the data when you need it.
Don't store the data context at all. Just create a new one on each page when you need it. When they hit the Save button, recreate a data context, load the object from the database, make the changes necessary based on the form input, and then save it back to the database. It should just be two database hits for each object, one to load, and then one to save it back.
I think the best practice with the data context is the Unit of Work pattern, where the scope of the Unit of Work is the single request that you are servicing. Instantiate a new data context each time you need to make changes. If you're concerned about overwriting changes that have been made after drawing the previous page, then consider using and persisting a version/timestamp in a hidden field and checking it against that returned from the data context when retrieving the entity to update.

Categories

Resources