Dealing with Enum in MVC 5.1

Enum
If you are reading this post, I bet you at least once went in stackoverflow with the hope to find out a solution of the problem that you were stuck with. Other day, while I was surfing #stackoverflow, I have noticed that a good number of developers often faced difficulty while dealing with enum in Asp.Net MVC, their fair amount of code is just not enough to make their enum turn into dropdown list. And this problem was there for years. Obviously, genius developers did not seat idle. They figure out different approaches to deal with this issue and most often these  approaches is not fairly simple to follow.

With the release of MVC 5.1  in Feb 27, 2014 there finally came enum support it View. The old school approach still applicable and useable and plenty of such resources could be found on the web  but  it is not recommended as the new way is much simpler. I am not going to discuss the old way rather I will focus the new way of achieving our purpose.

Software Requirements
You will be in need of any of the following –

  • Visual Studio 2012: Download ASP.NET and Web Tools 2013.1 for Visual Studio 2012.
  • Visual Studio 2013: Download Visual Studio 2013 Update 1. This update is needed for editing ASP.NET MVC 5.1 Razor Views.

Now, you have the tools, lets get started by creating an Asp.Net Web Application. To demonstrate the enum support we will build a simple student registration form where we expect that a dropdown box will show the Gender, Country from where people can select their desired information.
Initially, for the demonstration purpose I have created the following architecture. You can download the entire source file from here.

EnumDemo_Project Hierarchy

Gender enum is fairly simple, it consists with Male and Female and it is defined as

public enum Gender
{
     Male,
     Female
}

5 countries are added in the enum list. The Country enum is defined as

    public enum Country
    {
        Australia,
        Bangladesh,
        England,
        France,
        Usa
    }

The student entity [POCO (Plain Old C# object) class] looks like this

    public class Student
    {
        public int Id { get; set; }
        public String Name { get; set; }
        public Gender Gender { get; set; }
        public Country Country { get; set; }
    }

Right now, If we just rely on scaffolding to do the hard work for you and generate some view then you will end up like this

If you never deal with enum before this view probably freaks you out, you probably found yourself saying –

hey wait a minute, scaffolding is not working! its not rendering the view properly

Right now you probably want to inspect the scaffolding generated code for the view and here it is-

    <div class="form-group">
            @Html.LabelFor(model => model.Gender, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Gender)
                @Html.ValidationMessageFor(model => model.Gender)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Country, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Country)
                @Html.ValidationMessageFor(model => model.Country)
            </div>
        </div>

And everything seems fairly fine. right? There you have the editor extension EditorFor() on the right position and data was also passed properly. Then whats wrong? Well, actually EditorFor() does not know how to render an Enum, so it uses its default settings and render a textbox for the enum.

Don’t freak out! We will deal with this issue within a minute. But first update Microsoft ASP.NET MVC from NuGet. Do it otherwise you would not find the extension method that we are going to use to solve our problem.

Managing NuGet Packages

Managing NuGet Packages

Once you are done change the markup as follows –

     @*@Html.EditorFor(model => model.Gender)*@

     @Html.EnumDropDownListFor(model=>model.Gender)

Here, we are using the new EnumDropDownListFor() extension method. Implementation is same as EditorFor() extension.
HURRAH! just one line of code saves our live.

EnumDemo_4

Now, lets extend the scenario bit. What if we want to show special character/symbol spaces in the enumdropdowlist. We know Enum does not support spaces/special characters so what we can do is to use Display Attribute. Here in the following scenario we are using [Display(Name=”United States”)] to show “United States” instead of showing “Usa”.


   public enum Country
    {
        Australia,
        Bangladesh,
        England,
        France,
        [Display(Name="United States")]
        Usa
    }

and here is the view. You can see its showing “United States” in the EnumDropDownList.

EnumDemo_5

Note: At this point if you just create a new student selecting the values from EnumDropDownList , when you revisit to see what you have just saved you will find that instead of displaying “United States” , its showing “Usa” in the list. If you don’t get my point yet have a look on the following image.

And that something what we probably don’t want to display. To deal with we can use some kind of templating mechanism.

We are going to provide the enum template, and its important  that we put it in the right folder since we want all Enums to use this by default.
Here is the locaition /Shared/DisplayTemplates where we are going to create the partial view for display scenario and make sure to name it as enum.cshtml . File hierarchy is shown in the following image –

EnumDemo_8

Inside Enum.cshtml copy the following code there and you are done !

@model Enum

@if (EnumHelper.IsValidForEnumHelper(ViewData.ModelMetadata))
{
    // Display Enum using same names (from [Display] attributes) as in editors
    string displayName = null;
    foreach (SelectListItem item in EnumHelper.GetSelectList(ViewData.ModelMetadata, (Enum)Model))
    {
        if (item.Selected)
        {
            displayName = item.Text ?? item.Value;
        }
    }

    // Handle the unexpected case that nothing is selected
    if (String.IsNullOrEmpty(displayName))
    {
        if (Model == null)
        {
            displayName = String.Empty;
        }
        else
        {
            displayName = Model.ToString();
        }
    }

    @Html.DisplayTextFor(model => displayName)
}
else
{
    // This Enum type is not supported.  Fall back to the text.
    @Html.DisplayTextFor(model => model)
}

Run the application once again and you will find your long desired solution on the screen 🙂

If you can remember, we made some changes in the code that was generated by scaffholding. If you are dealing with a large application where almost each view deals with Enum values then probably it would be hard and time consuming to make changes on several places and such scenario I prefer to write down a EditorTemplates so that the scaffholding generated code can run smoothly without any kind of modification.

Here is the locaition /Shared/EditorTemplates where we are going to create the partial view for display scenario and make sure to name it as enum.cshtml . File hierarchy is shown in the following image –
EnumDemo_9

Inside Enum.cshtml copy the following code and you are good to go with the scaffholding generated code.

@model Enum

@if (EnumHelper.IsValidForEnumHelper(ViewData.ModelMetadata))
{
    @Html.EnumDropDownListFor(model => model, htmlAttributes: new { @class = "form-control" })
}
else
{
    @Html.TextBoxFor(model => model, htmlAttributes: new { @class = "form-control" })
}

Thats really everything that you should know to deal with Enum in MVC5. Hope you find this post helpful.

Additional Resource:

[Attribute] Routing in ASP.Net MVC 5 / WebAPI 2 (Part II)

https://learnwithshahriar.wordpress.com/

In my last post I have discussed about the attribute routing and I believe that was quite enough to get started with the new featured offered both by MVC5 and WebAPI 2. In this post I will keep digging the same topic and together we will explore how we could impose certain constraints to get more control over routing.

Imposing Constraints

Imposing Constraints restrict how the parameters in the route template are matched. In the last post, I have shown how to search certain employee by their id. Now, lets say we also want the end user to search employee by Name. Also assume different view have be render in that case.

home/3 <–render “View A”
home/shahriar <–render “View B”

What we can do is to add another ActionResult which receives a string parameter. But only doing so does not solve our problem. We have to explicitly define and impose certain constraints to let the routing framwork work perfectly.

Notice the route carefully. In both cases routing framework hits the home controller and then based on the action parameter received it renders different view. If we just don’t explicitly tell the routing framework to impose certain constraints it just can’t fly you(the viewer) to the the appropriate action.

The general syntax for imposing certain constraint is {parameter:constraint}.

public class HomeController : Controller
{
      [Route("home/{id? : int}")]
      public ActionResult Employee(int id)
      {
           //Logic goes here
      }

      [Route("home/{name}")]
      public ActionResult Employee(string name)
      {
           //Logic goes here
      }
}

Here, the first route will only be selected if the “id” segment of the URI is an integer. Otherwise, the second route will be chosen. (“?”) symbol specifies that this parameter is Optional.

Ken Egozi posted a nice article regarding Attribute routing and there he posted Complete list of constraints with some example that I have added below:

Constraint Description Example
alpha Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {x:alpha}
bool Matches a Boolean value. {x:bool}
datetime Matches a DateTime value. {x:datetime}
decimal Matches a decimal value. {x:decimal}
double Matches a 64-bit floating-point value. {x:double}
float Matches a 32-bit floating-point value. {x:float}
guid Matches a GUID value. {x:guid}
int Matches a 32-bit integer value. {x:int}
length Matches a string with the specified length or within a specified range of lengths. {x:length(6)}
{x:length(1,20)}
long Matches a 64-bit integer value. {x:long}
max Matches an integer with a maximum value. {x:max(10)}
maxlength Matches a string with a maximum length. {x:maxlength(10)}
min Matches an integer with a minimum value. {x:min(10)}
minlength Matches a string with a minimum length. {x:minlength(10)}
range Matches an integer within a range of values. {x:range(10,50)}
regex Matches a regular expression. {x:regex(^\d{3}-\d{3}-\d{4}$)}

Pay Close Attention : Some of the constraints, such as “min”, “maxlength”, “minlength” take arguments in parentheses.

To apply multiple constraints to a parameter, separated each constraint by a colon.

Rrestricting specific actions to specific HTTP verbs:
Existing attributes (e.g. [HttpGet], [HttpPost] and [AcceptVerbs]) are allowed 🙂

And this brings to end of this topic. I have tried to cover up almost all the relevant major things regarding attribute routing but yet there has some mini tips and tricks that hasn’t been discussed here. I leave up those those tiny things for you. Hope you could figure out those tiny things by yourself. Happy coding 🙂

Additional Resources:

  1. Maarten Balliauw’s blog
  2. Attribute routing in Web api 2

 

[Attribute] Routing in ASP.Net MVC 5 / WebAPI 2 (Part I)

https://learnwithshahriar.wordpress.com/

In the last post I have discussed how the routing framework actually works, in this post I will discuss about one of the coolest latest feature offered by MVC5, WebAPI2 and its called “Attribute Routing”. Under the hood, attribute routing still maintains the same mechanism of routing framework.

So, right now you might be thinking if new routing feature(attribute routing) also use the same routing mechanism under the hood then where the twist actually came in the game? Well, over the last few years while developing large enterprise web apps it was found that as the project gets bigger and special cases accumulate it becomes hard to keep track of all those routes in a single file. Things get bit messy while developers have to write code to apply complex constraint. In most cases, they use a custom constraint by implementing IRouteConstraint and defining the custom logic in the Match method – if it returns true, the route is a match.

public interface IRouteConstraint
{
    bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
}

The problem with the Convention-based Routing is that since the routes are physically separated from the controllers they apply to, it often take some detective work to understand the relationships. With the hope to give a better development experience of this issues #Microsoft has adopted “attribute routing” feature in Asp.Net MVC5 and WebApi2 from Tim McCall. Continue reading

Uploading Image

So, last night a friends of mine raised a question on the Asp.Net MVC – Bangladesh group how he can upload images in ASP.Net MVC. Instantly, two answers appears in my mind as its solution. He can either use string or as byte[] to Upload, Save, Retrieve the image.
I also pointed the following code snipped :

Assuming you have stored your image in db as byte[] :

byte[] data = (byte[])ds.Tables[0].Rows[0][0];

MemoryStream strm = new MemoryStream();

strm.Write(data, 0, data.Length);

strm.Position = 0;

System.Drawing.Image img = System.Drawing.Image.FromStream(strm);

BitmapImage bi = new BitmapImage();

bi.BeginInit();

MemoryStream ms = new MemoryStream();

img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);

