Archive

Posts Tagged ‘tutorial’

[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] MvcImage Project–How did i do Thumbnail Support?

March 24, 2012 4 comments

Before reading the following, please read up on the tutorial posts listed here to get up-to speed on how i have accomplished Image handling using MVC so far, as i wont be going into detail on the code i have updated changed, only new code:

http://garfbradazweb.wordpress.com/2012/02/26/mvc-3-mvcimage-project-its-alive/

Also, before i continue, you can download and use the code on my MvcImage Codeplex Project home page.

So firstly i updated the AjaxSubmit and ImageLoad controllers. I have updated them to segregate the Image and Thumbnail byte arrays within the Session. The AjaxSubmit controller references my new Extension method Images.CreateThumbnailToByte to create the Thumbnail. I will discuss this in a moment. Remember to read up in the previous tutorials on how i use these withn a JQuery Image Preview Plugin.

So the AjaxSubmit Code is:

       [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult AjaxSubmit(int id)
        {

            Session["Image.ContentLength"] = Request.Files[0].ContentLength;
            Session["Image.ContentType"] = Request.Files[0].ContentType;
            byte[] b = new byte[Request.Files[0].ContentLength];
            Request.Files[0].InputStream.Read            (b, 0, Request.Files[0].ContentLength);
            Session["Image.ContentStream"] = b;

            if (id > 0)
            {
                byte[] thumbnail = Images.CreateThumbnailToByte(Request.Files[0].InputStream, 100, 100);

                Session["Thumbnail.ContentLength"] = thumbnail.Length;
                Session["Thumbnail.ContentType"] = Request.Files[0].ContentType;

                byte[] c = new byte[thumbnail.Length];
                Request.Files[0].InputStream.Read(c, 0,                 Request.Files[0].ContentLength);
                Session["Thumbnail.ContentStream"] = thumbnail;

            }

            return Content(Request.Files[0].ContentType + ";" + Request.Files[0].ContentLength);
        }

And the ImageLoad Controller Code – the id parameter is used to determine if to Respond with the Thumbnail binary or the Image binary.

       public ActionResult ImageLoad(int id)
        {
            if(id == 0)
            {
                byte[] b = (byte[])Session["Image.ContentStream"];
                int length = (int)Session["Image.ContentLength"];
                string type = (string)Session["Image.ContentType"];
                Response.Buffer = true;
                Response.Charset = "";
                Response.Cache.SetCacheability(HttpCacheability.NoCache);
                Response.ContentType = type;
                Response.BinaryWrite(b);
                Response.Flush();
                Response.End();
                Session["Image.ContentLength"] = null;
                Session["Image.ContentType"] = null;
                Session["Image.ContentStream"] = null;
            }

            //--The following is the Thumnbail id.

            if (id == 1)
            {
                byte[] b = (byte[])Session["Thumbnail.ContentStream"];
                int length = (int)Session["Thumbnail.ContentLength"];
                string type = (string)Session["Thumbnail.ContentType"];
                Response.Buffer = true;
                Response.Charset = "";
                Response.Cache.SetCacheability(HttpCacheability.NoCache);
                Response.ContentType = type;
                Response.BinaryWrite(b);
                Response.Flush();
                Response.End();
                Session["Thumbnail.ContentLength"] = null;
                Session["Thumbnail.ContentType"] = null;
                Session["Thumbnail.ContentStream"] = null;
            }
            return Content("");
        }

It is not the nicest code but it is there to show the logic behind what I’m trying to do. I’m following the same pattern as before but this time handling two byte arrays.

So lets resume talking about the following piece of code:

byte[] thumbnail = Images.CreateThumbnailToByte(Request.Files[0].InputStream, 100, 100);

So parameter 1 is the Stream of the Image, and the 2nd and 3rd parameters are the maximum height and width of the new resized Thumbnail Image. So using inspiration from Nathanael Jones (but not using it all as i didn’t have time), i maintain the aspect ratio of the image and create a new height and width by scaling the image properly. Then again based on advice from Nathanael’s article we create a new GDI drawing surface and create the Thumbnail Image. Then lastly we convert it back to a binary stream and return the byte array.

Here is the code:

        /// <summary>
        /// This method creates a Thumbnail Image and and scales it. /// It returns a byte array
        /// to be used.
        /// </summary>
        /// <param name="stream">Image Stream</param>
        /// <param name="maxHeight">Max Height (Used to scale the image</param>
        /// <param name="maxWidth">Max Width (Used to scale the image)</param>
        /// <returns>Scaled thumbail image byte array.</returns>
        public static byte[] CreateThumbnailToByte(Stream stream,         double maxHeight, double maxWidth)
        {
            int newHeight;
            int newWidth;
            double aspectRatio = 0;
            double boxRatio = 0;
            double scale = 0;

            Stream imageStream = new MemoryStream();
            Image originalImage;

            Streams.RewindStream(ref stream);
            using (originalImage = Image.FromStream(stream))
            {
                //--We need too maintain the aspect ratio on the image.
                aspectRatio = originalImage.Width / originalImage.Height;
                boxRatio = maxWidth / maxHeight;

                if (boxRatio > aspectRatio)
                {
                    scale = maxHeight / originalImage.Height;
                }
                else
                {
                    scale = maxWidth / originalImage.Width;
                }

                //--Scale the Original Images dimensions
                newHeight = (int)(originalImage.Height * scale);
                newWidth = (int)(originalImage.Width * scale);

                using (var bitmap = new Bitmap(newWidth, newHeight))

                //--Create a new GDI+ drawing surfance based on the original Image. 
 //--This methid allows us to alter
                //--it where necessary, based on advice from here. //--http://nathanaeljones.com/163/20-image-resizing -pitfalls/
                using (var graphics = Graphics.FromImage(bitmap))
                {
                    var rectangle = new Rectangle(0, 0, newWidth, newHeight);

                    graphics.CompositingQuality = CompositingQuality.HighQuality;
                    graphics.SmoothingMode = SmoothingMode.HighQuality;
                    graphics.InterpolationMode =                     InterpolationMode.HighQualityBicubic;
                    graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    graphics.DrawImage(originalImage, rectangle);

                    //--The same image to a new ImageStream so we can //--convert this to a byte array.
                    bitmap.Save(imageStream, originalImage.RawFormat);

                }

                byte[] thumbnail = new byte[imageStream.Length];
                BinaryReader reader = new BinaryReader(imageStream);
                imageStream.Seek(0, SeekOrigin.Begin);

                //--Load the image binary.
                thumbnail = reader.ReadBytes((int)imageStream.Length);
                return thumbnail;

            }        

       }

.

Now in the future i will look to extend this further to encompass all of Nathanael’s advice. So that’s it! Took a lot of work getting there, not just coding the solution but understanding it which is key.

The last thing i did was add more properties to my ImageModel to save the Thumbnail data separately from the main Image, so we don’t have to convert it each and every time we want to load the Thumbnail instead of the main Image binary.

So my new ImageModel class is now:

    /// <summary> 
    /// This class represents the table for Images and it necessary columns
    /// </summary>
    public class ImageModel : IImageModel
    {
        [Key]
        public Guid UniqueId { get; set; }
        [Required]
        public Guid TableLink { get; set; }
        public String RecordStatus { get; set; }
        [Date]
        public DateTime RecordCreatedDate { get; set; }
        [Date]
        public DateTime RecordAmendedDate { get; set; }
        public Byte[] Source { get; set; }
        public Int32 FileSize { get; set; }
        public String FileName { get; set; }
        [FileExtensions("png|jpg|jpeg|gif")]
        public String FileExtension { get; set; }
        public String ContentType { get; set; }
        public Int32 Height { get; set; }
        public Int32 Width { get; set; }

        //-- New in Alpha Release 0.0.2
        public Byte[] ThumbnailSource { get; set; }
        public Int32 ThumbnailFileSize { get; set; }
        public String ThumbnailContentType { get; set; }
        public Int32 ThumbnailHeight { get; set; }
        public Int32 ThumbnailWidth { get; set; }

    }
Enhanced by Zemanta
Follow

Get every new post delivered to your Inbox.

Join 244 other followers

%d bloggers like this: