Click here to Skip to main content
Click here to Skip to main content

Best way to use Entity Framework

By , 14 Jan 2013
 

Introduction

I have been working in .NET the last 4 years, Since last year I have been using 4.0 framework with Entity Framework and MVC. After reading so many articles and doing a lot of research I made an architecture to use Entity Framework in a robust and easy manner.

Background 

The basic idea behind this architecture is to provide the facility to the developer to use entity framework data context in a robust way and without writing custom Insert, Update and Delete methods for each individual entity table classes.

Start Integration   

Let's start with the integration first we need to create our Entity Model, you can use any approach to create the EF designer class like Code First, Model First or Database First. But in my opinion the database first approach is the best one among all of them because in this approcah developer can use maximum benefits of this particular framework.   

So in my way create the edmx file, connect them with the desired database and generate the table classes by step by step wizard. Here is the detailed description to generate edmx file.   


After generating the above edmx we will create two classes name as Global.cs which will handle the Database Context in a robust way and another one will be Helper.cs which will consist the generic extension methods of Insert, Update and Delete operations.  The Product.cs is a partial class of the product entity and I'll consider this class later in this article.

Global.cs    

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
 
namespace Business
{
    public sealed class Global
    {
        public static NorthwindEntities Context
        {
            get
            {
                string ocKey = "key_" + HttpContext.Current.GetHashCode().ToString("x");
                if (!HttpContext.Current.Items.Contains(ocKey))
                    HttpContext.Current.Items.Add(ocKey, new NorthwindEntities());
                return HttpContext.Current.Items[ocKey] as NorthwindEntities;
            }
        }
    }
}   

The above class holds the Entity Framework Data Context in HttpContext.  This thing will make the DataContext to use in a robust manner,  because the data context will be remain same for each HTTP request. In this working style the nightmare to initialize and dispose the data context on certain code block gets removed. The context will be disposed when HTTP request ends. 

Helper.cs    

using System.Linq;
using System.Data.Objects.DataClasses;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data;
 
namespace Business
{
    public static class Helper
    {
        /// <summary>
        /// Save the reocrd to related table, if the primary key passed update it else add it.
        /// </summary>
        /// <param name="objEntity">Entity Object</param>
        public static void Save(this EntityObject objEntity)
        {
            try                                       // Update record.
            {
                Global.Context.DetachObject(objEntity);
                Global.Context.AttachTo(Global.Context.GetEntitySetName(objEntity), objEntity);
                Global.Context.ObjectStateManager.ChangeObjectState(objEntity, EntityState.Modified);
                Global.Context.SaveChanges();
 
            }
            catch (OptimisticConcurrencyException)   // Insert if found the record is already exists.
            {
                Global.Context.DetachObject(objEntity);
                Global.Context.AddObject(Global.Context.GetEntitySetName(objEntity), objEntity);
                Global.Context.SaveChanges();
            }
        }
 
        /// <summary>
        /// Delete the record from related table. The primary key value must be filled.
        /// </summary>
        /// <param name="objEntity">Entity Object</param>
        public static void Delete(this EntityObject objEntity)
        {
            if (objEntity != null)
            {
                Global.Context.DetachObject(objEntity);
                Global.Context.Attach(objEntity);
                Global.Context.ObjectStateManager.ChangeObjectState(objEntity, EntityState.Deleted);
                Global.Context.SaveChanges();
            }
        }
 
        private static string GetEntitySetName(this ObjectContext Context, EntityObject entity)
        {
            string entityTypeName = entity.GetType().Name;
            var container = Context.MetadataWorkspace.GetEntityContainer(Context.DefaultContainerName, DataSpace.CSpace);
            return container.BaseEntitySets.FirstOrDefault(meta => meta.ElementType.Name == entityTypeName).Name;
        }
 
        private static void DetachObject(this ObjectContext Context, EntityObject entity)
        {
            if (entity.EntityKey != null)
            {
                object objentity = null;
                var exist = Context.TryGetObjectByKey(entity.EntityKey, out objentity);
                if (exist) { Context.Detach(entity); }
            }
        }
    }
}

