An ASP.NET MVC model contains all of the business, validation, and data access logic required by your application. In other words, a model contains all of your application logic except the view logic and controller logic. The bulk of your time and effort when building an ASP.NET MVC application is devoted to building your model classes.
The focus of this chapter is on creating model classes for data access. In particular, you learn how to build model classes by using the Microsoft Entity Framework.
This chapter is divided into four parts. In the first part, you are provided with an overview of the Microsoft Entity Framework. You learn how to perform basic database operations such as listing, inserting, updating, and deleting records.
Next, you learn about the repository software design pattern. Things change. New technologies for data access are introduced every few years. The repository pattern enables you to isolate your data access logic from the remainder of your application so your application can adapt gracefully to change.
In the next section — and this is my favorite section of this chapter — you learn how to create a generic repository. I show you how you can avoid writing a new repository class each and every time you build a new application. I explain how you can create a single generic repository that works with both the Microsoft Entity Framework and LINQ to SQL (and other new data access technologies that might be introduced in the future).
Finally, we discuss how you write units tests for application logic that interacts with data access logic. You learn how to both mock and fake a repository layer.
Creating a Data Model
In this book, we focus on using the Microsoft Entity Framework to build our data model. The Microsoft Entity Framework is Microsoft’s recommended data access technology.
It is important to emphasize, however, that the ASP.NET MVC framework is not tied to the Microsoft Entity Framework. You can use your favorite data access technology with ASP.NET MVC. For example, ASP.NET MVC is entirely compatible with alternative data access technologies such as Microsoft LINQ to SQL or NHibernate.
The Microsoft Entity Framework (in part) is an Object Relational Mapping (ORM) tool. You use the Microsoft Entity Framework to generate data model classes from a database automatically. That way, you do not have to undergo the tedious process of building these data model classes by hand.
For example, if you have a database table named Products then you can use the Microsoft Entity Framework to generate a class named Product that represents a particular row from the table. In your code, instead of interacting with the Products database directly, you interact with the Entity Framework object context and the Product class.
The Entity Framework shields you from needing to interact directly with the database. By taking advantage of the Entity Framework, you never need to write SQL queries again. You can write all of your data access code in C# or VB.NET instead of SQL.
*** Begin Warning ***
In order to use the Microsoft Entity Framework, you need .NET Framework 3.5 Service Pack 1. The Entity Framework is included in Service Pack 1.
*** End Warning ***
Creating a Data Model with the Microsoft Entity Framework
Imagine that you have created a database named ProductsDB and a database table named Products. The columns for the Products table are listed in Figure 1. This table has four columns. The first column, the Id column, is a primary key column and an identity column.
Figure 1 – Products Database Table
Now, imagine that you want to access this table from an ASP.NET MVC application. The first step is to use the Microsoft Entity Framework to generate a data model. Follow these steps:
1. Right-click the Models folder in the Solution Explorer window and select the menu option Add, New Item.
2. In the Add New Item dialog, pick the Data category and the ADO.NET Entity Data Model template (see Figure 2). Name your data model DataModel.edmx and click the Add button. After you click the Add button, the Entity Data Model Wizard appears (see Figure 3).
3. In the Choose Model Contents wizard step, select Generate from database.
4. In the Choose Your Data Connection wizard step, pick the ProductsDB.mdf database for the data connection and the name ProductsDBEntities for the connection settings name (see Figure 4).
5. In the Choose Your Database Objects wizard step, select the Products table and enter the namespace Models (see Figure 5).
6. Click the Finish button to complete and close the wizard.
Figure 2 – Adding an ADO.NET Entity Data Model
Figure 3 – Using the Entity Data Model Wizard
Figure 4 – Choosing your data connection
Figure 5 – Choosing your database objects
After you complete the wizard, the ADO.NET Entity Designer appears (see Figure 6). The designer displays a single entity class named Products that corresponds to the Products database table. The class contains a property that corresponds to each column from the database.
Figure 6 – The ADO.NET Entity Designer
Notice that the name of the entity class is Products. The wizard generates an entity class with the same name as the corresponding database table. Because we want to use the entity class to represent a particular product, you should change the name of the entity class from Products to Product (singular). Right-click the entity on the designer surface and select the menu option Rename to rename the class. Click the Save button (the button with the anachronistic icon of a floppy disk) to save the renamed entity.
Each entity displayed in the ADO.NET Entity Designer corresponds to a C# or VB.NET class. When you modify entities in the designer and save the changes, the ADO.NET Entity Framework generates the C# or VB.NET classes in the background. You can see these classes by expanding the DataModel.edmx node in the Solution Explorer window and double-clicking the DataModel.Designer file to open the file in the Visual Studio Code Editor (see Figure 7).
Figure 7 – The DataModel.Designer file
Currently, our Entity Framework designer file contains definitions for two classes named ProductsDBEntities and Product. The ProductsDBEntities class represents an Entity Framework object context class. The object context class is the class that you use to interact with the database in your code.
In the following sections, you learn how to use the ProductsDBEntities and the Product classes to list, insert, edit, and delete database records.
*** Begin Warning ***
You never want to modify the Entity Framework designer file directly. The ADO.NET Entity Framework will overwrite any changes that you make to this file the next time you save changes that you’ve made in the ADO.NET Entity Designer.
*** End Warning ***
Listing Records
You use the Entity Framework object context – in our case, the ProductsDBEntities class – to retrieve a set of database records. Instead of using a SQL Select command, you use LINQ to represent the query.
For example, the Index() action in Listing 1 illustrates how you can retrieve all of the products from the Products database table.
Listing 1 – ControllersHomeController.cs Index action [C#]
using System.Linq; using System.Web.Mvc; using MvcApplication1.Models; namespace MvcApplication1.Controllers { public class HomeController : Controller { private ProductsDBEntities _entities = new ProductsDBEntities(); // // GET: /Home/ public ActionResult Index() { return View(_entities.ProductSet.ToList()); } } }
Listing 1 – ControllersHomeController.vb Index action [VB]
Public Class HomeController Inherits System.Web.Mvc.Controller Private _entities As New ProductsDBEntities() ' ' GET: /Home/ Function Index() As ActionResult Return View(_entities.ProductSet.ToList()) End Function End Class
Notice that the Home controller has a private field named _entities that represents an Entity Framework object context. The expression _entities.ProductSet.ToList() returns a generic list of Product classes that represent all of the records from the Products database table.
Of course, you can use LINQ to perform more complicated types of queries. The following query returns all of the products that have a price greater than $10.00 and returns the products in order of the product name.
[C#]
var results = from p in _entities.ProductSet where p.Price > 10.00m orderby p.Name select p;
[VB]
Dim results = From p In _entities.ProductSet _ Where p.Price > 10.0 _ Order By p.Name _ Select p
*** Begin Note ***
To learn more about LINQ, see Appendix A.
*** End Note ***
Getting a Single Record
You use a different LINQ query when retrieving a single record. For example, the Details() action in Listing 2 retrieves the product record from the Products database that has an Id of 2.
Listing 2 – ControllersHomeController.cs Details action [C#]
// // GET: /Home/Details/5 public ActionResult Details(int id) { var result = (from p in _entities.ProductSet where p.Id == id select p).FirstOrDefault(); return View(result); }
Listing 2 – ControllersHomeController.vb Details action [VB]
' ' GET: /Home/Details/5 Function Details(ByVal id As Integer) As ActionResult Dim result = (From p In _entities.ProductSet _ Where p.Id = id _ Select p).FirstOrDefault() Return View(result) End Function
Creating Records
You also use the Entity Framework object context to create new database records. For example, the second Create() action in Listing 3 adds a new Product to the Products database table.
Listing 3 – ControllersHomeController.cs Create action [C#]
// // GET: /Home/Create public ActionResult Create() { return View(); } // // POST: /Home/Create [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create([Bind(Exclude=”Id”)]Product productToCreate) { try { _entities.AddToProductSet(productToCreate); _entities.SaveChanges(); return RedirectToAction("Index"); } catch { return View(); } }
Listing 3 – ControllersHomeController.vb Create action [VB]
' ' GET: /Home/Create Function Create() As ActionResult Return View() End Function ' ' POST: /Home/Create <AcceptVerbs(HttpVerbs.Post)> _ Function Create(<Bind(Exclude:=”Id”)>productToCreate As Product) As ActionResult Try _entities.AddToProductSet(productToCreate) _entities.SaveChanges() Return RedirectToAction("Index") Catch Return View() End Try End Function
The first Create() action displays the HTML form for creating a new product. The HTML form is submitted to the second Create() action. This action actually adds the new product to the database.
Notice that two commands must be executed to add the new product to the database. First, the new product is added to the set of products represented by the Entity Framework object context by calling the AddToProductSet() method. Next, the SaveChanges() method is called to actually save the new record to the underlying database.
Editing Records
You can update a database record simply by modifying the properties of an entity and calling the SaveChanges() method. For example, the following three statements can be used to double the price for a product:
[C#]
var productToEdit = (from p in _entities.ProductSet where p.Id == 3 select p).FirstOrDefault(); productToEdit.Price = productToEdit.Price * 2; _entities.SaveChanges();
[VB]
Dim productToEdit = (From p In _entities.ProductSet _ Where p.Id = 3 _ Select p).FirstOrDefault() productToEdit.Price = productToEdit.Price * 2 _entities.SaveChanges()
However, when creating an ASP.NET MVC action, you typically do not retrieve an entity before modifying it. Instead, an instance of the entity is handed to you as a parameter of the action method.
The object context can only track changes to an entity when the entity is attached to the context. When the ASP.NET MVC framework creates the entity as an action method parameter, the entity is not attached.
In this situation, you need to retrieve the original entity, apply any property changes to the original entity, and then call SaveChanges() to update the database. For example, the second Edit() action in Listing 4 updates a Product in the database.
Listing 4 – ControllersHomeController.cs Edit action [C#]
// // GET: /Home/Edit/5 public ActionResult Edit(int id) { var productToEdit = (from p in _entities.ProductSet where p.Id == id select p).FirstOrDefault(); return View(productToEdit); } // // POST: /Home/Edit/5 [AcceptVerbs(HttpVerbs.Post)] public ActionResult Edit(Product productToEdit) { try { var originalProduct = (from p in _entities.ProductSet where p.Id == productToEdit.Id select p).FirstOrDefault(); _entities.ApplyPropertyChanges(originalProduct.EntityKey.EntitySetName, productToEdit); _entities.SaveChanges(); return RedirectToAction("Index"); } catch { return View(); } }
Listing 4 – ControllersHomeController.vb Edit action [VB]
' ' GET: /Home/Edit/5 Function Edit(ByVal id As Integer) As ActionResult Dim productToEdit = (From p In _entities.ProductSet _ Where p.Id = id _ Select p).FirstOrDefault() Return View(productToEdit) End Function ' ' POST: /Home/Edit/5 <AcceptVerbs(HttpVerbs.Post)> _ Function Edit(productToEdit As Product) As ActionResult Try Dim originalProduct = (From p In _entities.ProductSet _ Where p.Id = productToEdit.Id _ Select p).FirstOrDefault() _entities.ApplyPropertyChanges(originalProduct.EntityKey.EntitySetName, productToEdit) _entities.SaveChanges() Return RedirectToAction("Index") Catch Return View() End Try End Function
The first Edit() action in Listing 4 retrieves the Product so that it can be displayed in an HTML form. The second Edit() action is invoked when the HTML form is submitted and this Edit() action performs the actual update in the database.
The second Edit() action:
1. Retrieves the original product
2. Calls ApplyPropertyChanges() to update the original product with the changes from the modified product
3. Calls SaveChanges() to persist the changes to the database
*** Begin Warning ***
The ApplyChanges() method won’t update navigation properties or related objects. It only applies to properties of the immediate object.
*** End Warning ***
Deleting Records
Finally, you can use the Entity Framework object context to delete records. For example, the Delete() action in Listing 5 deletes a product record from the database.
Listing 5 – ControllersHomeController.cs Delete action [C#]
// // GET: /Home/Delete/5 public ActionResult Delete(int id) { var productToDelete = (from p in _entities.ProductSet where p.Id == id select p).FirstOrDefault(); return View(productToDelete); } // // POST: /Home/Delete [AcceptVerbs(HttpVerbs.Post)] public ActionResult Delete(Product productToDelete) { try { var originalProduct = (from p in _entities.ProductSet where p.Id == productToDelete.Id select p).FirstOrDefault(); _entities.DeleteObject(originalProduct); _entities.SaveChanges(); return RedirectToAction("Index"); } catch { return View(); } }
Listing 5 – ControllersHomeController.vb Delete action [VB]
' ' GET: /Home/Delete/5 Function Delete(ByVal id As Integer) As ActionResult Dim productToDelete = (From p In _entities.ProductSet _ Where p.Id = id _ Select p).FirstOrDefault() Return View(productToDelete) End Function ' ' POST: /Home/Delete <AcceptVerbs(HttpVerbs.Post)> _ Function Delete(productToDelete As Product) As ActionResult Try Dim originalProduct = (From p In _entities.ProductSet _ Where p.Id = productToDelete.Id _ Select p).FirstOrDefault() _entities.DeleteObject(originalProduct) _entities.SaveChanges() Return RedirectToAction("Index") Catch Return View() End Try End Function
Using the Repository Pattern
Things change. When I originally wrote this chapter, I wrote all of the code samples using Microsoft LINQ to SQL instead of the Microsoft Entity Framework. When Microsoft announced that the Entity Framework is the recommended data access technology, I rewrote this chapter.
When you build an application, you should build the application to gracefully adapt to change. If you believe that code is likely to change in the future then you should encapsulate the code into a separate class.
In this section, you learn how to use a software design pattern named the repository pattern to isolate your data access layer from the remainder of your application. By taking advantage of the repository pattern, you can easily modify your application to take advantage of a different data access technology in the future.
Creating a Product Repository
Let’s create a product repository that encapsulates all of our data access logic for working with products. We need to create two objects:
· IProductRepository – This interface describes the methods for listing, getting, inserting, updating, and deleting products.
· ProductRepository – This class implements the IProductRepository interface.
Why do we need to create both an interface and a class? In our application code, we always program against the interface. We call methods of the IProductRepository interface instead of calling methods of the ProductRepository class. That way, we can change our implementation of the interface in the future without needing to rewrite any of the application code that interacts with the interface.
The IProductRepository interface is contained in Listing 6. This interface describes five methods named List(), Get(), Create(), Edit(), and Delete().
Listing 6 – ModelsIProductRepository.cs [C#]
using System.Collections.Generic; namespace MvcApplication1.Models { public interface IProductRepository { IEnumerable<Product> List(); Product Get(int id); void Create(Product productToCreate); void Edit(Product productToEdit); void Delete(Product productToDelete); } }
Listing 6 – ModelsIProductRepository.vb [VB]
Public Interface IProductRepository Function List() As IEnumerable(Of Product) Function [Get](ByVal id As Integer) As Product Sub Create(ByVal productToCreate As Product) Sub Edit(ByVal productToEdit As Product) Sub Delete(ByVal productToDelete As Product) End Interface
The ProductRepository class in Listing 7 implements the IProductRepository interface. This implementation of the IProductRepository interface uses the Microsoft Entity Framework. However, we could implement the IProductRepository interface with a class that uses Microsoft LINQ to SQL, NHibernate, or just about any other data access technology.
Listing 7 – ModelsProductRepository.cs [C#]
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MvcApplication1.Models { public class ProductRepository : IProductRepository { private ProductsDBEntities _entities = new ProductsDBEntities(); #region IProductRepository Members public IEnumerable<Product> List() { return _entities.ProductSet.ToList(); } public Product Get(int id) { return (from p in _entities.ProductSet where p.Id == id select p).FirstOrDefault(); } public void Create(Product productToCreate) { _entities.AddToProductSet(productToCreate); _entities.SaveChanges(); } public void Edit(Product productToEdit) { var originalProduct = Get(productToEdit.Id); _entities.ApplyPropertyChanges(originalProduct.EntityKey.EntitySetName, productToEdit); _entities.SaveChanges(); } public void Delete(Product productToDelete) { var originalProduct = Get(productToDelete.Id); _entities.DeleteObject(originalProduct); _entities.SaveChanges(); } #endregion } }
Listing 7 – ModelsProductRepository.vb [VB]
Public Class ProductRepository Implements IProductRepository Private _entities As New ProductsDBEntities() #Region "IProductRepository Members" Public Function List() As IEnumerable(Of Product) Implements IProductRepository.List Return _entities.ProductSet.ToList() End Function Public Function [Get](ByVal id As Integer) As Product Implements IProductRepository.Get Return (From p In _entities.ProductSet _ Where p.Id = id _ Select p).FirstOrDefault() End Function Public Sub Create(ByVal productToCreate As Product) Implements IProductRepository.Create _entities.AddToProductSet(productToCreate) _entities.SaveChanges() End Sub Public Sub Edit(ByVal productToEdit As Product) Implements IProductRepository.Edit Dim originalProduct = [Get](productToEdit.Id) _entities.ApplyPropertyChanges(originalProduct.EntityKey.EntitySetName, productToEdit) _entities.SaveChanges() End Sub Public Sub Delete(ByVal productToDelete As Product) Implements IProductRepository.Delete Dim originalProduct = [Get](productToDelete.Id) _entities.DeleteObject(originalProduct) _entities.SaveChanges() End Sub #End Region End Class
Finally, the Product controller in Listing 8 uses the product repository in its Index() and Create() actions.
Listing 8 – ModelsProductController.cs [C#]
using System.Web.Mvc; using MvcApplication1.Models; namespace MvcApplication1.Controllers { public class ProductController : Controller { private IProductRepository _repository; public ProductController() : this(new ProductRepository()) { } public ProductController(IProductRepository repository) { _repository = repository; } // // GET: /Product/ public ActionResult Index() { return View(_repository.List()); } // // GET: /Product/Create public ActionResult Create() { return View(); } // // POST: /Product/Create [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create(Product productToCreate) { try { _repository.Create(productToCreate); return RedirectToAction("Index"); } catch { return View(); } } } }
Listing 8 – ModelsProductController.vb [VB]
Public Class ProductController Inherits Controller Private _repository As IProductRepository Public Sub New() Me.New(New ProductRepository()) End Sub Public Sub New(ByVal repository As IProductRepository) _repository = repository End Sub ' ' GET: /Product/ Public Function Index() As ActionResult Return View(_repository.List()) End Function ' ' GET: /Product/Create Public Function Create() As ActionResult Return View() End Function ' ' POST: /Product/Create <AcceptVerbs(HttpVerbs.Post)> _ Public Function Create(ByVal productToCreate As Product) As ActionResult Try _repository.Create(productToCreate) Return RedirectToAction("Index") Catch Return View() End Try End Function End Class
Using the Dependency Injection Pattern
Notice that the Product controller in Listing 8 has two constructors. The first constructor, the parameterless constructor, calls the second constructor. The first constructor creates an instance of the ProductRepository class and passes the instance to the second constructor.
The only place in the Product controller that the ProductRepository class is used instead of the IProductRepository interface is in this first constructor. If you wanted to use a different class that implements the IProductRepository then you need to change only the code in the first constructor.
The Product controller uses a software design pattern called the dependency injection pattern. In particular, it uses a pattern named constructor dependency injection.
*** Begin Note ***
The dependency injection pattern, like many of the software design patterns discussed in this book, was first described by Martin Fowler. See:
http://martinfowler.com/articles/injection.html
*** End Note ***
The constructor dependency injection pattern enables you to “loosely couple” two classes. Class A is dependent on Class B. In our case, our Product controller class is dependent on a product repository class. Because we might need to change Class B in the future, we want to be able to change Class B with the minimum impact on the code in Class A.
The constructor dependency injection pattern enables us to limit the contact between the Product controller and the product repository to a single point of contact. A concrete implementation of the product repository is only used in one place – within the Product controller constructor. If we yanked the two classes apart, the only part that would break would be the constructor.
*** Begin Note ***
If you want to eliminate any reference to the ProductRepository class within the ProductController class then you can take advantage of a Dependency Injection (DI) framework such as the Microsoft Managed Extensibility Framework (MEF) or StructureMap.
*** End Note ***
Later in this chapter — in the section entitled Testing Data Access — you’ll see another advantage of using the dependency injection pattern. The dependency injection pattern enables us to easily unit test our application code.
Creating a Generic Repository
Whenever you discover that you are writing the same code over and over again, you should step back and question your sanity. Mostly likely, you are wasting away precious moments of your life that could be better spent seeing movies or taking a walk in the park.
Recently, I realized that I was writing pretty much the same repository class over and over again. Therefore, I decided to create a generic repository. Creating a generic repository has several benefits:
1. I can use the generic repository as a starting point for all of my repository classes in the future.
2. I can use the generic repository with both the Microsoft Entity Framework and LINQ to SQL. Better yet, I can use the generic repository with some future and unknown data access technology.
3. I can use the generic repository in my unit tests.
In the CommonCode folder on the CD that accompanies this book, you’ll find a GenericRepository solution. This solution contains four projects (see Figure 8). Here’s a description of each project:
· GenericRepository Project – Contains the IGenericRepository interface. Also contains the FakeGenericRepository.
· EFGenericRepository Project – Contains the implementation of the IGenericRepository interface for the Microsoft Entity Framework.
· LSGenericRepository Project – Contains the implementation of the IGenericRepository interface for Microsoft LINQ to SQL.
· GenericRepository.Tests Project – Contains unit tests for the FakeGenericRepository.
The IGenericRepository interface is contained in Listing 9.
Listing 9 –IGenericRepository.cs [C#]
using System.Collections.Generic; using System.Linq; namespace GenericRepository { public interface IGenericRepository { IQueryable<T> List<T>() where T:class; T Get<T>(int id) where T : class; void Create<T>(T entityToCreate) where T : class; void Edit<T>(T entityToEdit) where T : class; void Delete<T>(T entityToDelete) where T : class; } }
Listing 9 –IGenericRepository.vb [VB]
Public Interface IGenericRepository Function List(Of T As Class)() As IQueryable(Of T) Function [Get](Of T As Class)(ByVal id As Integer) As T Sub Create(Of T As Class)(ByVal entityToCreate As T) Sub Edit(Of T As Class)(ByVal entityToEdit As T) Sub Delete(Of T As Class)(ByVal entityToDelete As T) End Interface
Figure 8 – The Generic Repository solution
Notice that each of the methods contained in the IGenericRepository interface are generic methods. The methods contain an open generic type parameter for the entity. For example, you can get a customer with a particular Id by executing the following code:
[C#]
var customerToEdit = _repository.Get<Customer>(3)
[VB]
Dim customerToEdit =_repository.Get(Of Customer)(3)
You can get a list of all products with a price less than $50.00 with the following code:
[C#]
var result = (from p in _repository.List<Product>()
where p.Price < 50.00m
select p).ToList();
[VB]
Dim result = (From p In _repository.List(Of Product)() _
Where p.Price < 50.00D _
Select p).ToList()
Using the Generic Repository with the Entity Framework
If you want to use the generic repository with the Microsoft Entity Framework then you need to add references to two assemblies to your ASP.NET MVC project. You need to add a reference to the assembly generated by the GenericRepository project (the GenericRepository.dll assembly) and you need to add a reference to the assembly generated by the EFGenericRepository project (the EFGenericRepository.dll assembly).
In your ASP.NET MVC project, you generate your data model classes with the Entity Framework Wizard in the normal way. After you generate the data model classes, you can use the classes with the generic repository.
The controller in Listing 10 uses the EF generic repository in its Index() and Create() actions.
*** Begin Note ***
The code in Listing 10 is contained in the EFMvcApplication project included on the CD that accompanies this book.
*** End Note ***
Listing 10 – ControllersHomeController.cs [C#]
using System.Linq; using System.Web.Mvc; using EFMvcApplication.Models; using GenericRepository; namespace EFMvcApplication.Controllers { public class HomeController : Controller { private IGenericRepository _repository; public HomeController() { _repository = new EFGenericRepository(new ToyStoreDBEntities()); } // // GET: /Home/ public ActionResult Index() { return View(_repository.List<Product>().ToList()); } // // GET: /Home/Create public ActionResult Create() { return View(); } // // POST: /Home/Create [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create([Bind(Exclude="Id")]Product productToCreate) { try { _repository.Create<Product>(productToCreate); return RedirectToAction("Index"); } catch { return View(); } } } }
Listing 10 – ControllersHomeController.vb [VB]
Imports GenericRepository Public Class HomeController Inherits Controller Private _repository As IGenericRepository Public Sub New() _repository = New EFGenericRepository(New ToyStoreDBEntities()) End Sub ' ' GET: /Home/ Public Function Index() As ActionResult Return View(_repository.List(Of Product)().ToList()) End Function ' ' GET: /Home/Create Public Function Create() As ActionResult Return View() End Function ' ' POST: /Home/Create <AcceptVerbs(HttpVerbs.Post)> _ Public Function Create(<Bind(Exclude:="Id")> ByVal productToCreate As Product) As ActionResult Try _repository.Create(Of Product)(productToCreate) Return RedirectToAction("Index") Catch Return View() End Try End Function End Class
Notice that an instance of the EFGenericRepository class is created in the controller’s constructor. The EFGenericRepository class is instantiated by passing an Entity Framework object context to the constructor for the EFGenericRepository class.
Using the Generic Repository with LINQ to SQL
In order to use the generic repository with LINQ to SQL, you need to add references to two assemblies to your ASP.NET MVC project. You need to add a reference to the assembly generated by the GenericRepository project (the GenericRepository.dll assembly) and you need to add a reference to the assembly generated by the LSGenericRepository project (the LSGenericRepository.dll assembly).
You generate your data model classes in the standard way: drag your database tables onto the LINQ to SQL designer.
The controller in Listing 11 uses the LS generic repository.
*** Begin Note ***
The code in Listing 11 is contained in the LSMvcApplication project included on the CD that accompanies this book.
*** End Note ***
Listing 11 – ControllersHomeController.cs [C#]
using System.Linq; using System.Web.Mvc; using GenericRepository; using LSMvcApplication.Models; namespace LSMvcApplication.Controllers { public class HomeController : Controller { private IGenericRepository _repository; public HomeController() { _repository = new LSGenericRepository(new DataModelDataContext()); } // // GET: /Home/ public ActionResult Index() { return View(_repository.List<Product>().ToList()); } // // GET: /Home/Create public ActionResult Create() { return View(); } // // POST: /Home/Create [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create([Bind(Exclude="Id")]Product productToCreate) { try { _repository.Create<Product>(productToCreate); return RedirectToAction("Index"); } catch { return View(); } } } }
Listing 11 – ControllersHomeController.vb [VB]
Imports GenericRepository Public Class HomeController Inherits Controller Private _repository As IGenericRepository Public Sub New() _repository = New LSGenericRepository(New DataModelDataContext()) End Sub ' ' GET: /Home/ Public Function Index() As ActionResult Return View(_repository.List(Of Product)().ToList()) End Function ' ' GET: /Home/Create Public Function Create() As ActionResult Return View() End Function ' ' POST: /Home/Create <AcceptVerbs(HttpVerbs.Post)> _ Public Function Create(<Bind(Exclude:="Id")> ByVal productToCreate As Product) As ActionResult Try _repository.Create(Of Product)(productToCreate) Return RedirectToAction("Index") Catch Return View() End Try End Function End Class
The LSGenericRepository is created in the controller’s constructor. You create an LSGenericRepository by passing a LINQ to SQL DataContext to the constructor for the LSGenericRepository class.
Extending the Generic Repository
The generic repository is only meant to act as a starting point. At some point, you’ll want to add custom methods to the generic repository. The easiest way to extend the generic repository is to create two new types:
· IRepository – Create a custom interface that inherits from the IGenericRepository interface.
· Repository – Create a custom class that implements the IRepository interface and inherits from a GenericRepository class such as the EFGenericRepository or LSGenericRepository.
For example, imagine that you want to create a custom method named ProductCount() that returns a count of products in the database. You find it more intuitive to call the custom method than to call the generic method List<Product>().Count(). In that case, you could create the interface in Listing 12.
Listing 12 – IRepository.cs [C#]
using GenericRepository; namespace EFMvcApplication.Models { interface IRepository : IGenericRepository { int GetProductCount(); } }
Listing 12 – IRepository.vb [VB]
Public Interface IRepository Function GetProductCount() As Integer End Interface
Notice that the interface in Listing 12 inherits from the IGenericRepository interface. Any class that implements the IRepository interface must implement every method from the IGenericRepository interface.
The new repository class in Listing 13 inherits from the EFGenericRepository class. However, the new class includes a new method named GetProductCount().
Listing 13 – Repository.cs [C#]
using System.Data.Objects; using System.Linq; using GenericRepository; namespace EFMvcApplication.Models { public class Repository : EFGenericRepository, IRepository { public Repository(ObjectContext context) : base(context) { } #region IRepository Members public int GetProductCount() { return this.List<Product>().Count(); } #endregion } }
Listing 13 – Repository.vb [VB]
Imports GenericRepository Imports System.Data.Objects Public Class Repository Inherits EFGenericRepository Implements IRepository Sub New(ByVal context As ObjectContext) MyBase.New(context) End Sub Public Function GetProductCount() As Integer Implements IRepository.GetProductCount Return Me.List(Of Product)().Count() End Function End Class
After you create the IRepository and Repository types, you can use these types in your controllers in exactly the same way as you would use the IGenericRepository and GenericRepository types. But now you are free to extend the generic repository as much as you please…
Testing Data Access
Normally, when you execute unit tests, you don’t want to test code that interacts with an actual database. In other words, you want to avoid unit testing data access logic.
In order for unit tests to be useful, you need to be able to execute them very fast. Unfortunately, accessing a database is one of the slowest operations that you can perform in a website. Therefore, you want to avoid interacting with a real database in your unit tests.
Instead of testing your data access logic, you want to test all of the business logic that interacts with the data access logic. For example, you definitely do want to build unit tests for all of the controller logic and all of the validation logic in your application. The challenge is testing this business logic without testing the data access logic.
In this section, we discuss two approaches to overcoming this challenge. You learn how you can mock your repository and you learn how you can fake your repository.
Testing with a Mock Repository
One approach to testing code that interacts with a repository is to mock the repository by using a Mock Object Framework such as Moq. You can use Moq to generate a mock class from any interface. For example, you can generate a mock generic repository from the IGenericRepository interface.
Consider the controller action in Listing 14. This action validates whether the Product FirstName property has a value before creating the product in the database.
*** Begin Note ***
We discuss validation in detail in Chapter 8, Validating Form Data. We discuss Moq in Appendix C.
*** End Note ***
Listing 14 – ControllersHomeController.cs [C#]
using System.Linq; using System.Web.Mvc; using EFMvcApplication.Models; using GenericRepository; namespace EFMvcApplication.Controllers { public class HomeController : Controller { private IGenericRepository _repository; public HomeController() :this(new EFGenericRepository(new ToyStoreDBEntities())){} public HomeController(IGenericRepository repository) { _repository = repository; } // // GET: /Home/ public ActionResult Index() { return View(_repository.List<Product>().ToList()); } // // GET: /Home/Create public ActionResult Create() { return View(); } // // POST: /Home/Create [AcceptVerbs(HttpVerbs.Post)] public ActionResult Create([Bind(Exclude="Id")]Product productToCreate) { if (productToCreate.Name.Trim().Length == 0) { ModelState.AddModelError("Name", "Product name is required."); return View(); } try { _repository.Create<Product>(productToCreate); return RedirectToAction("Index"); } catch { return View(); } } } }
Listing 14 – ControllersHomeController.vb [VB]
Imports GenericRepository Public Class HomeController Inherits Controller Private _repository As IGenericRepository Public Sub New() Me.New(New EFGenericRepository(New ToyStoreDBEntities())) End Sub Public Sub New(repository As IGenericRepository) _repository = repository End Sub ' ' GET: /Home/ Public Function Index() As ActionResult Return View(_repository.List(Of Product)().ToList()) End Function ' ' GET: /Home/Create Public Function Create() As ActionResult Return View() End Function ' ' POST: /Home/Create <AcceptVerbs(HttpVerbs.Post)> _ Public Function Create(<Bind(Exclude:="Id")> ByVal productToCreate As Product) As ActionResult If productToCreate.Name.Trim().Length = 0 Then ModelState.AddModelError("Name", "Product name is required.") Return View() End If Try _repository.Create(Of Product)(productToCreate) Return RedirectToAction("Index") Catch Return View() End Try End Function End Class
Imagine that you want to test the Create() action in Listing 14. You want to ensure that a validation error message is created when someone attempts to submit a new Product without supplying a value for the Name property.
The unit test in Listing 15 contains two methods. The first method, named Initialize(), creates a mock generic repository. The Moq framework is used to generate the mock generic repository from the IGenericRepository interface.
The second method, named NameIsRequired(), represents the unit test. This unit test creates an instance of the HomeController class with the mock generic repository. Next, the unit test invokes the Create() action and validates that an error message was, in fact, added to model state.
Listing 15 – ControllersHomeControllerTestMock.cs [C#]
using System; using System.Web.Mvc; using EFMvcApplication.Controllers; using EFMvcApplication.Models; using GenericRepository; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; namespace EFMvcApplication.Tests.Controllers { [TestClass] public class HomeControllerTestMock { private Mock<IGenericRepository> _mockRepository; [TestInitialize] public void Initialize() { _mockRepository = new Mock<IGenericRepository>(); } [TestMethod] public void NameIsRequired() { // Arrange var controller = new HomeController(_mockRepository.Object); var productToCreate = new Product(); productToCreate.Name = String.Empty; // Act var result = (ViewResult)controller.Create(productToCreate); // Assert var modelStateError = result.ViewData.ModelState["Name"].Errors[0].ErrorMessage; Assert.AreEqual("Product name is required.", modelStateError); } } }
Listing 15 – ControllersHomeControllerTestMock.vb [VB]
Imports Microsoft.VisualStudio.TestTools.UnitTesting Imports Moq Imports System.Web.Mvc Imports GenericRepository <TestClass()> _ Public Class HomeControllerTestMock Private _mockRepository As Mock(Of IGenericRepository) <TestInitialize()> _ Sub Initialize() _mockRepository = New Mock(Of IGenericRepository)() End Sub <TestMethod()> _ Sub TestMethod1() ' Arrange Dim controller As New HomeController(_mockRepository.Object) Dim productToCreate As New Product() productToCreate.Name = String.Empty ' Act Dim result As ViewResult = controller.Create(productToCreate) ' Assert Dim modelStateError = result.ViewData.ModelState("Name").Errors(0).ErrorMessage Assert.AreEqual("Product name is required.", modelStateError) End Sub End Class
We are able to substitute the mock repository for the actual repository in our unit test because the Home controller uses dependency injection. We are taking advantage of the second constructor that accepts any class that implements the IGenericRepository interface.
Testing with a Fake Generic Repository
When practicing test-driven development, you want your repository layer to behave just like a real repository layer in your unit tests. For example, if you create a new product by calling a repository method then you want to be able to retrieve the new product by calling another repository method.
If you need to fake the behavior of the generic repository layer in a unit test then you can take advantage of the fake generic repository. The fake generic repository is an implementation of the IGenericRepository interface that works with a simple in-memory database.
For example, the unit test in Listing 16 verifies that when you invoke the List() action that you can get back a product that you just created by invoking the Create() action.
Listing 16 – ControllersHomeControllerTestFake.cs [C#]
using System.Collections; using System.Web.Mvc; using EFMvcApplication.Controllers; using EFMvcApplication.Models; using GenericRepository; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace EFMvcApplication.Tests.Controllers { [TestClass] public class HomeControllerTestFake { IGenericRepository _fakeRepository; [TestInitialize] public void Initialize() { _fakeRepository = new FakeGenericRepository(); } [TestMethod] public void CreateThenList() { // Arrange var controller = new HomeController(_fakeRepository); var productToCreate = Product.CreateProduct(-1, "Test", "Test", 3.44m); // Act controller.Create(productToCreate); var results = (ViewResult)controller.Index(); // Assert var products = (ICollection)results.ViewData.Model; CollectionAssert.Contains(products, productToCreate); } } }
Listing 16 – ControllersHomeControllerTestFake.vb [VB]
Imports Microsoft.VisualStudio.TestTools.UnitTesting Imports GenericRepository Imports System.Web.Mvc Imports System.Collections <TestClass()> _ Public Class HomeControllerTestFake Private _fakeRepository As IGenericRepository <TestInitialize()> _ Public Sub Initialize() _fakeRepository = New FakeGenericRepository() End Sub <TestMethod()> _ Public Sub CreateThenList() ' Arrange Dim controller = New HomeController(_fakeRepository) Dim productToCreate = Product.CreateProduct(-1, "Test", "Test", 3.44D) ' Act controller.Create(productToCreate) Dim results = CType(controller.Index(), ViewResult) ' Assert Dim products = CType(results.ViewData.Model, ICollection) CollectionAssert.Contains(products, productToCreate) End Sub End Class
Notice that a FakeGenericRepository class is created in the Intialize() method. The fake generic repository stores objects in memory instead of touching a real database server.
Summary
In this chapter, we concentrated on building data model classes. In the first section, you were provided with a brief introduction to the Microsoft Entity Framework. You learned how to perform basic database operations such as listing, creating, editing, and deleting database records.
Next, you learn how to implement the repository software design pattern. You learned how to move database logic from your controllers into a separate repository layer. Moving your database logic into a repository layer makes your application more resilient to change.
We also discussed how you can create a generic repository layer so you don’t need to re-implement the same data access logic every time you create a new ASP.NET MVC project. You learned how to use the generic repository layer with both the Microsoft Entity Framework and Microsoft LINQ to SQL.
Finally, you learned how to unit test code that interacts with a repository layer. We discussed how you can mock a repository layer by taking advantage of the Moq Mock Object Framework. You also learned how to fake a repository layer by taking advantage of the FakeGenericRepository class.
I’m in two minds whether to use an IGenericRepository approach, or to use T4 to generate each of the strongly typed Repositories. In fact, I’m more leaning towards the latter, as the T4 generation could add extensibility points for each of the repositories, almost as LinqToSql has.
I’m glad that these chapters, the Contact Management sample, MVCStorefront, Oxite etc. are now all using very similar Service/Repository approaches. Makes the recommended/suggested data access approaches a lot clearer.
I noticed in Listing 8 that you have your controller in the Models directory.
“Listing 8 – ModelsProductController.cs [C#]”
Otherwise I like your presentation of the Repository Pattern.
I haven’t finished reading this yet; but good so far.
There seems to be a consensus that a generic repository is an anti-pattern. Having a concrete generic repository is fine though. The basis being not all repositories will support all the methods in the contract, in which case they end up throwing NotImplemented exceptions. Just something to keep in mind and maybe research a little bit more.
@Jason — thanks for bringing this debate to my attention:
codebetter.com/…/ddd-the-generic-repository.aspx
codebetter.com/…/…ository-lt-t-gt-domagic.aspx
I understand the concerns about the interface not being too open. I’m considering rewriting the Extending the Generic Repository section above to use composition instead of inheritance.
Jason, don’t mistake “assertions” for “consensus.”
Hello Stephen
Here there are some recommendations:
1. Beginning the Listening Records part I think you can add something like: Now we have an initial a model. Remember that we use our Model via Controllers so let’s test our Products from a Home Controller calling a Strong Typed View.
2. Clarify and numerate the listing after listing 1:
var results = from p in _entities.ProductSet
where p.Price > 10.00m
orderby p.Name
select p;
To:
public ActionResult Index()
{
var results = from p in _entities.ProductSet
where p.Price > 10.00m
orderby p.Name
select p;
return View(results);
}
3. After listing 3 I think you can add a text like: Remember that our views can take some form fields and transform into a product object if they correspond to product class.
4. As you have seen in my recommendations I´m not an expert in .Net and I´m learning MVC. So this paragraph is not clear for me: “The object context can only track changes to an entity when the entity is attached to the context. When the ASP.NET MVC framework creates the entity as an action method parameter, the entity is not attached”. Can you explain it a little more? What is the “The object context”?
5. As a good practice of usability, the user must know the state of the system. In all your CRUD examples (Creating, Editing and Deleting), after Database changes, you return user to the Action Index. But the user how knows if the changed was OK? I think you can add this line to all CRUD actions, before return RedirectToAction(“Index”);
For example to delete action:
ViewData[“message”] = string.Format(“The product with ID {0} has been successfully deleted”, originalProduct.Id ) ;
6. You just begin the “Using the Repository Pattern” after present the CRUD Actions. But you didn´t mention about using Store Procedures. Will you talk about them later? In other chapter? It´ll be great mention about them: how use them? Are they necessary (because the model permits construct all a “Database logic”)? And maybe an example.
7. Neither you ´t mention about customize my model with custom methods. Earlier you mention about “returns all of the products that have a price greater than $10.00”. What if I´d like this query as a method. Something like _entities.ProductsGreaterThan (int value). An example with it will be great. With it you can truly create a Business Logic into the model.
8. After presenting the Generic Repository, you talk about Extending the Generic Repository. I think you can mention that all Repository can be extended. Maybe you can write an example for extending a repository before the “Using the Dependency Injection Pattern”.
9. In the Listing 14 of “Testing with a Mock Repository” appears a method that I don’t know: ModelState.AddModelError(). You mention in the Note that validation will be discussed in Capter 8, but We are in Model Chapter. It´ll be great that you explain that the Model has a state where you can bla, bla, bla… And, for example, you can manage message errors. Look in my Comment 5. I was thinking to manage messages via ViewData.
Well… That is all. Thanks for sharing.
You mention above that applies changes doesn’t ‘navigate’ through the object graph?
That being said, how would you save an object with a large object graph?
ie. does that include children added to collections?
object – Company
has Employees
You create an add an employee to the company employees list and goto save the company.
Are you saying that EF doesn’t support cascading save/update/delete calls ?
It would help to clarify that in your writing. I know it’s a book on asp.net mvc, but since you’ve done such a great job on the models part, expanding that a tad would really help readers that are wanting to understand how this works (or like someone who has worked with other orm’s like nhibernate where cascading is part of the mapping file)
Thanks
Is there any way you can post the code for this and the generic respository?
Not sure I agree with the whole Model part of the MVC implementation, seems to me that the Model is still part of presentation in the MVC framework. I think Model should be off in it’s own seperate project, but maybe thats just me.
Also in the Generic Repository IQueryable List () where T:class; seems to me that now the business logic can build dynamic queries, which I think crosses a layer boundry to some extent. Probably better to have a derivation method that does loose matching on an entity. Also I don’t think there is a guarentee with Queryable about when the query runs. Not sure I agree with the idea.
This is really a great series Stephen.
I think you do a disservice to Data Access Testing, though when you say it is not required. I agree fully with your requirement to set up a mock repository and test the business logic in that way. However, the data access should be unit tested as well, though from a separate assembly/project so that you do not run into the speed issues you describe of accessing a database. By having it in a separate assembly you have testability without major impaacts. This then makes it easier to make changes to the data access layer reliably, which is the purpose of a unit test.
Thanks for the chapters so far.
This is a great series. It seems there may be more need for understanding how we can go n-tier with ado entity at this point than mvc.
I agree with Evan that it seems like the repositories should go to somewhere else beside the model folder, possibly a separate project depending on the project size.
I also can see the argument for not using generics. It does get old copying implementations sometimes but come on what are we talking about here, two minutes or three to do a little copy and paste?
Nice, I’ll pass on the “DataSets on Steroids” approach called Entity Framework.
No persistence ignorance means shitty MVC.
Hi,
thank you for all your posts about ASP.NET MVC. I have a guestion or suggestion if you will. You wrote:
“This implementation of the IProductRepository interface uses the Microsoft Entity Framework. However, we could implement the IProductRepository interface with a class that uses Microsoft LINQ to SQL, NHibernate, or just about any other data access technology.”
Correct me, if I am wrong, but I think that (since you didn’t use something like IPoco adapter) your repository iterface depends on EF as well, so you cannot (easily) change the data access technology. For example Rob Coney in his MVC Storefront mapped objects returned by Linq2sql to his own bussines (POCO) objects. This makes in my opinon real repositories, which can be replaced easily.
I think you could use repository in many ways, if you follow Rob’s style, he’s much more DDD
I ordered (paid) a copy of your book from amazon since Dec. 28, 2008. According to amazon they will ship it on July 29, 2009 and I am wondering if it is possible to send me the implementation of the EFGenericRepository and its implementation while I wait for the book – Thanks.
Great Post! Very detailed explanation given about Models. Really very useful one.
Very Nice.
I have a question: how to pass modelw between actions? That is I display a model in one view and when I click a button, I would like to pass this model to another action.
Hope you could give me some advice.
Thanks for this essential blog.
fg
Well, Thanks for sharing.
Thanks.
Thanks essential blog.
It no longer uses LINQ to SQL classes directly.
This constructor creates an instance of the MovieRepository class and passes it to the second constructor.
The MoviesController class is taking advantage of a software design pattern called the Dependency Injection pattern.
The code interacts with an abstract interface instead of a concrete implementation of the interface.
This tutorial was to demonstrate, good.
advantage of this simple application in this tutorial.
It’s well work a read.
Curious on your thoughts.
Superb stuff, beautifully thought out description of model validation.
Great Informative post.
Really nice post.
This is really a great series Stephen.
Great Valuable Post.
Really, Very useful one this.
I really like this approach.
Really nice too.
Thanks for your write-up.
Hello Steve, great post.
thanks for script
eww w this is given in attachment. I understand that very well. Thanks.
I must say, very helpful tips for users. Thanks.
Thanks for your write-up.
Great post, thanks for sharing this information!
Every kind of multimedia players have their own strength and weaknesses. There are players who allows one to connect to the net. But since they are called media players, they must be compatible to variety of multimedia file formats.
.Thanks for sharing your views.. ( Professional Logo – stationery design )
If interested then for more info on it visit: my.safaribooksonline.com/…/ch05lev1sec1
dubai flats for rent | Abu Dhabi accommodation
f343
Green lantern: First Flight movie download
Green Street Hooligans 2 movie download
Guess Who movie download
Gumball 3000: The Movie movie download
Half Baked movie download
Hancock movie download
Hanger movie download
selam hi This sounds fascinating sıcak sohbet I’m going to read that tracing articlekısa aşk şiirleri when I have a moment.
Wow. erotik film izle is
şifalı bitkiler zayıflama de
çet sohbet fer
netlog ger
müzik dinle err
şarkı dinle
cüneyt arkın filmleri kk
isyan sözleri fer
hikayeler er
kadir inanır filmleri izle der
escort bayanlar der
bedava chat dd
chat odaları der
liseli kızlar derf
kızlarla sohbet fder
sohbet err