Using ADO.NET Entity Framework classes with ASP.NET MVC

by Matthias Broschk August 15, 2009 00:05

The ASP.NET MVC framework provides an easy approach to create an ASP.NET application based on the Model-View-Controller pattern.  Without bringing any restrictions to your web applications (it supports existing ASP.NET features), it offers you the full benefits of the MVC pattern such as Test Driven Development.

The ADO.NET Entity Framework (EF) is an object-relational-mapping framework for ADO.NET. Although there has been some criticism and NHibernate is currently definitely far more powerful and more suitable for enterprise applications I believe the Entity Framework has a high potential. I am sure that the weaknesses will be dealt with in future versions, such as EF 2.0 with comes with .NET 4.0.

Since both technologies were developed by Microsoft they nicely integrate with each other. In this post will create a simple ASP.NET MVC application which uses EF classes. I recycled the product database from an earlier post. This is going to be a simple product list, for which I added a picture column to the product table. The Entity Data Model of the application is fairly dull, since there is only one entity and no relations, but this will do. 

DataTable

In order to write a controller it is first necessary to determine the required actions. Since I would like to see a list of products, there has to be some kind of index action. Additional actions for creating new products or editing exiting would be nice too. Other possibilities such as actions for viewing details or removing products could also be implemented. The following code sample shows the controller class and the first action (“Index”).

