Tuesday, March 15, 2011

Another Entity Framework 4 Repository & Unit of Work Solution–Intro and Part 3: Generated Code Review

This is Part 3 in a series of posts that walk through the process of implementing a solution that separates concerns into specific behaviors using the Entity Framework 4 and the Repository and Unit of Work Patterns.

Below is the series outline; again, this is Part 3.
Now on to Part 3… Open up the Chinook solution in Visual Studio.  Expand the Chinook.Core Project, expand the Domain directory, and then expand both the Domain.Poco.tt and Domain.Poco.Metadata.tt files.  You should see something like the following: image These child-files under the *.tt file are the code file generated by the T4 templates.  Go ahead an open the highlighted file – InvoiceLine.Metadata.cs and this file’s sister class file Invoice.cs (under the Domain.Poco.tt file).  The Invoice.cs file contains a POCO class for the Invoice entity of the EF data model (edmx), while the InvoiceLine.Metadata.cs contains the data annotations/metadata ‘buddy’ class for the sister POCO class (Invoice.cs).  That’s quite a mouthful, so take a bit of time and review the generated code. Okay, now expand the Domain and Services directories and the associated T4 template files within the Chinook.Core project.  You should have something similar to the following: image Open up the following Repository.Interface.tt child files.  Reference the code snippets below when reading the brief description of each file.
  • IReadOnlyRepository.cs
    • The IReadOnlyRepository is the base interface that all the entity specific interfaces inherit from.  The IReadOnlyRepository contains (you guessed it…Winking smile) read-only interfaces to the persistence layer.
  • IRepository.cs
    • The IRepository interface extends the IReadOnlyIRepository interface to include creating and deleting or entities.
    • The original intent of separating the IReadOnlyRepository and IRepository interfaces was that so POCO object backed by data views (VIEWs on a database) would not inherit creating and deleting functionality.
  • IUnitOfWork.cs
    • The IUnitOfWork interface handles all the transactions and changes needed to persist modifications to the persistence mechanism. 

IReadOnlyRepository.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Chinook.Core.Repository
{
    /// <summary>
    /// Generic Read-Only Repository Contract
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IReadOnlyRepository<t> where T : class
    {
        /// <summary>
        /// Gets an IQueryable sequence of entities of type T.
        /// </summary>
        /// <returns></returns>
        IQueryable<t> Queryable();

        /// <summary>
        /// Gets an IEnumerable sequence of all entites of type T.
        /// </summary>
        /// <returns></returns>
        IEnumerable<t> All();

        /// <summary>
        /// Get an IEnumerable sequence of entities of type T filtered on the @where predicate.
        /// </summary>
        /// <param name="where">The where predicate.</param>
        /// <returns></returns>
        IEnumerable<t> Find(Expression<func><t bool ,>> where);

        /// <summary>
        /// Gets a single entity in a sequence of entities of type T using the filtered @where predicate.
        /// </summary>
        /// <param name="where">The where predicate.</param>
        /// <returns></returns>
        T Single(Expression<func><t bool ,>> where);

        /// <summary>
        /// Gets the first entity in a sequence of entities of type T using the filtered @where predicate.
        /// </summary>
        /// <param name="where">The where predicate.</param>
        /// <returns></returns>
        T First(Expression<func><t bool ,>> where);

        /// <summary>
        /// Gets a single entity (or default of entity of type T) in a sequence of entities of type T using the filtered @where predicate.
        /// </summary>
        /// <param name="where">The where predicate.</param>
        /// <returns></returns>
        T SingleOrDefault(Expression<func><t bool ,>> where);

        /// <summary>
        /// Gets a first entity (or default entity of type T) in a sequence of entities of type T using the filtered @where predicate.
        /// </summary>
        /// <param name="where">The where predicate.</param>
        /// <returns></returns>
        T FirstOrDefault(Expression<func><t bool ,>> where);
    }
}

