Skip to content

Instantly share code, notes, and snippets.

@klmallory
Created June 14, 2017 16:55
Show Gist options
  • Save klmallory/ef5e8aef92bba91998926137d5a08850 to your computer and use it in GitHub Desktop.
Save klmallory/ef5e8aef92bba91998926137d5a08850 to your computer and use it in GitHub Desktop.
Simple Entity Framework Repository Pattern With Nesting Units of Work
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Patterns.Repository;
namespace System.Data.Entity.Repository
{
public class EntityRepository<EntityType> : IRepository<EntityType> where EntityType : class
{
public EntityRepository(string name, IServiceLocator locator)
{
this.name = name;
this.locator = locator;
this.unitOfWorkStore = locator.Resolve<IUnitOfWorkStore>(name);
}
static readonly object syncRoot = new object();
[ThreadStatic]
IUnitOfWork transientUow;
public IUnitOfWork TransientUow
{
get
{
lock (syncRoot)
if (this.transientUow == null)
this.transientUow = BeginUnitOfWork();
return this.transientUow;
}
}
string name;
IServiceLocator locator;
IUnitOfWorkStore unitOfWorkStore;
public IUnitOfWork BeginUnsafeUnitOfWork()
{
var uow = this.locator.Resolve<IUnitOfWork>(name + "_unsafe");
unitOfWorkStore.ActiveUnitOfWork = uow;
return uow;
}
public IUnitOfWork BeginUnitOfWork()
{
var uow = this.locator.Resolve<IUnitOfWork>(name + "_safe");
unitOfWorkStore.ActiveUnitOfWork = uow;
return uow;
}
public IUnitOfWork BeginLockedUnitOfWork()
{
var uow = this.locator.Resolve<IUnitOfWork>(name + "_locked");
unitOfWorkStore.ActiveUnitOfWork = uow;
return uow;
}
public IQueryable<EntityType> Query()
{
var uow = this.unitOfWorkStore.ActiveUnitOfWork;
if (uow != null)
{
return uow.Get<EntityType>();
}
else
{
return TransientUow.Get<EntityType>();
}
}
public void Integrate(EntityType entity)
{
var uow = this.unitOfWorkStore.ActiveUnitOfWork;
if (uow == null)
throw new InvalidOperationException("No Active Unit Of Work to Integrate With");
uow.Integrate<EntityType>(entity);
}
public void Add(EntityType entity)
{
var uow = this.unitOfWorkStore.ActiveUnitOfWork;
if (uow == null)
throw new InvalidOperationException("No working context for Add.");
uow.Add(entity);
}
public void Add(List<EntityType> entities)
{
var uow = this.unitOfWorkStore.ActiveUnitOfWork;
if (uow == null)
throw new InvalidOperationException("No working context for Add.");
uow.Add(entities);
}
public void Remove(EntityType entity)
{
var uow = this.unitOfWorkStore.ActiveUnitOfWork;
if (uow == null)
throw new InvalidOperationException("No working context for Remove.");
uow.Remove(entity);
}
public void Remove(List<EntityType> entities)
{
var uow = this.unitOfWorkStore.ActiveUnitOfWork;
if (uow == null)
throw new InvalidOperationException("No working context for Remove.");
uow.RemoveRange(entities);
}
public void Dispose()
{
lock (syncRoot)
if (transientUow != null)
{
transientUow.Dispose();
transientUow = null;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace Sysytem.Patterns.Repository
{
public interface IRepository<EntityType> : IDisposable where EntityType : class
{
void Add(List<EntityType> entities);
void Add(EntityType entity);
IUnitOfWork BeginLockedUnitOfWork();
IUnitOfWork BeginUnitOfWork();
IUnitOfWork BeginUnsafeUnitOfWork();
IQueryable<EntityType> Query();
void Remove(List<EntityType> entities);
void Remove(EntityType entity);
void Integrate(EntityType entity);
}
}
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace System.Patterns.Repository
{
public interface IUnitOfWork : IDisposable
{
Guid DataContextHandle { get; }
int ThreadId { get; }
bool ValidState { get; }
IQueryable<EntityType> Get<EntityType>() where EntityType : class;
IDisposable BeginTransaction();
void AddRange<EntityType>(IEnumerable<EntityType> entities) where EntityType : class;
void Add<EntityType>(EntityType entity) where EntityType : class;
void Remove<EntityType>(EntityType entity) where EntityType : class;
void RemoveRange<EntityType>(IList<EntityType> entities) where EntityType : class;
void Integrate<EntityType>(EntityType entity) where EntityType :class;
EntityType Create<EntityType>() where EntityType : class;
void Commit();
event UnitOfWorkCommitting UnitOfWorkCommitting;
event UnitOfWorkCanceling UnitOfWorkCanceling;
}
}
using System.Collections.Generic;
using System.Threading;
using System;
using System.Linq;
using System.Text;
namespace System.Patterns.Repository
{
public interface IUnitOfWorkStore
{
IUnitOfWork ActiveUnitOfWork { get; set; }
}
public class ThreadedUnitOfWorkStore : IUnitOfWorkStore
{
object _syncRoot = new object();
IUnitOfWork _unitOfWork;
Dictionary<int, List<IUnitOfWork>> _units = new Dictionary<int, List<IUnitOfWork>>();
public ThreadedUnitOfWorkStore()
{
if (_syncRoot == null)
_syncRoot = new object();
if (_units == null)
_units = new Dictionary<int, List<IUnitOfWork>>();
}
public virtual IUnitOfWork ActiveUnitOfWork
{
get
{
lock (_syncRoot)
if (_units.Count > 0 && _units.ContainsKey(Thread.CurrentThread.ManagedThreadId) && _units[Thread.CurrentThread.ManagedThreadId].Count > 0)
return _units[Thread.CurrentThread.ManagedThreadId].Last();
return null;
}
set
{
lock (_syncRoot)
{
if (value == null)
{
_unitOfWork = value;
return;
}
value.UnitOfWorkCommitting += OnUnitOfWorkCommitting;
value.UnitOfWorkCanceling += OnUnitOfWorkCanceling;
if (_units.ContainsKey(value.ThreadId))
{
if (_units[value.ThreadId].Any(u => u.DataContextHandle == value.DataContextHandle))
return;
_units[value.ThreadId].Add(value);
}
else
_units.Add(value.ThreadId, new List<IUnitOfWork>() { value });
}
}
}
private void OnUnitOfWorkCanceling(IUnitOfWork uow)
{
lock (_syncRoot)
{
if (uow != null && _units.ContainsKey(uow.ThreadId))
{
if (_units[uow.ThreadId].Count > 1 && _units[uow.ThreadId].Any(u => u.DataContextHandle == uow.DataContextHandle))
{
_units[uow.ThreadId].RemoveAll(dc => dc.DataContextHandle == uow.DataContextHandle);
}
else
_units.Remove(uow.ThreadId);
}
}
}
void OnUnitOfWorkCommitting(IUnitOfWork uow)
{
lock (_syncRoot)
{
if (uow != null && _units.ContainsKey(uow.ThreadId))
{
if (_units[uow.ThreadId].Count > 1 && _units[uow.ThreadId].Any(u => u.DataContextHandle == uow.DataContextHandle))
{
_units[uow.ThreadId].RemoveAll(dc => dc.DataContextHandle == uow.DataContextHandle);
}
else
_units.Remove(uow.ThreadId);
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
namespace System.Data.Entity.Repository
{
public delegate void UnitOfWorkCommitting(IUnitOfWork uow);
public delegate void UnitOfWorkCanceling(IUnitOfWork uow);
public enum LockType
{
Safe = 0,
Unsafe,
Locked
}
public class UnitOfWork : IUnitOfWork
{
object syncRoot = new object();
public UnitOfWork(string name, IServiceLocator locator, LockType lockType = LockType.Safe)
{
this.DataContextHandle = Guid.NewGuid();
this.context = locator.Resolve<DbContext>(name);
if (lockType == LockType.Locked)
isolationLevel = IsolationLevel.Serializable;
else if (lockType == LockType.Unsafe)
isolationLevel = IsolationLevel.ReadUncommitted;
else
isolationLevel = IsolationLevel.ReadCommitted;
}
DbContext context;
private IsolationLevel isolationLevel = IsolationLevel.ReadUncommitted;
private DbContextTransaction transaction;
public Guid DataContextHandle { get; private set; }
public int ThreadId { get; protected set; } = System.Threading.Thread.CurrentThread.ManagedThreadId;
public bool Committed { get; protected set; }
public bool ValidState
{
get
{
return context != null;
}
}
public event UnitOfWorkCommitting UnitOfWorkCommitting;
public event UnitOfWorkCanceling UnitOfWorkCanceling;
public IDisposable BeginTransaction()
{
if (transaction != null)
throw new InvalidOperationException("This Data Context already has a Transaction.");
transaction = context.Database.BeginTransaction(isolationLevel);
return transaction;
}
public void Add<EntityType>(EntityType entity) where EntityType : class
{
context.Set<EntityType>().Add(entity);
context.Entry(entity).State = EntityState.Added;
}
public void AddRange<EntityType>(IEnumerable<EntityType> entities) where EntityType : class
{
context.Set<EntityType>().AddRange(entities);
foreach(var e in entities)
context.Entry(e).State = EntityState.Added;
}
protected void InvokeUnitOfWorkCommitting()
{
lock (syncRoot)
{
UnitOfWorkCommitting?.Invoke(this);
}
}
protected void InvokeUnitOfWorkCanceling()
{
lock (syncRoot)
{
UnitOfWorkCanceling?.Invoke(this);
}
}
public void Commit()
{
context.SaveChanges();
if (transaction != null)
transaction.Commit();
InvokeUnitOfWorkCommitting();
Committed = true;
}
public void Remove<EntityType>(EntityType entity) where EntityType : class
{
context.Set<EntityType>().Remove(entity);
}
public void RemoveRange<EntityType>(IList<EntityType> entities) where EntityType : class
{
context.Set<EntityType>().RemoveRange(entities);
}
public IQueryable<EntityType> Get<EntityType>() where EntityType : class
{
return context.Set<EntityType>();
}
public void Integrate<EntityType>(EntityType entity) where EntityType : class
{
context.Entry(entity).State = EntityState.Modified;
}
public EntityType Create<EntityType>() where EntityType : class
{
var entity = context.Set<EntityType>().Create();
context.Entry(entity).State = EntityState.Added;
return entity;
}
public void Dispose()
{
if (transaction != null)
transaction.Dispose();
if (context != null)
context.Dispose();
if (!Committed)
InvokeUnitOfWorkCanceling();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment