Showing posts with label Entity Framework. Show all posts
Showing posts with label Entity Framework. Show all posts

Tuesday, 19 June 2012

Entity Framework Removing Failed Entities Saves From Object Context

Again another issue when I moved to Entity Framework 4, to be far this might have been there all along and I really got issue after more users were on the system and they got inventive at crashing it.

BackGround

Website using EF4.0 database first design which uses a standard Unit of Work and Repository pattern which allows me to developer db helper classes very quickly and I can move this from project to projects without having to reinvent the wheel. If any changes are done on the application (inserts, updates etc. ) then these are save by context.SaveChanges() as per normal patterns of this type.


Problem

The problem is that there is a single context for the whole application and the way how the context works. Also if a user (or code user calls really) calls context.SaveChanges() then it will save all changes in that context even if they are for other users or other parts of the application. This in theory is fine as you are encapsulating the data management to Entity Framework i.e. you don't care as long as it works.

There is a serious bug with this though; if you save data that will cause an exception that data (or entity) will be still be in the context and will try and save again on the next context.SaveChanges() attempt. So you may end up with domino effect on your web application as user after user starts to fall over as context.SaveChanges() tries to save the same bad data.


What you could Do

Have a different context for every user/session or dispose of contexts directly after making a change. This is very wasteful and missing the point of Entity Framework as it automatically load balances the data management and thing like connection pooling etc..

Ideally: Remove The Bad Entity and Throw that Exception 

If a user submits bad data or something falls over while save you really want to remove that data entity from the context and throw the original exception; this really should be the standard pattern in Entity Framework.

So I changed my Repository base class as follows:

FROM


public void SaveChanges()
 {
      context.SaveChanges();
  }

TO 


public void SaveChanges()

{

try  {
         context.SaveChanges();
      }

catch (Exception ex) 
{
      // Get All properties from the Exception
      var properties = ex.GetType().GetProperties();

      foreach (PropertyInfo p in properties)
     { 
         //Search Properties of Exception for StateEntries   
         if (p.Name == "StateEntries")
        {
            //Get the entities 
             var entities = (IEnumerable) p.GetValue(ex, null);
                        
             foreach (var objectStateEntry in entities)
             {
                 //Accept Changes (ie Abandon save of Entity Causing issues)
                  objectStateEntry.AcceptChanges();
              }
        }

      }
           //Throw the Exception so the upper level know it have a problem.
           throw;
}


How it works

Works by extracting the Entity Framework object which caused the exception. The object or "objectStateEntry" is embedded at run time within the exception, you just have to find it.

  1. Try and Save
  2. Get Properties of the Exception
  3. Find "StateEntries" in the exception properties which is the original object collection
  4. There will be only only one  "objectStateEntry" to get this via reflection
  5. Accept Changes on EF entity via  objectStateEntry.AcceptChanges(), this will tell EF that changes on this object are saved or ignore the save really. 
  6. Throw the exception back at calling code: this is very important you want to handle the error not ignore it.








EF: New transaction is not allowed because there are other threads running in the session.

Having quite a few issues after I updated a key project to Entity Framework 4.0 (EF4), it seems that a search using a read closely followed by a write update the EF context. After much searching and fiddling

I came across a blog where they added a ToList() at the end of a query.

This seemed to work as it closed the transaction directly after a read.

This is very odd and did not happen in previous versions of EF.


Example below in the change in repository Helper

 FROM THIS

 public static List GetAll()
 {
           var results = RepositoryRole.GetAll(); 
            return results.ToList(); 
 } 


 TO THIS

 public static List GetAll() 
 { 
      var results = RepositoryRole.GetAll().ToList();
      return results;
 }


p.s. I have lost the reference post that gave me the answer, opps sorry if it was your post, I'll add it if I find it again.

Thursday, 30 June 2011

EF4.1 Entity Framework Bind Issue with GridView

I've had an odd issue with Entity Framework 4.1 EF4.1 when binding to a GridView when adding a new items with a Repository using a Unit of Work pattern.

Symptoms are

  1. Add new item to Repository
  2. Save
  3. Refresh the GridView with the new item
  4. GridView Can not bind due to a "Object Does not match target type" exception
The thing it is does match and it should be perfectly acceptable.  Digging Deep this is what I found:

Click to enlarge
The new object added is of a different type to other stored objects already in the database. But they all come from the same place and should all be the same object.

But making a small change to the Repository Helper Code fixes this.


So Adding a specific cast for the fetch from the repository seems to work and it will bind to the GridView with no issues.  If we look again at the collection just before it binds to the GridView then we see that it is all the same object type.


Click to Enlarge
The above work around does NOT WORK in all cases. See other suggestions below.


Further Investigations: 

The above solution did seem to work for while but it simply failed on another section of code that has new items added to the Entity. This is because the GridView control can't deal with polymorphic data sources when using BoundFields.  


The two alternatives are 


1.  GridView : Template Fields


Use TemplateFields on GridView instead of BoundFields.  TemplateFields can deal with polymorphic datasources. If howevert you have a lot of code already written you may have to change front end code if you use row commands etc.


2. Use Linq for DataSources for transitional sourvce.

So instead using the incoming polymorphic collection from EF simply use Linq to extract what you need.


        protected void GetAllProducts()
        {
            gvGroups.DataSource = ProductHelper.Get();
            gvGroups.DataBind();
        }

You can create a Linq query that will create an object collection that is not polymorphic as below.


        protected void GetAllProducts()
        {
            var query = from p in ProductHelper.Get()
                        select new {p.ProductId, p.ProductName, p.ProductDesc, p.ProductLink};

            gvGroups.DataSource = query;
            gvGroups.DataBind();
        }


The real issue here is how the GridView control handles incoming data, I did as other have done and go sidetracked thinking the problem is with Entity Framework and the returning object set.


Other Solutions


I've seen some crazy stuff to get passed this issue such using cloning or translational interfaces with various code that changes the collection coming in. This is far too complex and defeats the purpose of using EF in the first place which allows for model changes with minimum code changes. I will be using a mix of Linq and Templates where I think the best will suit.