DCSIMG
Repository and Unit of Work T4 Template for Entity Framework - Gil Fink's Blog

Gil Fink's Blog

Fink about IT

News

Microsoft MVP

My Facebook Profile My Twitter Profile My Linkedin Profile

Locations of visitors to this page

Creative Commons License

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.
© Copyright 2013 Gil Fink

Hebrew Articles

Index Pages

My OSS Projects

English Articles

Repository and Unit of Work T4 Template for Entity Framework

Repository and Unit of Work T4 Template for Entity Framework

Two weeks ago IRepository and Unit of Work T4 Template for Entity Framework
wrote the
Revisiting the Repository
and Unit of Work Patterns
with Entity Framework

post. One thing that I
thought would be nice
was to have an automatic
code generation that will help me to build these patterns without sweating.
So I sat down and created a T4 Template to auto generate the same patterns
that I showed in the post.

The Code

One thing to understand is that the provided T4 Template isn’t bullet
proof and errors can occur (you can change the implementation as you
like). In order to use it copy and paste the code to a .tt file that needs
to be located in the library of your Entity Data Model file
(the T4 seeks edmx files in the current directory to auto generate the
classes). Another option is to download the T4 Template from here
So here it is:

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ import namespace="System.IO" #>
<#@ output extension=".cs" #>
<#         
if(Errors.HasErrors)
{
    return String.Empty;
}
 
CodeGenerationTools code = new CodeGenerationTools(this){FullyQualifySystemTypes = true, CamelCaseFields = false};
MetadataLoader loader = new MetadataLoader(this);
 
string open = "<";
string close = ">";
string SourceCsdlPath = FindEDMXFileName();
ReferenceCsdlPaths = new string[] {};
string namespaceName = code.VsNamespaceSuggestion();
ItemCollection = loader.CreateEdmItemCollection(SourceCsdlPath, ReferenceCsdlPaths.ToArray());
EntityContainer container = ItemCollection.GetItems<EntityContainer>().FirstOrDefault();
#>
using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
 
namespace <#=namespaceName#>
{
    public interface IRepository<T> where T : class
    {    
        #region    Methods
    
        T GetById(int id);
        IEnumerable<T> GetAll();
        IEnumerable<T> Query(Expression<Func<T, bool>> filter);        
        void Add(T entity);
        void Remove(T entity);   
        
        #endregion
    }
    
