patterncsharpMinor
Repository Pattern universal application
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
Main Repository Context:
And here is the demo EmployeeRepository:
Employee Repository derives from a generic
```
public class Repository : Interfaces.Repositories.IRepository where T : class
{
protected readonly DbContext Context;
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
First off I am using the interface
As simple interface the has the standard insert\update\delete and queryable methods. It is worth noting that I am referencing
Now for Database connections I have created an implementation called
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
Again really simple implementation and also has another constructor reference (
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
```
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
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
XmlRepositorypublic 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.