The above class consists extension methods which will take care of Insert, Delete and Update operations against the DB with database context. As you can see, these extension methods are for EntityObject class and this class inherits all the database table classes which is generated by entity framework see image below of Northwind.Designer.cs

 

 

So it means that this database entity classes can use the above extension methods as well because of the wonderful OOPS concept Inheritance Smile  

How to use above architecture

Now most the things are done so lets take a look of an example of using this architecture. As i created a partial class of Product and this particular class has also has a definition in the designer class as well.

Product.cs     

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Business
{
    public partial class Product
    {
        public static List<Product> GetProducts()
        {
            return Global.Context.Products.ToList();
        }
 
        public static Product GetProduct(int ProductId)
        {
            return Global.Context.Products.SingleOrDefault(P => P.ProductID == ProductId);
        }
    }
}

This is just a simple class which has two static methods to retrive the product table data. The thing to be note here that i am using our Global.Context that we creted earlier.

Now this business logic project is completed you can add many more classes into it as per requirement.  You can attach this layer with any kind of your project Web Application or Website etc. In my case i have attached a web application in my demo

 

Default.aspx

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApp._Default" %>
 
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent"></asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <h2>Products</h2><br /><hr /><br />
 
    <asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">
        <asp:View ID="View1" runat="server">
            <div style="text-align:right;margin-bottom:5px;"><asp:Button ID="btnAdd" 
                    runat="server" Text="Add Product" onclick="btnAdd_Click" /></div>
            <asp:GridView ID="GV" runat="server" AutoGenerateColumns="false" Width="100%" 
                onrowcommand="GV_RowCommand">
                <Columns>
                    <asp:BoundField HeaderText="Product Id" DataField="ProductID" 
                      HeaderStyle-HorizontalAlign="Left" HeaderStyle-Width="80" />
                    <asp:BoundField HeaderText="Product Name" DataField="ProductName" HeaderStyle-HorizontalAlign="Left" />
                    <asp:BoundField HeaderText="Qty / Unit" DataField="QuantityPerUnit" HeaderStyle-HorizontalAlign="Left" />
                    <asp:BoundField HeaderText="Price" DataField="UnitPrice" ItemStyle-HorizontalAlign="Right" />
                    <asp:BoundField HeaderText="Stock" DataField="UnitsInStock" ItemStyle-HorizontalAlign="Center" />
                    <asp:BoundField HeaderText="Discontinued" DataField="Discontinued" ItemStyle-HorizontalAlign="Center" />
                    <asp:TemplateField ItemStyle-HorizontalAlign="Center">
                        <ItemTemplate>
                            <asp:LinkButton ID="LinkButton1" runat="server" 
                              CommandName="IsEdit" CommandArgument='<%#Eval("ProductID") %>'>Edit</asp:LinkButton>&nbsp;
                            <asp:LinkButton ID="LinkButton2" runat="server" 
                              CommandName="IsDelete" CommandArgument='<%#Eval("ProductID") %>' 
                              OnClientClick="javascript:return confirm('Are you sure want to delete?')">Delete</asp:LinkButton>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>    
        </asp:View>
        <asp:View ID="View2" runat="server">
            <table width="100%">
                <tr>
                    <th width="100" align="right">Name&nbsp;&nbsp;</th>
                    <td>
                        <asp:HiddenField ID="hfProductId" runat="server" Value="0" />
                        <asp:TextBox ID="txtName" runat="server" Width="300"></asp:TextBox>
                        <asp:RequiredFieldValidator ID="RF1" runat="server" 
                          ControlToValidate="txtName" ErrorMessage="Enter Product Name" 
                          ValidationGroup="Save"></asp:RequiredFieldValidator>
                    </td>
                </tr>
                <tr>
                    <th align="right">Qty per unit&nbsp;&nbsp;</th>
                    <td><asp:TextBox ID="txtQty" runat="server" Width="300"></asp:TextBox>
                        <asp:RequiredFieldValidator ID="RF2" runat="server" 
                          ControlToValidate="txtQty" ErrorMessage="Enter Qty" 
                          ValidationGroup="Save"></asp:RequiredFieldValidator>
                    </td>
                </tr>
                <tr>
                    <th align="right">Price&nbsp;&nbsp;</th>
                    <td><asp:TextBox ID="txtPrice" runat="server" Width="300"></asp:TextBox>
                        <asp:RequiredFieldValidator ID="RF3" runat="server" 
                          ControlToValidate="txtPrice" ErrorMessage="Enter Price" 
                          ValidationGroup="Save"></asp:RequiredFieldValidator>
                    </td>
                </tr>
                <tr>
                    <th align="right">Stock&nbsp;&nbsp;</th>
                    <td><asp:TextBox ID="txtStock" runat="server" Width="300"></asp:TextBox>
                        <asp:RequiredFieldValidator ID="RF4" runat="server" 
                          ControlToValidate="txtStock" ErrorMessage="Enter Stock" 
                          ValidationGroup="Save"></asp:RequiredFieldValidator>
                    </td>
                </tr>
                <tr>
                    <th align="right">Discontinued&nbsp;&nbsp;</th>
                    <td><asp:CheckBox ID="chkDiscontinued" runat="server" /></td>
                </tr>
                <tr>
                    <td>&nbsp;</td>
                    <td>
                        <asp:Button ID="btnSave" runat="server" Text="Save" 
                          onclick="btnSave_Click" ValidationGroup="Save" />&nbsp;
                        <asp:Button ID="btnCancel" runat="server" 
                          Text="Cancel" onclick="btnCancel_Click" />
                    </td>
                </tr>
            </table>
        </asp:View>
    </asp:MultiView>