    public abstract class Repository<T> : IRepository<T>
                                  where T : class
    {
        #region Members
 
        protected IObjectSet<T> _objectSet;
 
        #endregion
 
        #region Ctor
 
        public Repository(ObjectContext context)
        {
              _objectSet = context.CreateObjectSet<T>();
        }
 
        #endregion
 
        #region IRepository<T> Members
 
        public IEnumerable<T> GetAll()
        {
              return _objectSet;
        }
 
        public abstract T GetById(int id);
 
        public IEnumerable<T> Query(Expression<Func<T, bool>> filter)
        {
              return _objectSet.Where(filter);
        }
 
        public void Add(T entity)
        {
              _objectSet.AddObject(entity);
        }
 
        public void Remove(T entity)
        {
              _objectSet.DeleteObject(entity);
        }
 
        #endregion
      }
 
<#
    foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
    {        
#>
    
    public partial class <#= entity.Name #>Repository : Repository<#=open#><#=entity.Name#><#=close#>
    {
        #region Ctor
 
        public <#= entity.Name #>Repository(ObjectContext context)
               : base(context)
        {
        }
 
        #endregion
 
        #region Methods
 
        public override <#= entity.Name #> GetById(int id)   
        {
            return _objectSet.SingleOrDefault(e => e.<#= entity.KeyMembers.First().Name #> == id);
        }
 
        #endregion        
    }
<# 
    }        
#>
        
  public interface IUnitOfWork
  {
      #region    Methods
    
    <#
        foreach (EntitySet set in container.BaseEntitySets.OfType<EntitySet>())
        {    
    #>
        IRepository<#= open #><#= set.ElementType.Name #><#= close #> <#= set.Name #> { get; }   
    <# 
        }
    #>
    void Commit();
    
    #endregion
  }
 
  public partial class UnitOfWork : IUnitOfWork
  {
    #region Members
 
    private readonly ObjectContext _context;
    <#
        foreach (EntitySet set in container.BaseEntitySets.OfType<EntitySet>())
        {    
    #>
    private <#= set.ElementType.Name #>Repository _<#= set.Name.ToLower() #>;
    <# 
        }
    #>    
    #endregion
 
    #region Ctor
 
    public UnitOfWork(ObjectContext context)
    {
      if (context == null)
      {
        throw new ArgumentNullException("context wasn't supplied");
      }
 
      _context = context;
    }
 
    #endregion
 
    #region IUnitOfWork Members
 
    <#
        foreach (EntitySet set in container.BaseEntitySets.OfType<EntitySet>())
        {    
    #>
    public IRepository<#= open #><#= set.ElementType.Name #><#= close #> <#= set.Name #>
    {
        get
        {
            if (_<#= set.Name.ToLower() #> == null)
            {
                _<#= set.Name.ToLower() #> = new <#= set.ElementType.Name #>Repository(_context);
            }
            return _<#= set.Name.ToLower() #>;
        }
    }
    <# 
        }
    #>    
    
    public void Commit()
    {
      _context.SaveChanges();
    }
 
    #endregion
  }
}
<#+
public string SourceCsdlPath{ get; set; }
public EdmItemCollection ItemCollection{ get; set; }
public IEnumerable<string> ReferenceCsdlPaths{ get; set; }
 
string FindEDMXFileName()
{            
    string[] entityFrameworkFiles = Directory.GetFiles(Host.ResolvePath(string.Empty), "*.edmx");
    if(entityFrameworkFiles.Length > 0)
    {
        return entityFrameworkFiles[0];
    }
    
    return string.Empty;
}
#>

 

and this is the generated code that I get after running the T4 Template
on my testing edmx file:

 
using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
 
namespace ConsoleApplication1
{
    public interface IRepository<T> where T : class
    {    
        #region    Methods
    
        T GetById(int id);
        IEnumerable<T> GetAll();
        IEnumerable<T> Query(Expression<Func<T, bool>> filter);        
        void Add(T entity);
        void Remove(T entity);   
        
        #endregion
    }
    
    public abstract class Repository<T> : IRepository<T>
                                  where T : class
    {
        #region Members
 
        protected IObjectSet<T> _objectSet;
 
        #endregion
 
        #region Ctor
 
        public Repository(ObjectContext context)
        {
              _objectSet = context.CreateObjectSet<T>();
        }
 
        #endregion
 
        #region IRepository<T> Members
 
        public IEnumerable<T> GetAll()
        {
              return _objectSet;
        }
 
        public abstract T GetById(int id);
 
        public IEnumerable<T> Query(Expression<Func<T, bool>> filter)
        {
              return _objectSet.Where(filter);
        }
 
        public void Add(T entity)
        {
              _objectSet.AddObject(entity);
        }
 
        public void Remove(T entity)
        {
              _objectSet.DeleteObject(entity);
        }
 
        #endregion
      }
 
    
    public partial class CourseRepository : Repository<Course>
    {
        #region Ctor
 
        public CourseRepository(ObjectContext context)
               : base(context)
        {
        }
 
        #endregion
 
        #region Methods
 
        public override Course GetById(int id)   
        {
            return _objectSet.SingleOrDefault(e => e.CourseID == id);
        }
 
        #endregion        
    }
    
    public partial class DepartmentRepository : Repository<Department>
    {
        #region Ctor
 
        public DepartmentRepository(ObjectContext context)
               : base(context)
        {
        }
 
        #endregion
 
        #region Methods
 
        public override Department GetById(int id)   
        {
            return _objectSet.SingleOrDefault(e => e.DepartmentID == id);
        }
 
        #endregion        
    }
    
    public partial class EnrollmentRepository : Repository<Enrollment>
    {
        #region Ctor
 
        public EnrollmentRepository(ObjectContext context)
               : base(context)
        {
        }
 
        #endregion
 
        #region Methods
 
        public override Enrollment GetById(int id)   
        {
            return _objectSet.SingleOrDefault(e => e.EnrollmentID == id);
        }
 
        #endregion        
    }
    
    public partial class PersonRepository : Repository<Person>
    {
        #region Ctor
 
        public PersonRepository(ObjectContext context)
               : base(context)
        {
        }
 
        #endregion
 
        #region Methods
 
        public override Person GetById(int id)   
        {
            return _objectSet.SingleOrDefault(e => e.PersonID == id);
        }
 
        #endregion        
    }
        
  public interface IUnitOfWork
  {
      #region    Methods
    
            IRepository<Course> Courses { get; }   
            IRepository<Department> Departments { get; }   
            IRepository<Enrollment> Enrollments { get; }   
            IRepository<Person> People { get; }   
        void Commit();
    
    #endregion
  }
 
  public partial class UnitOfWork
  {
    #region Members
 
    private readonly ObjectContext _context;
        private CourseRepository _courses;
        private DepartmentRepository _departments;
        private EnrollmentRepository _enrollments;
        private PersonRepository _people;
        
    #endregion
 
    #region Ctor
 
    public UnitOfWork(ObjectContext context)
    {
      if (context == null)
      {
        throw new ArgumentNullException("context wasn't supplied");
      }
 
      _context = context;
    }
 
    #endregion
 
    #region IUnitOfWork Members
 
        public IRepository<Course> Courses
    {
        get
        {
            if (_courses == null)
            {
                _courses = new CourseRepository(_context);
            }
            return _courses;
        }
    }
        public IRepository<Department> Departments
    {
        get
        {
            if (_departments == null)
            {
                _departments = new DepartmentRepository(_context);
            }
            return _departments;
        }
    }
        public IRepository<Enrollment> Enrollments
    {
        get
        {
            if (_enrollments == null)
            {
                _enrollments = new EnrollmentRepository(_context);
            }
            return _enrollments;
        }
    }
        public IRepository<Person> People
    {
        get
        {
            if (_people == null)
            {
                _people = new PersonRepository(_context);
            }
            return _people;
        }
    }
        
    
    public void Commit()
    {
      _context.SaveChanges();
    }
 
    #endregion
  }
}

Enjoy!

Comments

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# July 5, 2010 2:22 PM

The Morning Brew - Chris Alcock » The Morning Brew #636 said:

Pingback from  The Morning Brew - Chris Alcock  &raquo; The Morning Brew #636

# July 6, 2010 10:25 AM

Twitter Trackbacks for Repository and Unit of Work T4 Template for Entity Framework - Gil Fink on .Net [microsoft.co.il] on Topsy.com said:

Pingback from  Twitter Trackbacks for                 Repository and Unit of Work T4 Template for Entity Framework - Gil Fink on .Net         [microsoft.co.il]        on Topsy.com

# July 7, 2010 2:48 PM

Edmilson said:

Hi Gil, very good post!

I´d like to ask you to talk, in one future post,  about more then one .edmx  file  in  one app  and  how could us work with Repository and Unit of Work  in  one  app  with  hundreads  of Entities  Should  we  separate that in various  Units of Works  or put all togheter in just one?

If I use more then  one .edmx  how I should do?

thanks

Edmilson

# July 12, 2010 5:45 PM

Nick said:

Hi Gil,

Thank you for a great post and the templates.

Can I ask where do you get the context from? The context to pass to the UnitOfWork's constructor. It is generated separately?

Just a side question, when you were doing the templates, were you using tangible t4 editor add-on? It's supposed to have intellisense support, but I found the it's not really working, say when I try to edit the template, am I missing a couple of Import namespace statements? I'm new to t4.

Thank you

Nick

# August 11, 2010 6:05 AM

Gil Fink said:

@Nick,

The context can be injected using Dependency Injection methods (since ObjectContext is an abstract class). Also, if you aren't using DI you could just create the context outside with its relevant lifetime (for example per request in web applications).

For the second question, yes I was using the tangible t4 editor add-on and it support intellisense. In order to get the intellisense you need to use import and include directives.

# August 11, 2010 7:47 AM

Nick said:

@Gill

Thank you so much for the answers!

I managed to fix the intellisense, it was very strange and after trying a couple of other templates with the same issues re-emerging I've just reinstalled it and it now works as it should.

Re the object context: I'm sorry I wasn't clear with my question. When I ran your templates I could find the actual class that contains the hard-coded implementation, like you get when you use POCO generation template(it creates Model.Context.tt). I realise that you can pass any context implementation using DI. I dont think I could use DI without specifying which context implementation to use or am I wrong?

Thanks again!

Nick

# August 12, 2010 4:36 AM

Ismar said:

Hi Gil,

finally i find complete example on how implementing Repository and Unit of Work patterns together with EF 4. Many thanks!

I have one question; could you modify the template so it creates one file per class/interface, and is it possible to place these generated files in folders (for instance "Repositories" and "UnitOfWork"? Right now i have only found examples where generated files are directly connected (as least in Solution Explorer) to the .tt file, but i hope it's possible to separate generated files from template?

Thanks in advance!

Ismar

# September 2, 2010 1:31 AM

Gil Fink said:

@Ismar

Currently, T4 templates don't support the creation of file per class/interface.

The generated code is connected to the .tt file.

# September 2, 2010 7:51 AM

Tips for programmer said:

Pingback from  Tips for programmer

# March 9, 2012 4:24 PM