ms.Seek(0, SeekOrigin.Begin);

bi.StreamSource = ms;

bi.EndInit();

imagebox.Source = bi;

Seeing this approach Alim Ul Karim vai pointed on the performance issue.
He left a very constructive suggestion, he said-

“If you are not using NOSQL this is not a good practice. To save the image use file system. Just upload the image to a file location and display it with the URL. and if you save a image in database it always converts it to binary which is very overkill for server where you could easily use the browser to work it for you. Simple things matters. “

So, keeping his suggestion in mind here it can be done –
For this demonstration at first I have created a model named “Picture.cs”. Inside which the following property is added :

public class Picture 
{
     Public HttpPostedFileBase File {get; set;}
}

Then you have to add a simple upload form in the view.

@using(Html.BeginForm(null, null, FormMethod.Post, new { enctype="multipart/form-data"}))
<table>
     <tr>
         <td> <input type="file" name="File" id="File" /> </td>
     </tr>

     <tr>
         <td> <input type="submit" name="submit" value="upload" /> </td>
     </tr>
</table>

Now in the desired controler page we have to post the content.

[HttpPost]
public ActionResult Index(Picture pic)
{
   //check for contents
   if(pic.File.ContentLength>0)
   {
        var fileName=Path.GetFileName(pic.File.FileName);
        var path=Path.Combine(Server.MapPath("~/Content/images"), fileName);
        pic.File.SaveAs(path);
   }
   return RedirectToAction("Index");
}

Run it! and You are good to GO! Retrieving images is fairly simple, so I am not demonstrating it here 🙂

If you want to upload multiple image then just change picture property/model as IEnumerable and add multiple in the form.

Understanding the Routing framework

Asp.Net MVC Routing framework is the core of every Asp.Net MVC request. This means whenever an end user hits for a particular url, the routing framework came in to play.

Routing framework is basically a pattern matching system. The Routing System uses a series of rules listed in a routing table [App_Start\RouteConfig.cs] to determine which Controller and Action are executed in response to a request intercepted by the routing engine. When a URL is found to match a pattern, the routing engine attempts to match the text in the placeholders. If it cannot find a match, it returns a 404 HTTP Status Code. This whole concept is shown in the following image :

https://learnwithshahriar.wordpress.com/

Please note, Each routing rule contains placeholders that can match a controller, an action, and any number of variables. Variable data can be passed to the request handler without requiring a query string.

Now, Lets have a look on RouteConfig.cs file which is located under “App_Start” folder.  Routing engine basically matches the pattern of the received url against the RouteCollection registered in the following file.

 public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }

As you can see, the default code snipped that ships with the MVC template does not only used for providing a Name and URL  pattern but also defines a set of default parameter. In case,  the routing engine does not find a perfect match it starts filling values that are not provided by the URL from the default values.

If you want to map new routes you simply add your desired routes in this file(RouteConfig.cs). You add the route to the application by adding the Route object to the static Routes property of the RouteTable class. The Routes property is a RouteCollection object that stores all the routes for the application.

Note:

When adding new routes ALWAYS KEEP IN MIND that you have to add specific route on the top and followed by more generic route in the end. Otherwise your web app will never receive proper routing.

In the next post I will discuss about “Attribute Routing” . Till then have fun !