</asp:Content>

Default.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Business;
 
namespace WebApp
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
                BindGrid();
        }
 
        private void BindGrid()
        {
            GV.DataSource = Product.GetProducts();
            GV.DataBind();
        }
 
        protected void btnAdd_Click(object sender, EventArgs e)
        {
            MultiView1.ActiveViewIndex = 1;
            hfProductId.Value = "0";
            txtName.Text = txtQty.Text = txtPrice.Text = txtStock.Text = String.Empty;
            chkDiscontinued.Checked = false;
        }
 
        protected void btnSave_Click(object sender, EventArgs e)
        {
            Product objProduct = new Product
            {
                ProductName = txtName.Text,
                QuantityPerUnit = txtQty.Text,
                UnitPrice = Convert.ToDecimal(txtPrice.Text),
                UnitsInStock = Convert.ToByte(txtStock.Text),
                Discontinued = chkDiscontinued.Checked
            };
 
            if (hfProductId.Value != "0")
                objProduct.ProductID = Convert.ToInt32(hfProductId.Value);
 
            objProduct.Save();
            MultiView1.ActiveViewIndex = 0;
            BindGrid();
        }
 
        protected void btnCancel_Click(object sender, EventArgs e)
        {
            MultiView1.ActiveViewIndex = 0;
        }
 
        protected void GV_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            if (!String.IsNullOrEmpty(Convert.ToString(e.CommandArgument)))
            {
                Product objProduct = Product.GetProduct(Convert.ToInt32(e.CommandArgument));
 
                switch (e.CommandName)
                {
                    case "IsEdit":
                        hfProductId.Value = objProduct.ProductID.ToString();
                        txtName.Text = objProduct.ProductName;
                        txtQty.Text = objProduct.QuantityPerUnit;
                        txtPrice.Text = (objProduct.UnitPrice ?? 0).ToString();
                        txtStock.Text = (objProduct.UnitsInStock ?? 0).ToString();
                        chkDiscontinued.Checked = objProduct.Discontinued;
                        MultiView1.ActiveViewIndex = 1;
                        break;
                    case "IsDelete":
                        objProduct.Delete();
                        BindGrid();
                        break;
                }
            }
        }
    }
}