public class ProductsController : Controller
   {
       private TestDBEntities _entities = new TestDBEntities();

       //
       // GET: /Products/

       public ActionResult Index()
       {
           return View(_entities.ProductSet.ToList());
       }

Next we have to create a view for this specific action. If you select a strongly-typed view, the ASP.NET MVC frameworks creates an ready-to-use aspx page for the specific content. For example in this case it will create the ASP.NET/HTML code for a product list.

AddView

The following excerpt from the index view displays an altered version of the original. Since the product’s picture property is a byte array and this can’t be simply included in the page we will need an action “Image”.

<table>
       <tr>
           <th>ID</th>
           <th>Picture</th>
           <th>Name</th>
           <th>Price</th>
           <th>Year</th>
           <th></th>
       </tr>

   <% foreach (var item in Model) { %>

       <tr>
           <td>
               <%= Html.Encode(item.ID) %>
           </td>
           <td>
              <img alt="<%= Html.Encode(item.Name) %>" src='<%= this.Url.Action("Image", "Products") + "/" + item.ID.ToString()%>' width="64" height="64" />
           </td>
           <td>
               <%= Html.Encode(item.Name) %>
           </td>
           <td>
               <%= Html.Encode(String.Format("{0:F}", item.Price)) %>
           </td>
           <td>
               <%= Html.Encode(item.Year) %>
           </td>
            <td>
               <%= Html.ActionLink("Edit", "Edit", new { id=item.ID }) %>
           </td>
       </tr>

   <% } %>

   </table>

   <p>
       <%= Html.ActionLink("Create New", "Create") %>
   </p>

This action shows one of the nice things about ASP.NET MVC: Your actions can return all different kinds of content including images. The code is mainly self-explanatory. The product selection statement is a good example how EF nicely integrates with LINQ.

[AcceptVerbs(HttpVerbs.Get)]
public FileResult Image(int id)
{
    const string alternativePicturePath = @"/Content/question_mark.jpg";
	Product product = _entities.ProductSet.Where(k => k.ID == id).FirstOrDefault();

    MemoryStream stream;

    if (product != null && product.Picture != null)
    {
        stream = new MemoryStream(product.Picture);
    }
    else // If product cannot be found or product doesn't have a picture
    {
        stream = new MemoryStream();

        var path = Server.MapPath(alternativePicturePath);
        var image = new System.Drawing.Bitmap(path);

        image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
        stream.Seek(0, SeekOrigin.Begin);
    }

    return new FileStreamResult(stream, "image/jpeg");
}

The next source code line from the index view line shows how this action is called respectively how its URL is generated.

this.Url.Action("Image", "Products") + "/" + item.ID.ToString()

Now let’s have a look at the running application. Since the pictures have not been set yet we can only see the default images.

Screenshot1

Next we will implement the other actions. In order to create products we have to add two functions to the product controller: One for the initial page visit and one for the event that the user submits (posts) a “create product” form. Because we don’t require any initializations or data objects when the user first visits the page the first action method will be simple. The second one is only a little bit more complicated. It has an attribute which tells the framework that it will be called only by html post calls. The ID property has to be excluded because it must be set by the database and not by the application.

//
// GET: /Products/Create

public ActionResult Create()
{
    return View();
}

//
// POST: /Products/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "ID")]Product product)
{
    try
    {
        if (Request.Files.Count > 0)
        {
            product.Picture  = (new FileHandler()).uploadedFileToByteArray((HttpPostedFileBase)Request.Files[0]);
        }

        _entities.AddToProductSet(product);
        _entities.SaveChanges();

        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

The view can be generated automatically (like the index view), but we also have to make some adjustments. First the picture requires a fileupload-Control and a “multipart/form-data” form. Second we have to remove the input field for ID, since indexing will be handled by the database. The final create view is shown in the following box.

<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>

   <% using (Html.BeginForm("Create", "Products", FormMethod.Post, new { enctype = "multipart/form-data" })){%>

       <fieldset>
           <legend>Fields</legend>
           <p>
               <label for="Name">Name:</label>
               <%= Html.TextBox("Name") %>
               <%= Html.ValidationMessage("Name", "*") %>
           </p>
           <p>
               <label for="Price">Price:</label>
               <%= Html.TextBox("Price") %>
               <%= Html.ValidationMessage("Price", "*") %>
           </p>
           <p>
               <label for="Year">Year:</label>
               <%= Html.TextBox("Year") %>
               <%= Html.ValidationMessage("Year", "*") %>
           </p>
           <p>
               <label for="Name">Picture:</label>
               <input type="file" id="fileUpload" name="fileUpload" size="23"/>
               <%= Html.ValidationMessage("Picture", "*")%>
           </p>
           <p>
               <input type="submit" value="Create" />
           </p>
       </fieldset>

   <% } %>

   <div>
       <%=Html.ActionLink("Back to List", "Index") %>
   </div>

Finally we need two more functions for the “Edit” action. In This case the one that is initially called requires a bound product entity, because the form fields have to be pre-filled with the product data. Please note that in the second function the product has to be attached explicitly to the entity product set. This is due to the fact that the view passes a detached entity object to the action.

//
// GET: /Products/Edit/5

public ActionResult Edit(int id)
{
    Product product = _entities.ProductSet.Where(p => p.ID == id).FirstOrDefault();

    return View(product);
}

//
// POST: /Products/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Product product)
{
    try
    {
        _entities.AttachTo("ProductSet", product);

        ObjectStateManager stateMgr = _entities.ObjectStateManager;
        ObjectStateEntry stateEntry = stateMgr.GetObjectStateEntry(product);

        stateEntry.SetModified(); // Make sure the entity is marked as modified


        if (Request.Files.Count > 0)
        {
            product.Picture = (new FileHandler()).uploadedFileToByteArray((HttpPostedFileBase)Request.Files[0]);
        }

        _entities.SaveChanges();

        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

The code for the edit view is nearly identical with the edit view and therefore it is not necessary to show the code in this post.

Screenshot2 

Screenshot3

Finally with some pictures the list looks quite nice. Most parts of the application were generated by the ASP.NET MVC framework and thanks to the ADO.NET Entity Framework there was not even one single line of SQL code to write.

Full source code is available here.

Tags: , , , ,

ADO.NET Entity Framework | ASP.NET MVC

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading



Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen | Modified by Mooglegiant

About the author

Matthias Broschk from Hamburg (Germany) wrote his first goto statements in QuickBasic at the age of fifteen, switched to Visual Studio (i.e. Visual Basic / Visual C++) at version 5.0 and has been an addict of .NET since its early beginnings. There have been many other languages and frameworks (Java, PHP, ... ), but none about which he has been as enthusiastic as .NET. These days you can find more information in blogs rather than anywhere else. Therefore he has decided to share his experiences and start yet another .NET blog.