IRepository.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Chinook.Core.Repository
{ 
    /// <summary>
    /// Generic Repository Contract.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IRepository<t> : IReadOnlyRepository<t> where T : class
    {
        /// <summary>
        /// Adds the specified entity to the respository of type T.
        /// </summary>
        /// <param name="entity">The entity to add.</param>
        void Add(T entity);

        /// <summary>
        /// Deletes the specified entity to the respository of type T.
        /// </summary>
        /// <param name="entity">The entity to delete.</param>
        void Delete(T entity);

        /// <summary>
        /// Attaches the specified entity to the respository of type T.
        /// </summary>
        /// <param name="entity">The entity to attach.</param>
        void Attach(T entity);
    }
}
IUnitOfWork.cs
namespace Chinook.Core.Repository
{ 
 /// <summary>
    /// Unit of Work Contract
    /// </summary>
    public interface IUnitOfWork
    {
        /// <summary>
        /// Commits the changes to the data store.
        /// </summary>
        void Commit();
    }
}
The other Repository interfaces are just POCO specific Repository interfaces – man, that sounds really redundant… The Services Interfaces – child-files of the Services.Interface.tt template are just empty service interfaces that you can add to as needed.  Like the Repository interfaces, each POCO has it’s own Service interface. Next, expand the Chinook.Infrastructure Project’s Services.Implementation.tt file.  The code files generated by this T4 template are concrete implementation class of the aforementioned Service interfaces.  There is a one-to-one relationship among the code generated interfaces to the code generated concrete implementations. Next, expand the Chinook.Data.EF Project’s Repository.Implementation.EF.tt file. image As you can see, there is pretty much a one-one-relationship among the interfaces generated in the Chinook.Core/Repository to the concrete implementations here.  Go ahead and spend some time looking over the generated files.  The implementation for each is straight forward. Beyond the Repository implementations, the following are notable files generated by the Repository.Implementation.EF.tt template:
  • IObjectContext
    • this is an interface that defines the contract used by the ObjectContextAdapter, the EFUnitOfWork, and ultimately the ObjectContext.
  • ObjectContextAdapter
    • this is a simple adapter class that wraps the Entity Framework’s ObjectContext object for use in the Repository/Unit of Work paradigm.  This class implements the IObjectContext contract.
  • RepositoryIQueryableExtensions
    • this class includes a single method: Include.  Include is an IQueryable(of T) extension method that affords the ‘Eager Loading’ functionality provided by the Entity Framework.  But, since our solution abstracts the Entity Framework, the EF ‘Eager Loading’ Include method is not available.  This extension method gives our solution the functionality back.  You may not realize this yet, but that is a really cool ‘feature!’
    • The implementation of this Include extension method was taken from the following article on MSDN: http://msdn.microsoft.com/en-us/ff714955.aspx – this is a great article and one that I got quite a few ideas for this series and solution.
Template Bonuses
Template Force Code (Re)Generation – or NOT
Of this six T4 templates included in the download, all but the Domain.Poco.tt contain the following settings:
// answers the question: Force generation of file even if the code file exists?
bool forceCodeGeneration = false;
As the name of the setting/variable and the associated comment suggests, this is an on/off switch for forcing code (re)generation.  The default for this setting is false, meaning that if code is regenerated, the existing code will NOT be overwritten.  So, let’s say that you create a custom email validator like the one in a previous post of mine - ASP.NET 3 Custom Validation.  You manually decorate a few POCO properties in the POCO ‘buddy’ classes – the one containing metadata and data annotations – with the new email validator (e.g. Required(ErrorMessage = “Email is Required”)].  You have client-side and server-side validation working like a charm.  Now, let’s say that your one or your database tables has changed and or you added a new table.  You are going to want to regenerate all the classes and interfaces that the T4 templates created for you.  No Problem!  If you have the forceCodeGeneration settings set to false, your manual code changes will NOT get overwrittenand that hard work of yours will not need to be redone, or at a minimum, merged from the files in you source control system.  You are using a source control system, aren’t you???
StructureMap configuration for Repositories and Services
If you’re reading this blog series, I’m sure you’re familiar with Dependency Injection (DI).  There are quite a few DI containers for .NET; however, my favorite is StructureMap.  Why is it my favorite?  Well, when I first endeavored on the DI/IOC concept, StructureMap is what those around me recommended.  I was not disappointed!  The documentation could be better – it’s a little out-of-date; however, the DI API for StructureMap is extremely easy to use and relatively easy to understand.  You will first need to overcome the DI/IOC paradigm challenges, but once the 'light goes on,’ you’ll wonder how you survived without it… Okay, back to the Bonuses…  Expand the Repository.Implementation.EF.tt and Service.Implementation.tt T4 templates.  Below are screenshots of the two T4 template generated outputs.  Notice the highlighted files… image image All four of these files contain StructureMap Dependency Injection registration code.  The files stating with __StructureMap.* contain ‘raw’ registration code that you can use however you’d like.  The other two files (EFRepositoryRegistry.cs.txt and ServicesRegistry.cs.txt) contain StructureMap Registry derived classes.  According to the StructureMap documentation, Registries are the recommended way to configure StructureMap:
“The Registry DSL is the recommended way to configure StructureMap, and creating Registry classes is the recommended way of using the Registry DSL.”

We will be using the code in these files in the next post in this series… Stay tuned…

In summary, we reviewed a few of the code files that the T4 templates generated, discussed how to NOT to overwrite your manual code changes to the generated files, and discovered how the T4 templates generate StuctureMap DI registration snippets and Registries for you.

In the next post in this series, we will put all the fruits of our labor (okay, the code generated labor) to use in an ASP.NET MVC 3 Web Application.

Thanks for reading…

2 comments:

Onur Demirel said...

Great article,Thanks a lot..

tdryan said...

@Onur - thanks for the kind words...