This is a default demo page where all the functionality related Insert, Update and Delete described in easy manner. 

Conclusion 

This is all about the architecture. In short we have created a business project with our edmx and attach our classes so that combine with the orginal stuff without changing them. The biggest benefit that you do not need to worry if you change your edmx or update models in it because all things are separate.

I hope you enjoyed the article and thanks for reading this.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Kundan Singh Chouhan
Software Developer (Senior) Dotsquares Technolgy Pvt Ltd
India India
Eager to contribute highly applicable application development skills and ability to personalize service delivery to analyzing business needs and translating them into executable strategies.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1memberCarbonRobot21-Jan-13 5:24 
This is not the correct usage of entity framework. I suggest reading the correct way to use it and then you will not have problems with it.
General[My vote of 1] Not a recommended approachmemberArthur Vickers16-Jan-13 8:06 
Hi Kundan,
 
I work on the EF team and I have to say that I would not recommend this approach to using the Entity Framework for many reasons. Rather than go into all the reasons here I suggest that you (and others reading this) take a look at some of the many tutorials and samples that are out there, such as http://www.asp.net/mvc/tutorials/mvc-music-store[^]. You also might want to try using a newer version of EF, such as EF5, which doesn't use tightly coupled EntityObject entities anymore. More information can be found here: http://msdn.microsoft.com/en-US/data/ef[^].
 
Thanks,
Arthur
GeneralRe: [My vote of 1] Not a recommended approachmemberKundan Singh Chouhan16-Jan-13 16:39 
Hi Arthur, Thanks for reading and analyzing. If you don't mind can you tell me all the reasons for which you are not recommending this approach?
GeneralMy vote of 1memberPop Catalin14-Jan-13 22:59 
Wow, this has so many bad practices I've lost count: using static methods, as single global context, no ability to save a set of modified entities instead they all are saved individually, concurrency exceptions ignored ... My head hurts!
GeneralRe: My vote of 1memberKundan Singh Chouhan15-Jan-13 5:41 
Hi Pop sorry that your head gets hurt although my intention wasn't that. Yeah you are correct that these extension methods save individual entity so right now i am working on this particular thing will publish when gets completed
GeneralMy vote of 2memberPascen14-Jan-13 10:55 
Thanks for the article Kundan. But I have to disagree with the title. This is by far not the best way to use Entity Framework. The code is very tightly coupled. How do you unit test this code for example? You should have a look at IOC and SOLID principles and please stop misusing the static keyword like that.
 
You mentioned in the introduction that you also use MVC. Good OO design and the ability to unit test is one of the main reasons to use MVC IMO.
GeneralRe: My vote of 2memberKundan Singh Chouhan14-Jan-13 16:44 
Hi Pascen thanks for your consideration. Regarding Unit Test i have attached a unit test application with current example and its also available in download source code.
 
Although in unit test example i didn't use MVC. Its the best in my opinion because of two main reasons.
 
1) DataContext is alive and disposed on HttpContext basis so you do not need to write the code for initialize and dispose again and again in code blocks.
 
2) The Insert, Delete and Update methods are attached with Data Context Entity classes without any change effect in the original designer file. So you developer not need to write individual functions for this operation for each entity class.
 
I am aware about the DI and IOC implementation and already used MVC repository pattern as well but the nightmare of using this implementation that you need to create a separate class which inherits the other one and than write custom methods for each individual operations.
GeneralRe: My vote of 2memberCarbonRobot21-Jan-13 5:33 
You do not have to write Insert, Update, and Delete methods for entity classes. You also have no need to write separate classes at all.
 
I highly recommend reading the documentation on the Entity Framework website, and working through the samples.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130617.1 | Last Updated 14 Jan 2013
Article Copyright 2013 by Kundan Singh Chouhan
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid