Click here to Skip to main content
15,867,594 members
Articles / Web Development / ASP.NET

Developing Web Applications with ASP.NET MVC3 and Entity Framework 4.1

Rate me:
Please Sign up or sign in to vote.
4.82/5 (4 votes)
17 Aug 2011CPOL5 min read 105.3K   4.4K   50   43
In this article, I will provide a demo application that is developed with the ASP.NET MVC3 and Entity Framework4.1.

Introduction

ASP.NET MVC and Entity Framework is a good combination for a web application that has database backend on Microsoft platform. In this article, I will provide a demo application that is developed with the ASP.NET MVC3 and Entity Framework 4.1. The demo also adopts the following design principal, pattern and best practice to make it more flexible and extensible.

  • Separation of Concerns
  • POCO
  • Code-First
  • Registry Pattern
  • Repository Pattern
  • Persistence Ignorance
  • Dependency Injection
  • Unit Testing

In order to open and run the demo application, you need to have SQL Server/SQL Server Express with Northwind sample database and Visual Studio 2010 with ASP.NET MVC3 and Entity Framework 4.1.

Demo Application Architecture

ASP.NET MVC already has very good defined layers. In my demo, I move Model layer into its own assembly to allow reusable in other applications. Also, the model is a rich model that not just contains data and behaviors, also gets more responsibility via services. The application architecture is like this:

Architecture.gif

  • Demo.Web package contains ASP.NET MVC Views and Controllers
  • Demo.Web.Tests package contains test case for Controllers
  • Demo.Models package contains domain model, business model, and data interfaces
  • Demo.Data package contains data access logic with Entity Framework
  • Demo.Base package contains utilities for the entire application

Design Principal, Pattern, and Best Practice

I will not go through every detail of my demo application. The source code shows the complete picture. I will only focus on those design principals, patterns, and best practices used in here.

Separation of Concerns

The ASP.NET MVC already follows Separation of Concern principal pretty good, it puts UI into View layer, interaction into Controller layer, and domain entities into Model layer. The only improvement I made in this demo application is to further moving Model layer into a separate assembly and has a Data layer to contain data access logics with Entity Framework.

SeperationOfConcerns.gif

POCO

I use Visual Studio 2010 Extension Manager installed ADO.NET C# POCO Entity Generator generated my original POCO domain entity Customer and Order based on Entity Data Model (edmx). The code generated is default in Data layer, I moved them from Data layer into Model layer and completely removed code generator template files. The domain entity will be enhanced and modified manually since that except there are some major table structure changes.

CustomerClass.gif

C#
public partial class Customer
{
	#region Primitive Properties

	public virtual string CustomerID
	{
		get;
		set;
	}

	[Required]
	public virtual string CompanyName
	{
		get;
		set;
	}

	public virtual string ContactName
	{
		get;
		set;
	}

	public virtual string ContactTitle
	{
		get;
		set;
	}

	public virtual string Address
	{
		get;
		set;
	}

	public virtual string City
	{
		get;
		set;
	}

	public virtual string Region
	{
		get;
		set;
	}

	public virtual string PostalCode
	{
		get;
		set;
	}

	public virtual string Country
	{
		get;
		set;
	}

	public virtual string Phone
	{
		get;
		set;
	}

	public virtual string Fax
	{
		get;
		set;
	}

	#endregion
	#region Navigation Properties

	public virtual ICollection<Order> Orders
	{
		get
		{
			if (_orders == null)
			{
				var newCollection = new FixupCollection<Order>();
				newCollection.CollectionChanged += FixupOrders;
				_orders = newCollection;
			}
			return _orders;
		}
		set
		{
			if (!ReferenceEquals(_orders, value))
			{
				var previousValue = _orders as FixupCollection<Order>;
				if (previousValue != null)
				{
					previousValue.CollectionChanged -= 
								FixupOrders;
				}
				_orders = value;
				var newValue = value as FixupCollection<Order>;
				if (newValue != null)
				{
					newValue.CollectionChanged += FixupOrders;
				}
			}
		}
	}
	private ICollection<Order> _orders;

	#endregion
	#region Association Fixup

	private void FixupOrders(object sender, NotifyCollectionChangedEventArgs e)
	{
		if (e.NewItems != null)
		{
			foreach (Order item in e.NewItems)
			{
				item.Customer = this;
			}
		}

		if (e.OldItems != null)
		{
			foreach (Order item in e.OldItems)
			{
				if (ReferenceEquals(item.Customer, this))
				{
					item.Customer = null;
				}
			}
		}
	}

	#endregion
}

Code-First

Code-First in Entity Framework is different with Model-First and Database-First. Honestly, I follow the Code-First philosophy without use exact Code-First steps to setup project in the beginning. Instead, I created Entity Data Model from database, then use it to generate the original domain entity code and moved the code into model layer. So, the Code-First approach is just to allow me to isolate future domain entity code changes with database changes completely.

Registry Pattern

Model layer has a special public static object Registry that is a well-known object to let other objects using it find common objects and services. This is an implementation of Registry pattern in Pattern of Enterprise Application Architecture (PoEAA) book. In my demo application, Registry object allows domain entities and upper layers to locate Repository service - Context and RepositoryFactory.

RegistryClass.gif

C#
public static class Registry
{
	public static IDependencyLocator DependencyLocator;

	public static IContext Context
	{
		get
		{
			return DependencyLocator.LocateDependency<IContext>();
		}
	}
	
	public static IRepositoryFactory RepositoryFactory
	{
		get
		{
			return DependencyLocator.LocateDependency
						<IRepositoryFactory>();
		}
	}
} 

Repository Pattern

In Hibernate application, there is a pattern called Data Access Object (DAO) pattern, the Repository Pattern is the counterpart pattern with a different name in Entity Framework. I read Repository Pattern implementation code in NerdDinner, it is not separating data access logic to its own layer good enough. In my demo, Repository Interfaces all reside in Model layer to provide data service access point, then use Dependence Injection to inject specific Data layer implementation into Model layer. The reason for this kind of implementation is to promote Model centric idea and allow switching Data layer for different implementation or Unit Test.

RepositoryInterfaces.gif

RepositoryClasses.gif

Persistence Ignorance

Define data access interfaces in Model layer and put Data layer into its own assembly encourage Persistence Ignorance here, Model layer and upper layers don’t really need to worry about how the model gets loaded, updated, or saved by the data layer. Additionally, I can switch Data layer to different ORM framework, like NHibernate, or a Mock implementation whenever it is necessary.

Dependency Injection

Dependency Injection plays a crucial part in my demo. All services module need to be late bound with Model layer with Dependency Injection. In addition, the IoC container manages the lifetime of service objects. One example is the Context object. I set lifetime type as PerThreadLifetimeManager in Unity configuration. This makes one and only one context object created in a single request and the different request has a different context object. Another thing I want to mention is ASP.NET MVC3 has its own way to provide Dependency Inject for controller via implementing DependencyResolver interface. I tried it, but eventually not adopt it because my application is Model centric application, so Registry is a better place to locate all the services instead of controller constructor. The IoC container I used in the demo is Unity. The MEF IoC container is getting more popular at the moment, but it’s more intrusive to domain entities by using attribute and not allowing configurable dependencies that make me hesitate to use it.

C#
protected void Application_Start()
{
	AreaRegistration.RegisterAllAreas();

	RegisterGlobalFilters(GlobalFilters.Filters);
	RegisterRoutes(RouteTable.Routes);

	RegisterUnityContainer();
}

private void RegisterUnityContainer()
{
	var container = new UnityContainer();
	UnityConfigurationSection section = 
	  (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
	section.Configure(container);
	Registry.DependencyLocator = new UnityDependencyLocator(container);
} 

Unit Test

I usually use Test Project to automatically integration test instead of pure unit test. The reason I do this is to allow the test be more close to user acceptance test to avoid thinking of every possible test scenario for each single class in my application. In this demo, the CustomerControllerTest will have test case Index to test CustomerController Index action. There is another project Demo.DataMock that is the mock of Data layer. I can use mock framework (e.g. MOQ) to mock Data layer in test case, but I decide to not use it because I don’t want a permanent mock for Data layer in test case, I want Data layer mock during development and build process, on the other hand, I also want my test can run against the real data layer before I check in my changes to make sure the test case can be all passed in the “production” environment. I understand running again the real database is slow, but keep in mind, with test being more close to real usage allows me to uncover more possible bugs and the running again of real database is just a one time thing for each check in or before release to QA.

Web.config in Demo.Web
XML
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="IContext" type="Demo.Models.DataInterfaces.IContext, Demo.Models" />
<alias alias="Context" type="Demo.Data.Context, Demo.Data" />
<alias alias="IRepositoryFactory" 
	type="Demo.Models.DataInterfaces.IRepositoryFactory, Demo.Models" />
<alias alias="RepositoryFactory" type="Demo.Data.RepositoryFactory, Demo.Data" />
<container>
  <register type="IContext" mapTo="Context">
	<lifetime type="PerThreadLifetimeManager" />
  </register>
  <register type="IRepositoryFactory" mapTo="RepositoryFactory">
	<constructor>
	  <param name="context" />
	</constructor>        
  </register>
</container>
</unity>
App.config in Demo.Web.Tests
XML
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="IContext" type="Demo.Models.DataInterfaces.IContext, Demo.Models" />
<alias alias="Context" type="Demo.DataMock.Context, Demo.DataMock" />
<alias alias="IRepositoryFactory" 
	type="Demo.Models.DataInterfaces.IRepositoryFactory, Demo.Models" />
<alias alias="RepositoryFactory" type="Demo.DataMock.RepositoryFactory, Demo.DataMock" />
<container>
  <register type="IContext" mapTo="Context">
	<lifetime type="PerThreadLifetimeManager" />
  </register>
  <register type="IRepositoryFactory" mapTo="RepositoryFactory">
	<constructor>
	  <param name="context" />
	</constructor>
  </register>
</container>
</unity>

License

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


Written By
Software Developer (Senior)
United States United States
Senior Software Developer from New Jersey, USA

Have 15+ years experience on enterprise application development with various technologies.

Comments and Discussions

 
QuestionQuestions about this framework Pin
senvad8226-Jan-14 17:56
senvad8226-Jan-14 17:56 
AnswerRe: Questions about this framework Pin
Henry He28-Jan-14 8:56
Henry He28-Jan-14 8:56 
QuestionConnection string in Webconfig Pin
senvad8224-Jul-13 2:52
senvad8224-Jul-13 2:52 
AnswerRe: Connection string in Webconfig Pin
Henry He24-Jul-13 14:58
Henry He24-Jul-13 14:58 
Questionpls Need some help Pin
David Avila11-Jun-13 6:59
David Avila11-Jun-13 6:59 
AnswerRe: pls Need some help Pin
Henry He11-Jun-13 14:48
Henry He11-Jun-13 14:48 
QuestionGetting exception in Global.asax --> RegisterUnityContainer() --> section.Configure(container); Pin
KishorT26-Mar-13 0:25
KishorT26-Mar-13 0:25 
Getting below exception in Global.asax --> RegisterUnityContainer() --> section.Configure(container);
The type name or alias Context could not be resolved. Please check your configuration file and verify this type name.


at Microsoft.Practices.Unity.Configuration.ConfigurationHelpers.TypeResolverImpl.ResolveType(String typeNameOrAlias, Boolean throwIfResolveFails)
at Microsoft.Practices.Unity.Configuration.ConfigurationHelpers.TypeResolver.ResolveType(String typeNameOrAlias)
at Microsoft.Practices.Unity.Configuration.RegisterElement.GetMappedType()
at Microsoft.Practices.Unity.Configuration.RegisterElement.ConfigureContainer(IUnityContainer container)
at Microsoft.Practices.Unity.Configuration.ContainerConfiguringElement.ConfigureContainerInternal(IUnityContainer container)
at Microsoft.Practices.Unity.Configuration.ContainerElement.<>c__DisplayClass1.<configurecontainer>b__0(ContainerConfiguringElement element)
at Microsoft.Practices.ObjectBuilder2.EnumerableExtensions.ForEach[TItem](IEnumerable`1 sequence, Action`1 action)
at Microsoft.Practices.Unity.Configuration.ContainerElement.ConfigureContainer(IUnityContainer container)
at Microsoft.Practices.Unity.Configuration.UnityConfigurationSection.Configure(IUnityContainer container, String configuredContainerName)
at Microsoft.Practices.Unity.Configuration.UnityConfigurationSection.Configure(IUnityContainer container)
at Demo.Web.MvcApplication.RegisterUnityContainer() in ~\MVCDemo\Demo.Web\Global.asax.cs:line 52
at Demo.Web.MvcApplication.Application_Start() in ~\MVCDemo\Demo.Web\Global.asax.cs:line 43
KISH

AnswerRe: Getting exception in Global.asax --> RegisterUnityContainer() --> section.Configure(container); Pin
Henry He26-Mar-13 14:02
Henry He26-Mar-13 14:02 
QuestionEdit post method return an error when using registry pattern Pin
jacob_handaya7-Sep-12 1:19
jacob_handaya7-Sep-12 1:19 
AnswerRe: Edit post method return an error when using registry pattern Pin
Henry He12-Sep-12 2:50
Henry He12-Sep-12 2:50 
GeneralRe: Edit post method return an error when using registry pattern Pin
jacob_handaya24-Sep-12 19:13
jacob_handaya24-Sep-12 19:13 
QuestionStored Procedures Pin
Member 889556724-Jul-12 7:06
Member 889556724-Jul-12 7:06 
AnswerRe: Stored Procedures Pin
Henry He25-Jul-12 8:15
Henry He25-Jul-12 8:15 
GeneralRe: Stored Procedures Pin
DarrenL2220-Aug-12 14:05
DarrenL2220-Aug-12 14:05 
GeneralRe: Stored Procedures Pin
Henry He20-Aug-12 16:09
Henry He20-Aug-12 16:09 
SuggestionDAL should not depend on Model. Pin
arshi_4631-May-12 17:01
arshi_4631-May-12 17:01 
GeneralRe: DAL should not depend on Model. Pin
Henry He1-Jun-12 9:18
Henry He1-Jun-12 9:18 
Questionquick reference: install necessary items Pin
kelvin199718-May-12 19:59
kelvin199718-May-12 19:59 
GeneralMy vote of 3 Pin
Anup Shah (4 Years Exp. .net Development)9-Apr-12 22:41
Anup Shah (4 Years Exp. .net Development)9-Apr-12 22:41 
QuestionThe entity type "NameOfMyEntity" is not part of the model for the current context. Pin
David Avila13-Mar-12 5:06
David Avila13-Mar-12 5:06 
AnswerRe: The entity type "NameOfMyEntity" is not part of the model for the current context. Pin
Henry He13-Mar-12 14:55
Henry He13-Mar-12 14:55 
QuestionDBContext Generator or POCO Entity Generator Pin
easten12-Mar-12 22:22
easten12-Mar-12 22:22 
AnswerRe: DBContext Generator or POCO Entity Generator Pin
Henry He13-Mar-12 14:50
Henry He13-Mar-12 14:50 
QuestionError while executing in Global.ascx Pin
suntico.burhan25-Jan-12 22:31
suntico.burhan25-Jan-12 22:31 
AnswerRe: Error while executing in Global.ascx Pin
Henry He26-Jan-12 13:30
Henry He26-Jan-12 13:30 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.