[MVC 3] MvcImage Project–How did i do Thumbnail Support?


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:

https://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

[MVC3] ViewModel Binding To Controller–POST Methods


Now we should be writing strongly typed Views based on our Models. Now in some cases you may want to use the ViewModel pattern, which allows you to create a View based on:

 

a) One Model and some additional properties

b) Two or more combined Models

c) Two or more combined Models plus some additional properties.

These ViewModel classes would sit in a folder structure, you guessed it, ViewModels in your Visual Studio Project.An example would be my RegisterViewModel i have created:

 

 

public class RegisterViewModel {
    public RegisterModel RegisterModel { get; set; }
    public string UniqueKey { get; set; }
    public bool Thumbnail { get; set; }

}

 

 

So this is quite straight forward really. I have a RegisterModel which is the MVC default Membership model for handling when a new user Registers. I have a property called UniqueKey which will hold the of a created user, and a Boolean field Thumbnail which will be set to true if he want thumbnail images or false if we don’t.

So this is nice a simple then. So what this means that in our GET Action controller for register we can setup the required values. In this instance we only set the Thumbnail property:

 

// GET: /Account/Register public ActionResult Register()
{
    var viewModel = new RegisterViewModel {
        Thumbnail = true };

    return View(viewModel);
}

 

 

Then in our Review View, we need to declare our ViewModel instead of our RegisterModel, plus all our Model fields and the additional properties are parsed:

 

@model MvcImage.ViewModels.RegisterViewModel @using MvcImage.Models
@using Bradaz.OpenSource.MVC
@using Bradaz.OpenSource.MVC.HTMLHelpers

@{     ViewBag.Title = "Register";
} @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.ValidationSummary(true, "Account creation was unsuccessful. Please correct the errors and try again.")
    <div> <fieldset> <legend>Account Information</legend> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.UserName)
            </div> <div class="editor-field"> @Html.TextBoxFor(model => model.RegisterModel.UserName)
                @Html.ValidationMessageFor(model => model.RegisterModel.UserName)
            </div> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.Email)
            </div> <div class="editor-field"> @Html.TextBoxFor(model => model.RegisterModel.Email)
                @Html.ValidationMessageFor(model => model.RegisterModel.Email)
            </div> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.Password)
            </div> <div class="editor-field"> @Html.PasswordFor(model => model.RegisterModel.Password)
                @Html.ValidationMessageFor(model => model.RegisterModel.Password)
            </div> <div class="editor-label"> @Html.LabelFor(model => model.RegisterModel.ConfirmPassword)
            </div> <div class="editor-field"> @Html.PasswordFor(model => model.RegisterModel.ConfirmPassword)
                @Html.ValidationMessageFor(model => model.RegisterModel.ConfirmPassword)
            </div> <div class="editor-label"> @Html.Label("Avatar")
            </div> <div class="editor-field"> @Html.UploadImageFor(model => model.UniqueKey,thumbnail:true)
            </div> <div class="editor-field"> @Html.HiddenFor(model => model.Thumbnail)
            </div> <p> <input type="submit" value="Register" /> </p> </fieldset> </div> }

 

NB: I have changed my UploadImageFor HTML Helper to include Thumbnail support. This will be documented separately and released under my Open Source MvcImage Project.

So this all sounds simple right? Well you may get into a bit of a pickle (like me) when handling the POST Register ActionMethod. This would be peoples first attempts, which are both wrong:

Attempt 1

[HttpPost]
public ActionResult Register(RegisterModel registermodel,
    HttpPostedFileBase imageLoad2)
{
    ///---Code something }

Attempt 2

[HttpPost]
public ActionResult Register(RegisterViewModel registerviewmodel,
    HttpPostedFileBase imageLoad2)
{
    ///---Code something }

 

These are wrong if you wish to access the additional properties and the ResgiterModel within the RegisterViewModel. Let me explain:

 

Attempt 1 – You would be able to access the RegisterModel properties but not the Thumbnail property.

 

Attempt 2 – Your RegisterModel would be null and you wont be able to access the Thumbnail.

 

After these attempts most people scratch their head, curse, then head over to ASP.NET/FORUMS or stackoverflow for an answer. Well it took me a lot of scratching, googling and forum posting (plus nagging my poor friend Shawson!) to find the answers. The answer lies in how MVC binds the Models. The best place to start is in Fiddler, which is a free application for debugging HTTP and is very useful. Here is a glimpse of the form POST when i hit the Register button on my view:

 

 

Capture_Blog

 

As you can see our RegisterModel properties are prefixed with “RegisterModel” and our Thumbnail is just Thumbnail. So if you apply the MVC Binding on this, in the two attempts above, in the first attempt, MVC recognises RegisterModel as these have been binded from the HTTP post data, so are available. Thumbnail though isn’t available in the RegisterModel declaration in the class so this is ignored. In the 2nd attempt there is no RegisterViewModel in the POST so the binding will fail, thus nothing is available and you will get null fields.

 

The answer is the following:

[HttpPost]
public ActionResult Register(bool Thumbnail, 
[Bind(Prefix = "RegisterModel")]RegisterModel registermodel,
    HttpPostedFileBase imageLoad2)
{
    ///-Code something }

 

So here we declare Thumbnail as its own separate parameter and Bind the RegisterModel with a Prefix “RegisterModel” using the BindAttribute.  This allows us toaccess our RegisterViewModel properties AND Models within our controller quite happily.

I hope this helps you when using ViewModels.

Enhanced by Zemanta

[MVC 3] Entity Framework and Serializing JSON–Circular References


ADO.NET Entity Framework stack

Image via Wikipedia

I’m at the moment trying to write a light-weight AJAX control for my home page, which displays New Properties which have been added, aptly named WhatsNew.

The issue i had was the following error which i inspected in Fiddler when i didn’t have any JSON returned to my view:

A circular reference was detected while serializing an object type of “System.Data.Metadata.Edm.Properties”

A bit of googling, and it seems the JavaScriptSerializer has gets it knickers in a twist trying to traverse your Entity Framework objects which have relationships.

There are many options, but the one i took was to make the JSON as lightweight as possible and pass a ViewModel with only and not Entity Framework at objects at all. I build the view model based on data from the Entity Framework.

Here is my viewModel – the List was my old implementation which was causing the problems.

This will be returned as a JSON ActionResult to my view.  Bingo – I’ve fixed the issue! Smile

public class WhatsNewViewModel {
    //public List<PropertyViewModel> Properties { get; set; } 
 public string UniqiueKey { get; set; }
    public string Area { get; set; }
    public string PropertyType { get; set; }
}

Enhanced by Zemanta

[MVC 3] ObjectSet Extension Method–CountAll


I find myself when using Entity Framework, before accessing any of Objects i have various try/catch methods, due to the amount of exceptions that “bubble” up.

 

Now before i try and access my ObjectSet’s within my ObjectContext i usually make sure there are objects there to begin with. I usually do something like:

 

 

if ((db.RoomTypes.Count(i => i.Type_Code == room.Type)) > 0)
{
    roomType = db.RoomTypes.SingleOrDefault(i => i.Type_Code == room.Type);
    r.RoomType = roomType.Type_Name;

}

 

 

Now this is fine for when I’m trying to select against say, an id. This doesn’t work when i want to return all records from my ObjectSet. So i have come up with an extension method to the said ObjectSet:

        #region CountAll
        public static int CountAll<TEntity>(this ObjectSet<TEntity> objectSet) 
where TEntity : class { int _count = 0; foreach (var ObjectSet in objectSet) { if (objectSet != null) { _count++; } } return _count; } #endregion

 

 

Take note of the TEntity class constraint. Some little background. When you create your EF classes based on your database, there are various classes that are created.

Here is another example, db.Rooms being my ObjectContext:

 

Room room = db.Rooms.SingleOrDefault(r => r.Unique_Key == id);

 

If you look at the definition of Rooms (ObjectSet – a table), this is the collection of Room:

 

public ObjectSet<Room> Rooms
 {
     get
     {
         if ((_Rooms == null))
         {
             _Rooms = base.CreateObjectSet<Room>("Rooms");
         }
         return _Rooms;
     }
 }

 

If you look at the definition of Room generic parameter, represents a row in the table Room:

public partial class Room : EntityObject

 

So we know in essence the ObjectSet is the table and EntityObject is a row. So now we know this you can interrogate the definition of ObjectSet:

public class ObjectSet<TEntity> : ObjectQuery<TEntity>, IObjectSet<TEntity>,
IQueryable<TEntity>,IEnumerable<TEntity>, IQueryable, IEnumerable
where TEntity : class { }


So there’s your TEntity you need for your extension method constraint. Now you know why we have done what we have done for the Extension Method, here is how to use it:

try
{
  if ((db.Properties.CountAll()) > 0)
  {
     //--code here
      
  }
}

So we can CountAll the ObjectsSets (Rows) within Properties, if there are more than 0, the do something (Like query the data).

Anyway, i hope this helps.

 

 

 

Enhanced by Zemanta

[MVC 3] ModelState ModelErrorCollection Debug–Extension Method


In the last i described a way of using a static method to obtain which model field is causing your ModelState to be invalid. My good friend Shaw Young said that a Extension Method would be another way of doing this. And thinking of it, it makes more sense and a better and cleaner implementation.

So here is that piece of code again:

if (ModelState.IsValid)
{

  //—Your code here
}
else {
    Debug.ModelStateErrors( ModelState);
    ModelState.ModelStateErrors();
}

So now instead of a Debug.ModelStateErrors, there is a Extension Method ModelStateErrors referenced by the object itself.

And here is the code:

public static void ModelStateErrors (this ModelStateDictionary modelState)
{
    var errors = modelState.Where(a => a.Value.Errors.Count > 0)
        .Select(b => new { b.Key, b.Value.Errors })
        .ToArray();

    foreach (var modelStateErrors in errors)
    {
        int count = modelStateErrors.Errors.Count();
        System.Diagnostics.Debug.WriteLine("...Errored When Binding.",        
        modelStateErrors.Key.ToString());

    }

}

There you are! The only difference is the implementation of the static method itself. The actual code is the same.

Enhanced by Zemanta

[MVC 3] ModelState ModelErrorCollection Debug


UPDATE: Dan Kendall kindly done a gist of the code which can be found here if you need it which is related to this post:

https://gist.github.com/3699804

We all use the ModelState.IsValid before we commit any changes to our Entity Framework ObjectContext to update our data source. But what is ModelState.IsValid? Well basically ModelState says…

“Yes, all models have binded successfully from the view”

But what happens when we the ModelState is NOT valid. Well, I  have a little static method So you can write to the Output window to see what the property name is, so you can fix it quickly.

if (ModelState.IsValid)
{
   ///—- Your code.
}
else {
    Debug.ModelStateErrors( ModelState);

}

Now my method, Debug.ModelStateErrors consists of an Anon Type which i use a LINQ to abstract the ModelErrorCollection from the ModelState. I then print out the Key from the Dictionary to the Output window.  Example on the 2nd image which is property.PropertyType is the model field.

#if DEBUG
       /// <summary> 
 /// Output the properties which are causing the issues when 
 /// the model is binding. 
 /// </summary> public static void ModelStateErrors (ModelStateDictionary modelState)
        {
            var errors = modelState.Where(a => a.Value.Errors.Count > 0)
                .Select(b => new { b.Key, b.Value.Errors })
                .ToArray();

            foreach (var modelStateErrors in errors)
            {
                 System.Diagnostics.Debug.WriteLine("...Errored When Binding.",                                  modelStateErrors.Key.ToString());

            }

        }
#endif #endregion 

Capture

Enhanced by Zemanta

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


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