HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Repository Pattern universal application

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
patternuniversalrepositoryapplication

Problem

When I started learning Repository Pattern with Unity few days ago I was under impression that the main benefit of this pattern is the separation of data layer from the business layer.

In other words, if there is a need to change the way, how application stores the data, it's very easy as only one main model takes care of the communication.

This means, that if application currently saves data into a serialized XML files, it would not be very difficult to change this logic to connect to database instead.

I have found few nice demos that are also using Unit Of Work layer, which seemed very handy. Let me show you a bit of the code I have.

public class UnitOfWork : IUnitOfWork
{
    private readonly RepositoryContext _context;
    public IEmployeeRepository Employees { get; set; }

    public UnitOfWork(RepositoryContext context)
    {
        _context = context;
        Employees = new EmployeeRepository(_context);
    }

    public int Complete()
    {
        return _context.SaveChanges();
    }

    public void Dispose()
    {
        _context.Dispose();
    }
}


Main Repository Context:

public class RepositoryContext : DbContext
{
    public RepositoryContext() : base("name=RepositoryContext")
    {
    }

    public virtual DbSet Employees { get; set; }
    public virtual DbSet Furniture { get; set; }
}


And here is the demo EmployeeRepository:

public class EmployeeRepository:Repository, IEmployeeRepository
{
    public EmployeeRepository(RepositoryContext context) : base(context) { }

    public Employee GetEmployeeByName(string sName)
    {
        return MyContext.Employees.FirstOrDefault(n => n.Name == sName);
    }

    public RepositoryContext MyContext
    {
        get { return Context as RepositoryContext; }
    }
}


Employee Repository derives from a generic Repository which looks like this:

```
public class Repository : Interfaces.Repositories.IRepository where T : class
{
protected readonly DbContext Context;

Solution

What we are looking at is a long tutorial on Inversion of Control and dependency injection. Basically at the most simpilest form you will be looking to abstract your data layer from you application layer and provide interfaces for your IoC container to deliver to the application.

This can get quite confusing however all we have to do is remeber our application will be using the interfaces to work with the data layer and not the actual implementation.

So for your Unit of work you would resolve IUnityOfWork interface instead. Now here is a quick example I put together. It is by no means a finished or tested product (and the xml side is very fragile) but shows how this can work.

First off I am using the interface IRepository to access the data layer. This can be connected database through EntityFramework or Xml through serialization. This repository is simple.

public partial interface IRepository
    where T : BaseEntity
{
    T GetById(int id);

    T Insert(T item);

    T Update(T item);

    void Delete(T item);

    IQueryable Entities { get; }
}


As simple interface the has the standard insert\update\delete and queryable methods. It is worth noting that I am referencing BaseEntity which is a simple entity with an public int Id { get; set; } property.

Now for Database connections I have created an implementation called EfRepository

public class EfRepository : IRepository
    where T : BaseEntity
{

    public EfRepository(DbContext dbContext)
    {
        if (dbContext == null)
            throw new ArgumentNullException("dbContext");
        _dbContext = dbContext;
    }

    readonly DbContext _dbContext;
    DbSet _entities;

    /// 
    /// Gets the entities as a querably
    /// 
    public IQueryable Entities { get { return DbEntities; } }

    protected virtual DbSet DbEntities { get { return _entities ?? (_entities = _dbContext.Set()); } }

    public void Delete(T item)
    {
        DbEntities.Remove(item);

        _dbContext.SaveChanges();
    }

    public T GetById(int id)
    {
        return DbEntities.Find(id);
    }

    public T Insert(T item)
    {
        DbEntities.Add(item);
        _dbContext.SaveChanges();
        return item;
    }

    public T Update(T item)
    {
        _dbContext.SaveChanges();
        return item;
    }
}


Now this is a very simple class that connects to my Data layer via entity framework. (We will talk about the constructor shortly).

Next I have an Xml Repository named XmlRepository

public class XmlRepository : IRepository
    where T : BaseEntity
{
    public XmlRepository(XmlContext xmlContext)
    {
        _xmlContext = xmlContext;
    }

    readonly XmlContext _xmlContext;
    IList _entities;

    public IQueryable Entities
    {
        get
        {
            return XmlEntities.AsQueryable();
        }
    }

    protected IList XmlEntities
    {
        get
        {
            return _entities ?? (_entities = _xmlContext.Set());
        }
    }

    public void Delete(T item)
    {
        if (XmlEntities.Any(i => i.Id == item.Id))
        {
            XmlEntities.Remove(item);
            _xmlContext.SaveChanges(XmlEntities);
        }
    }

    public T GetById(int id)
    {
        return Entities.FirstOrDefault(x => x.Id == id);
    }

    public T Insert(T item)
    {
        if (item.Id == default(int)) //only add if id = default(int)
        {
            var lastEntity = Entities.LastOrDefault();
            if (lastEntity != null)
                item.Id = lastEntity.Id + 1;
            else
                item.Id = 1;

            XmlEntities.Add(item);
            _xmlContext.SaveChanges(XmlEntities);
        }
        return item;
    }

    public T Update(T item)
    {
        _xmlContext.SaveChanges((IList)Entities);
        return item;
    }
}


Again really simple implementation and also has another constructor reference (XmlContext).

We will get to resolution and setup of Unity shortly but given this we now have a two generic data access repositories. One for EntityFramework and one for Xml File Store.

Now back to the EfRepository it has a constructor parameter DbContext dbContext this is required as we need access to the underlying data context and connections. Now this is actually an injected class of type EfDataContext which has an initializer and a few overloads (plus dependency injection).

```
public class EfDataContext : DbContext
{
public static void Initialize()
{
var connectionFactory = new SqlConnectionFactory();
#pragma warning disable CS0618
Database.DefaultConnectionFactory = connectionFactory;
#pragma warning restore CS0618
}

public EfDataContext(ApplicationSettings applicationSettings)
: base(applicationSettings.DataConnectionString)
{
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new TestEntityMap());
base.OnModelCreat

Code Snippets

public partial interface IRepository<T>
    where T : BaseEntity
{
    T GetById(int id);

    T Insert(T item);

    T Update(T item);

    void Delete(T item);

    IQueryable<T> Entities { get; }
}
public class EfRepository<T> : IRepository<T>
    where T : BaseEntity
{

    public EfRepository(DbContext dbContext)
    {
        if (dbContext == null)
            throw new ArgumentNullException("dbContext");
        _dbContext = dbContext;
    }

    readonly DbContext _dbContext;
    DbSet<T> _entities;

    /// <summary>
    /// Gets the entities as a querably
    /// </summary>
    public IQueryable<T> Entities { get { return DbEntities; } }

    protected virtual DbSet<T> DbEntities { get { return _entities ?? (_entities = _dbContext.Set<T>()); } }

    public void Delete(T item)
    {
        DbEntities.Remove(item);

        _dbContext.SaveChanges();
    }

    public T GetById(int id)
    {
        return DbEntities.Find(id);
    }

    public T Insert(T item)
    {
        DbEntities.Add(item);
        _dbContext.SaveChanges();
        return item;
    }

    public T Update(T item)
    {
        _dbContext.SaveChanges();
        return item;
    }
}
public class XmlRepository<T> : IRepository<T>
    where T : BaseEntity
{
    public XmlRepository(XmlContext xmlContext)
    {
        _xmlContext = xmlContext;
    }

    readonly XmlContext _xmlContext;
    IList<T> _entities;

    public IQueryable<T> Entities
    {
        get
        {
            return XmlEntities.AsQueryable();
        }
    }

    protected IList<T> XmlEntities
    {
        get
        {
            return _entities ?? (_entities = _xmlContext.Set<T>());
        }
    }

    public void Delete(T item)
    {
        if (XmlEntities.Any(i => i.Id == item.Id))
        {
            XmlEntities.Remove(item);
            _xmlContext.SaveChanges(XmlEntities);
        }
    }

    public T GetById(int id)
    {
        return Entities.FirstOrDefault(x => x.Id == id);
    }

    public T Insert(T item)
    {
        if (item.Id == default(int)) //only add if id = default(int)
        {
            var lastEntity = Entities.LastOrDefault();
            if (lastEntity != null)
                item.Id = lastEntity.Id + 1;
            else
                item.Id = 1;

            XmlEntities.Add(item);
            _xmlContext.SaveChanges(XmlEntities);
        }
        return item;
    }

    public T Update(T item)
    {
        _xmlContext.SaveChanges((IList<T>)Entities);
        return item;
    }
}
public class EfDataContext : DbContext
{
    public static void Initialize()
    {
        var connectionFactory = new SqlConnectionFactory();
#pragma warning disable CS0618
        Database.DefaultConnectionFactory = connectionFactory;
#pragma warning restore CS0618
    }

    public EfDataContext(ApplicationSettings applicationSettings)
        : base(applicationSettings.DataConnectionString)
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new TestEntityMap());
        base.OnModelCreating(modelBuilder);
    }
}
public partial class XmlContext
{
    public XmlContext(ApplicationSettings appSettings)
    {
        _xmlFileLocation = appSettings.DataConnectionString;
    }

    readonly string _xmlFileLocation;

    public void SaveChanges<T>(IList<T> entities)
        where T : BaseEntity
    {
        if (entities == null)
            return;

        string filePath = Path.Combine(_xmlFileLocation, $"{typeof(T).Name}.xml");
        using (var writer = new StreamWriter(filePath))
        {
            var serializer = new XmlSerializer(typeof(List<T>));
            serializer.Serialize(writer, entities);
            writer.Flush();
        }
    }

    public IList<T> Set<T>()
        where T : BaseEntity
    {
        string filePath = Path.Combine(_xmlFileLocation, $"{typeof(T).Name}.xml");
        IList<T> entities = new List<T>();
        if (File.Exists(filePath))
        {
            //XDocument doc = XDocument.Load(filePath);
            using (var stream = File.OpenRead(filePath))
            {
                var serializer = new XmlSerializer(typeof(List<T>));
                entities = serializer.Deserialize(stream) as List<T>;
            }
        }
        return entities.OrderBy(e => e.Id).ToList();
    }
}

Context

StackExchange Code Review Q#122845, answer score: 6

Revisions (0)

No revisions yet.