Archive

Posts Tagged ‘ObjectContext’

[MVC3] ObjectContext.SaveChanges() — How to Use


ADO.NET Entity Framework stack

ADO.NET Entity Framework stack (Photo credit: Wikipedia)

Recently i have joined a Facebook Group The Dev circle, which is a group of likeminded developers who wish to learn, code and grok!

 

One member is starting out using Microsoft’s ORM Entity Framework (EF) and wanted to know how to update his Entities. This is easy within EF, as there is an method called SaveChanges() which will do the following:

 

Persists all updates to the data source and resets change tracking in the object context.

So how do we use it? Well with all Data Connections we need to use a “using” statement and we also need to make sure we catch the required Exceptions detailed in the MSDN documentation, one in particular is OptmisticConcurrencyException which allows us to resolve any concurrency conflicts based on parameters we pass to another method Refresh().

 

Take for e.g. a controller i have in a MVC3 Project which employs EF. Here is the code:

 

// POST: /LedgerUser/Create[Bind(Prefix = "LedgerUser")]

 [HttpPost]
 public ActionResult Create(bool Thumbnail,LedgerUser LedgerUser, 
HttpPostedFileBase imageLoad2) { var Avatar = new Accounts.Models.Image(); //--Handle the image first. if (imageLoad2 != null) { using (System.Drawing.Image img =
System.Drawing.Image.FromStream(imageLoad2.InputStream)) { //--Initialise the size of the array byte[] file = new byte[imageLoad2.InputStream.Length]; //--Create a new BinaryReader and set the InputStream for the
//--Images InputStream to the //--beginning, as we create the img using a stream.
BinaryReader reader = new BinaryReader(imageLoad2.InputStream); imageLoad2.InputStream.Seek(0, SeekOrigin.Begin); //--Load the image binary. file = reader.ReadBytes((int)imageLoad2.InputStream.Length); //--Create a new image to be added to the database Avatar.id = Guid.NewGuid(); Avatar.TableLink = LedgerUser.id; Avatar.RecordStatus = " "; Avatar.FileSize = imageLoad2.ContentLength; Avatar.FileName = imageLoad2.FileName; Avatar.FileSource = file; Avatar.FileContentType = imageLoad2.ContentType; Avatar.FileHeight = img.Height; Avatar.FileWidth = img.Width; Avatar.CreatedDate = DateTime.Now; //-- Now we create the thumbnail and save it. if (Thumbnail == true) { byte[] thumbnail =
Images.CreateThumbnailToByte(imageLoad2.InputStream, 100, 100); Avatar.ThumbnailSource = thumbnail; Avatar.ThumbnailFileSize = thumbnail.Length; Avatar.ThumbnailContentType =
Files.GetContentType(imageLoad2.FileName); Avatar.ThumbnailHeight = Images.FromByteHeight(thumbnail); Avatar.ThumbnailWidth = Images.FromByteWidth(thumbnail); } else { byte[] thumbnail = new byte[0]; Avatar.ThumbnailSource = thumbnail; Avatar.ThumbnailFileSize = 0; Avatar.ThumbnailContentType = " "; Avatar.ThumbnailHeight = 0; Avatar.ThumbnailWidth = 0; } } } if (!ModelState.IsValid) { ModelState.ModelStateErrors(); } if (ModelState.IsValid) { using (AccountsEntities context = new AccountsEntities()) { try { //--Save the LedgerUser context.LedgerUsers.AddObject(LedgerUser); context.SaveChanges(); } catch (OptimisticConcurrencyException) { context.Refresh(RefreshMode.ClientWins, LedgerUser); context.SaveChanges(); Console.WriteLine
("OptimisticConcurrencyException " + "handled and changes saved"); } catch (UpdateException ex) { Console.WriteLine(ex.ToString()); } try { //--Save the Image context.Images.AddObject(Avatar); context.SaveChanges(); return RedirectToAction("Index", "Home"); } catch (OptimisticConcurrencyException) { context.Refresh(RefreshMode.ClientWins, Avatar); context.SaveChanges(); Console.WriteLine
("OptimisticConcurrencyException Avatar" + "handled and changes saved"); } catch (UpdateException ex) { Console.WriteLine(ex.ToString() + " 2 "); } } } var userTypes = new SelectList(db.UserTypes, "id", "Description"); var ledgerUser = new LedgerUser() { id = LedgerUser.id, RecordStatus = LedgerUser.RecordStatus, CreatedDate = LedgerUser.CreatedDate, DateOfBirth = LedgerUser.DateOfBirth }; var viewModel = new LedgerUserViewModel() { UserTypes = userTypes, LedgerUser = ledgerUser }; return View(viewModel); }

 

So what is this controller doing:

  • Create new Ledger Users and their Avatar Images.
  • These are persisted using an AccountsEntities ObjectContext
  • I only save the the LedgerUser and Avatar if the ModelState.IsValid . I then try and SaveChanges.
  • I catch the required Exceptions
  • Return the viewModel if the SaveChanges() fails.

There is a good MSDN Article How to: Manage Data Concurrency in the Object Context to read here.

This approach should be used for most EF persistence.

 

 

 

Enhanced by Zemanta

[MVC 3] Entity Framework 4–Disposing ObjectContext–TIP

August 30, 2011 1 comment

ADO.NET Entity Framework stack

Image via Wikipedia

The Entity Framework can throw a lot of Exceptions depending on what you are trying to achieve. We are lucky that the default CRUD controller template includes how to use EF during a Create/Delete/Edit scenario, but sometimes (a lot of the time in my case), when you first begin using EF, the exceptions keep on coming.

 

A lot of people use either try/finally or using statements when using their ObjectContext. You will find a lot of people will have something like (db being the ObjectContext):

 

    finally {
        if (db != null)
 db.Dispose();
    }

}

 

 

Now this is fine, but you need to handle this correctly, because if you saves changes after this, you could throw an InvalidOperationException, with the following text:

 

The object cannot be attached because it is already in the object context. An object can only be reattached when it is in an unchanged state.

OR

 

 

ObjectDisposedException

 

In Entity Framework, entities are attached to the EF context and changes are “tracked” within the framework. So you need to make sure you are not trying to Attach or Detach your entities incorrectly. Remember:

 

AddObjectUsed solely to add new entities only (Does not deal with EntityState.Modified items) to the context to be added to the data source.

AttachUsed to update an object that already exists in the context.

 

Now if we Dispose the object context incorrectly, then this could cause the above exceptions to be thrown later on when trying to update the data sources after the finally statement which includes the Dispose.

 

Now the way i get around this is use partial to extend my EF ObjectContext class (in my case EduEntities), and override the Dispose method so i only Detach the Unchanged Entities.

 

public partial class EduEntities {
    protected override void Dispose(bool disposing)
    {

        foreach (var entry in ObjectStateManager.GetObjectStateEntries       (EntityState.Unchanged))
        {

            Detach(entry.Entity);
        }

        base.Dispose(disposing);

    }

}

 

This means that the tracking on my changed objects are not lost.

Enhanced by Zemanta
Follow

Get every new post delivered to your Inbox.

Join 269 other followers

%d bloggers like this: