I am following this article on MSDN. I ported it to EF Code First.
public interface IUnitOfWork
{
IRepository<Employee> Employees { get; }
IRepository<TimeCard> TimeCards { get; }
void Commit();
}
public class HrContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<TimeCard> TimeCards { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasMany(e => e.TimeCards)
.WithOptional(tc => tc.Employee);
}
}
public class SqlRepository<T> : IRepository<T>
where T : class
{
private readonly DbSet<T> entitySet;
public SqlRepository(DbContext context)
{
this.entitySet = context.Set<T>();
}
public void Add(T newEntity)
{
this.entitySet.Add(newEntity);
}
public IQueryable<T> FindAll()
{
return this.entitySet;
}
public T FindById(params object[] keys)
{
return this.entitySet.Find(keys);
}
public IQueryable<T> FindWhere(Expression<Func<T, bool>> predicate)
{
return this.entitySet.Where(predicate);
}
public void Remove(T entity)
{
this.entitySet.Remove(entity);
}
}
public class SqlUnitOfWork : IUnitOfWork, IDisposable
{
private readonly HrContext context;
private IRepository<Employee> employees;
private IRepository<TimeCard> timeCards;
public SqlUnitOfWork()
{
this.context = new HrContext();
}
public IRepository<Employee> Employees
{
get
{
return new SqlRepository<Employee>(context);
}
}
public IRepository<TimeCard> TimeCards
{
get
{
return new SqlRepository<TimeCard>(context);
}
}
public void Commit()
{
this.context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
var query = from e in unitOfWork.Employees.FindAll()
from tc in unitOfWork.TimeCards.FindAll()
where tc.Employee.Id == e.Id && e.Name.StartsWith("C")
select tc;
var timeCards = query.ToList();
This model is great as it gives me testability. However, running queries like the one above throws this
LINQ to Entities does not recognize the method
'System.Linq.IQueryable`1[DomainModel.Models.TimeCard] FindAll()'
method, and this method cannot be translated into a store expression.
I understand the error but is there any way to avoid it but still keep the repositories for testability?
Your select statement cannot be translated due to the nature of how IQueryable<T> and the query providers work: see this thread for more info What is the difference between IQueryable<T> and IEnumerable<T>?
You can 'help' the linq provider by dividing your expression into separate statements like this:
var ems = unitOfWork.Employees.FindAll();
var tcs = unitOfWork.TimeCards.FindAll();
var query = from e in ems
from tc in tcs
where tc.Employee.Id == e.Id && e.Name.StartsWith("C")
select tc;
Or you can let FindAll() return IEnumerable<T> instead of IQueryable<T> and then your original expression should work.
Related
We are using entity framework with repository/unit of work pattern. Now, we would like to use dapper along with EF. Below are few basic classes we are using:
public interface IRepository<TEntity> where TEntity : class
{
void Add(TEntity entity);
void Update(TEntity entity);
void Delete(int id);
TEntity GetById(int id);
IEnumerable<TEntity> GetAll();
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext _context;
protected readonly DbSet<TEntity> _entities;
public Repository(DbContext context)
{
_context = context;
_entities = context.Set<TEntity>();
}
public virtual void Add(TEntity entity)
{
_entities.Add(entity);
}
public virtual void Update(TEntity entity)
{
_entities.Update(entity);
}
public virtual void Delete(int id)
{
var entity = GetById(id);
_entities.Remove(entity);
}
public virtual TEntity GetById(int id)
{
return _entities.Find(id);
}
public virtual IEnumerable<TEntity> GetAll()
{
return _entities.ToList();
}
}
public interface IUnitOfWork
{
IEmployeeRepository Employees { get; }
ICityRepository Cities { get; }
int SaveChanges();
}
public class UnitOfWork : IUnitOfWork
{
readonly AppDbContext _context;
IEmployeeRepository _employees;
ICityRepository _cities;
public UnitOfWork(AppDbContext context)
{
_context = context;
}
public IEmployeeRepository Employees
{
get
{
if (_employees == null)
_employees = new EmployeeRepository(_context);
return _employees;
}
}
public ICityRepository Cities
{
get
{
if (_cities == null)
_cities = new CityRepository(_context);
return _cities;
}
}
public int SaveChanges()
{
return _context.SaveChanges();
}
}
public interface IEmployeeRepository : IRepository<Employee>
{
bool CheckEmployeeExists(int employeeId, string employeeName);
}
public class EmployeeRepository : Repository<Employee>, IEmployeeRepository
{
public EmployeeRepository(DbContext context) : base(context)
{ }
public bool CheckEmployeeExists(int employeeId, string employeeName)
{
// Implement Dapper code here
}
private AppDbContext _appContext => (AppDbContext)_context;
}
Now, we would like to implement few methods of EmployeeRepository like CheckEmployeeExists to use Dapper. What changes do we need to make in above code considering best coding practices so that both EF and dapper work in proper way. How to integrate Dapper here?
Thank you.
This is for EF Core (EF6 works the same but the methods are slightly different).
Either open/close the underlying DbConnection in each method:
public bool CheckEmployeeExists(int employeeId, string employeeName)
{
var con = context.GetDbConnection();
con.Open();
//use Dapper with DbConnection here
con.Close();
}
If you fail to Close() the connection, it will be closed by DbContext.Dispose(), so it's not critical to Close() and you must not call Dispose() here.
Or Open the connection in the constructor, then it will be available to any method for the lifetime of the DbContext, eg
public EmployeeRepository(DbContext context) : base(context)
{
this.con = context.GetDbConnection();
con.Open();
}
How do I update this code below to allow for multiple DbContexts? I searched other posts and I tried to make my unit of work and dbfactory classes accept a generic type but I had trouble with the RepositoryBase class, the repository, the service and tying it all together; I still struggle with generics in C#. Can anyone help me out?
DbFactory.cs
public class DbFactory : Disposable, IDbFactory
{
WilMpeContext _dbContext;
public WilMpeContext Init()
{
return _dbContext ?? (_dbContext = new WilMpeContext());
}
protected override void DisposeCore()
{
_dbContext?.Dispose();
}
}
Disposable.cs
public class Disposable : IDisposable
{
private bool isDisposed;
~Disposable()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!isDisposed && disposing)
{
DisposeCore();
}
isDisposed = true;
}
// Ovveride this to dispose custom objects
protected virtual void DisposeCore()
{
}
}
iDbFactory.cs
public interface IDbFactory : IDisposable
{
WilMpeContext Init();
}
IRepository.cs
public interface IRepository<T> where T : class
{
// Marks an entity as new
void Add(T entity);
// Marks an entity as modified
void Update(T entity);
// Marks an entity to be removed
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
// Get an entity by int id
T GetById(int id);
// Get an entity using delegate
T Get(Expression<Func<T, bool>> where);
// Gets all entities of type T
IEnumerable<T> GetAll();
// Gets entities using delegate
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
}
IUnitOfWork.cs
public interface IUnitOfWork
{
void Commit();
}
RepositoryBase.cs
public abstract class RepositoryBase<T> where T : class
{
#region Properties
private WilMpeContext _dataContext;
private readonly IDbSet<T> _dbSet;
protected IDbFactory DbFactory
{
get;
private set;
}
protected WilMpeContext DbContext
{
get { return _dataContext ?? (_dataContext = DbFactory.Init()); }
}
#endregion
protected RepositoryBase(IDbFactory dbFactory)
{
DbFactory = dbFactory;
_dbSet = DbContext.Set<T>();
}
#region Implementation
public virtual void Add(T entity)
{
_dbSet.Add(entity);
}
public virtual void Update(T entity)
{
_dbSet.Attach(entity);
_dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = _dbSet.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
_dbSet.Remove(obj);
}
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return _dbSet.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).FirstOrDefault<T>();
}
#endregion
}
UnitOfWork.cs
public class UnitOfWork : IUnitOfWork
{
private readonly IDbFactory _dbFactory;
private WilMpeContext _dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
this._dbFactory = dbFactory;
}
public WilMpeContext DbContext
{
get { return _dbContext ?? (_dbContext = _dbFactory.Init()); }
}
public void Commit()
{
DbContext.Commit();
}
}
WilMpeContext.cs - this is one of my DbContexts but now I need another and I'm not sure how to implement that with my existing design
public class WilMpeContext : IdentityDbContext<ApplicationUser>
{
public WilMpeContext()
: base("name=DefaultConnection", throwIfV1Schema: false) { }
public IDbSet<AppSetting> AppSettings { get; set; }
//the rest of the tables were removed for brevity
public virtual void Commit()
{
base.SaveChanges();
}
public static WilMpeContext Create()
{
return new WilMpeContext();
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
Here is how I setup the repository to use the unit of work. AppSetting is a table in my model:
public interface IAppSettingRepository : IRepository<AppSetting>
{
void UpdateAppSetting(AppSetting appSetting);
}
public class AppSettingRepository : RepositoryBase<AppSetting>,IAppSettingRepository
{
public AppSettingRepository(IDbFactory dbFactory)
: base(dbFactory) { }
//an example of how I do something in the database. See I use DbContext from RepositoryBase
public void UpdateAppSetting(AppSetting appSetting)
{
DbContext.Entry(appSetting).State = EntityState.Modified;
}
}
And this is my service:
public class AppSettingService : IAppSettingService
{
private readonly IAppSettingRepository _appSettingRepository;
private readonly IUnitOfWork _unitOfWork;
public AppSettingService(IAppSettingRepository appSettingRepository,
IUnitOfWork unitOfWork)
{
_appSettingRepository = appSettingRepository;
_unitOfWork = unitOfWork;
}
//call repository to do database stuff and then commit changes
public void UpdateAppSetting(AppSetting appSetting)
{
_appSettingRepository.UpdateAppSetting(appSetting);
_unitOfWork.Commit();
}
}
You need to make all your interfaces and classes that is using your current WilMpeContext to generic. Also you would need to put a generic constraint on new generic.
First make your Interfaces generic for Unit of work and DBFactory:
public interface IUnitOfWork<TContext> where TContext: DbContext, new()
{
void Commit();
}
public interface IDbFactory<TContext> : IDisposable where TContext: DbContext, new()
{
TContext Init();
}
Now let's update your Classes for UnitOfWork and DBFactory:
public class UnitOfWork<TContext> : IUnitOfWork<TContext> where TContext: DbContext, new()
{
private readonly IDbFactory<TContext> _dbFactory;
private TContext _dbContext;
public UnitOfWork(IDbFactory<TContext> dbFactory)
{
this._dbFactory = dbFactory;
}
public TContext DbContext
{
get { return _dbContext ?? (_dbContext = _dbFactory.Init()); }
}
public void Commit()
{
this.DbContext.SaveChanges();
}
}
public class DbFactory<TContext> : Disposable, IDbFactory<TContext> where TContext: DbContext, new()
{
TContext _dbContext;
public TContext Init()
{
return _dbContext ?? (_dbContext = new TContext());
}
protected override void DisposeCore()
{
_dbContext?.Dispose();
}
}
Add an additional generic on Repository base class:
public abstract class RepositoryBase<T, TContext> where T : class where TContext: DbContext, new()
{
#region Properties
private TContext _dataContext;
private readonly IDbSet<T> _dbSet;
protected IDbFactory<TContext> DbFactory
{
get;
private set;
}
protected TContext DbContext
{
get { return _dataContext ?? (_dataContext = this.DbFactory.Init()); }
}
#endregion
protected RepositoryBase(IDbFactory<TContext> dbFactory)
{
DbFactory = dbFactory;
_dbSet = DbContext.Set<T>();
}
#region Implementation
public virtual void Add(T entity)
{
_dbSet.Add(entity);
}
public virtual void Update(T entity)
{
_dbSet.Attach(entity);
_dataContext.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = _dbSet.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
_dbSet.Remove(obj);
}
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return _dbSet.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).FirstOrDefault<T>();
}
#endregion
}
Here is your repository that will be typed with your Actual Context WilMpeContext class.
public class AppSettingRepository : RepositoryBase<AppSetting, WilMpeContext>, IAppSettingRepository
{
public AppSettingRepository(IDbFactory<WilMpeContext> dbFactory)
: base(dbFactory) { }
//an example of how I do something in the database. See I use DbContext from RepositoryBase
public void UpdateAppSetting(AppSetting appSetting)
{
DbContext.Entry(appSetting).State = System.Data.Entity.EntityState.Modified;
}
}
Finally your AppsettingService
public class AppSettingService : IAppSettingService
{
private readonly IAppSettingRepository _appSettingRepository;
private readonly IUnitOfWork<WilMpeContext> _unitOfWork;
public AppSettingService(IAppSettingRepository appSettingRepository,
IUnitOfWork<WilMpeContext> unitOfWork)
{
_appSettingRepository = appSettingRepository;
_unitOfWork = unitOfWork;
}
//call repository to do database stuff and then commit changes
public void UpdateAppSetting(AppSetting appSetting)
{
_appSettingRepository.UpdateAppSetting(appSetting);
_unitOfWork.Commit();
}
}
Now your UnitOfWork infrastructure will support any DbContext for all repositories.
Udpate
Alternative solution(May be)
May be I'm wrong but why would you need multiple dbcontext if your dbContext is not shared across the applications. I'm sure you are using Asp.net 5 MVC project that generates it's own IdenityDbContext and you might have your other DBContext as well. You can merge these two context and this way you can keep your current implementation of UOW.
Check out these SO QnA if this is the actual problem.
ASP.NET Identity DbContext confusion
Merge MyDbContext with IdentityDbContext
Using Autofac, I tried to use my generic type :
public interface IRepository<T> where T : class
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
T GetById(long Id);
T GetById(string Id);
T Get(Expression<Func<T, bool>> where);
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
}
to register my generic class :
public abstract class RepositoryBase<T> where T : class
{
private iMOSSContainer dataContext;
private readonly IDbSet<T> dbset;
protected RepositoryBase()
{
DatabaseFactory = new DatabaseFactory();
dbset = DataContext.Set<T>();
}
protected RepositoryBase(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected iMOSSContainer DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
public virtual void Add(T entity)
{
dbset.Add(entity);
}
public virtual void Update(T entity)
{
dbset.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
dbset.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
dbset.Remove(obj);
}
public virtual T GetById(long id)
{
return dbset.Find(id);
}
public virtual T GetById(string id)
{
return dbset.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return dbset.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return dbset.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return dbset.Where(where).FirstOrDefault<T>();
}
}
this way :
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(RepositoryBase<>)).As(typeof(IRepository<>)); //.InstancePerLifetimeScope();
Container = builder.Build();
And returns an error "sequence contains no element" when building a container.
Did i do something wrong with the code? I already tried to search for an answer to no avail. So, your help would be pretty much appreciated.
Ok, then i changed the RepositoryBase class like this :
public class RepositoryBase<T> : IRepository<T> where T : class
{
private iMOSSContainer dataContext;
private readonly IDbSet<T> dbset;
public RepositoryBase()
{
DatabaseFactory = new DatabaseFactory();
dbset = DataContext.Set<T>();
}
So :
i use the "iRepository" interface (and move the "where T : class"
statement at the end of the line)
i remove the "abstract" class (because it leads to another error)
set the default constructor to "public" (because it leads to another
error, as well)
And everything is working good right now.
Your RepositoryBase type is abstract, therefore it can't be registered. You need to register non-abstract types. I'm guessing what you're intending to do is register all subtypes of RepositoryBase.
Have been implementing Generic Repository, Unit of Work pattern with EF5 Code First from a number of resources and have come up with the following assemblies.
Interfaces, Contexts, Model, Repositories, UnitsOfWork
In the Context assembly I have my migrations folder which contains Configuration.cs
internal sealed class Configuration : DbMigrationsConfiguration<Context.SportsContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(Context.SportsContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
As you can see this DbMigrationsConfiguration takes in my SportsContext which is also defined in the contexts assembly (Contexts folder)
public class SportsContext : IDbContext
{
private readonly DbContext _context;
public SportsContext()
{
_context = new DbContext("SportsContext");
}
public void Dispose()
{
_context.Dispose();
}
public IDbSet<T> GetEntitySet<T>() where T : class
{
return _context.Set<T>();
}
public void ChangeState<T>(T entity, EntityState state) where T : class
{
_context.Entry(entity).State = state;
}
public void SaveChanges()
{
_context.SaveChanges();
}
}
This implements IDbContext which is defined in the Interfaces assembly
public interface IDbContext : IDisposable
{
IDbSet<T> GetEntitySet<T>() where T : class;
void ChangeState<T>(T entity, EntityState state) where T : class;
void SaveChanges();
}
In my UnitsOfWork assembly I have the following class
public class SportUnitOfWork : IUnitofWork
{
private readonly IDbContext _context;
public SportUnitOfWork()
{
_context = new SportsContext();
}
private GenericRepository<Team> _teamRepository;
private GenericRepository<Fixture> _fixtureRepository;
public GenericRepository<Team> TeamRepository
{
get { return _teamRepository ?? (_teamRepository = new GenericRepository<Team>(_context)); }
}
public GenericRepository<Fixture> FixtureRepository
{
get { return _fixtureRepository ?? (_fixtureRepository = new GenericRepository<Fixture>(_context)); }
}
public void Save()
{
_context.SaveChanges();
}
public IDbContext Context
{
get { return _context; }
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
For examples sake I have added the GenericRepository class in the Repositories assembly
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private IDbContext _context;
public GenericRepository(IDbContext context)
{
_context = context;
}
public GenericRepository(IUnitofWork uow)
{
_context = uow.Context;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (_context == null) return;
_context.Dispose();
_context = null;
}
public void Add(T entity)
{
_context.GetEntitySet<T>().Add(entity);
}
public void Update(T entity)
{
_context.ChangeState(entity, EntityState.Modified);
}
public void Remove(T entity)
{
_context.ChangeState(entity, EntityState.Deleted);
}
public T FindSingle(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes)
{
var set = FindIncluding(includes);
return (predicate == null) ? set.FirstOrDefault() : set.FirstOrDefault(predicate);
}
public IQueryable<T> Find(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes)
{
var set = FindIncluding(includes);
return (predicate == null) ? set : set.Where(predicate);
}
public IQueryable<T> FindIncluding(params Expression<Func<T, object>>[] includeProperties)
{
var set = _context.GetEntitySet<T>();
if (includeProperties != null)
{
foreach (var include in includeProperties)
{
set.Include(include);
}
}
return set.AsQueryable();
}
public int Count(Expression<Func<T, bool>> predicate = null)
{
var set = _context.GetEntitySet<T>();
return (predicate == null) ? set.Count() : set.Count(predicate);
}
public bool Exist(Expression<Func<T, bool>> predicate = null)
{
var set = _context.GetEntitySet<T>();
return (predicate == null) ? set.Any() : set.Any(predicate);
}
}
The problem I have is in the Configuration class which inherits from DbMigrationsConfiguration is expecting a DbContext parameter.
Error is Error 1 The type 'Contexts.Context.SportsContext' cannot be used as type parameter 'TContext' in the generic type or method 'System.Data.Entity.Migrations.DbMigrationsConfiguration'. There is no implicit reference conversion from 'Contexts.Context.SportsContext' to 'System.Data.Entity.DbContext'.
I can change the SportsContext to also inherit from DbContext but then I need to add a reference to EntityFramework 5 in the UnitsOfWork assembly as we want to possibly change or take out each layer without any reference to underlying models which is why i went with this pattern.
As we are looking at adding further contexts and models in the future so wanted to setup a architecture in that we could just add the context, model and then implement the relevant interfaces as and when needed.
A WebAPI Restful Web Service will be interacting with our data via the SportUnitOfWork, if I have understood the patterns correctly.
If anyone has any ideas on how I could do this or anything that I am doing wrong please let me know
thanks in advance Mark
Resolved this by doing the following
Changed my SportsContext class to a BaseContext which is abstract
public abstract class BaseContext : IDbContext
{
protected DbContext Context;
public void Dispose()
{
Context.Dispose();
}
public IDbSet<T> GetEntitySet<T>() where T : class
{
return Context.Set<T>();
}
public void Add<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Added;
}
public void Update<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Modified;
}
public void Delete<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Deleted;
}
public void SaveChanges()
{
// At the moment we are conforming to server wins when handling concurrency issues
// http://msdn.microsoft.com/en-us/data/jj592904
try
{
Context.SaveChanges();
}
catch (DbUpdateConcurrencyException e)
{
//Refresh using ServerWins
var objcontext = ((IObjectContextAdapter) Context).ObjectContext;
var entry = e.Entries;
objcontext.Refresh(RefreshMode.StoreWins, entry);
SaveChanges();
}
}
private DbEntityEntry GetDbEntityEntrySafely<T>(T entity) where T : class
{
DbEntityEntry dbEntityEntry = Context.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
// Set Entity Key
var objcontext = ((IObjectContextAdapter) Context).ObjectContext;
if (objcontext.TryGetObjectByKey(dbEntityEntry.Entity))
Context.Set<T>().Attach(entity);
}
return dbEntityEntry;
}
}
created in the Context folder a new class called FootballContext which inherits from BaseContext.
public class FootballContext : BaseContext
{
public FootballContext(string connectionstringName)
{
Context = new BaseFootballContext(connectionstringName);
}
}
Created a new folder called DbContexts
In here created the following classes,
public class BaseFootballContext : DbContext
{
public BaseFootballContext(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
public IDbSet<Fixture> Fixtures { get; set; }
public IDbSet<Team> Teams { get; set; }
}
public class MigrationsContextFactory : IDbContextFactory<BaseFootballContext>
{
public BaseFootballContext Create()
{
return new BaseFootballContext("FootballContext");
}
}
now my Configuration class can take in the BaseFootballContext as this is a DbContext.
My UnitOfWork class can now set the context to be FootballContext so does not have to reference EntityFramework.
This works with Migrations as well.
Only problem I have now is to figure out how to get this to work in a disconnected environment as I am having a problem reattaching entities and applying updates.
I'm playing with the latest Entity Framework CTP 5 release and building a simple asp.net MVC blog where I just have two tables: Post and Comments. This is done entirely in POCO, I just need help on the DbContext part, where I need it to be unit testable (using IDbSet?) and I need a simple/generic repository pattern for add, update, delete, retrieval. Any help is appreciated.
Thanks.
Start with you DbContext, create a new file called Database.cs:
Database.cs
public class Database : DbContext
{
private IDbSet<Post> _posts;
public IDbSet<Post> Posts {
get { return _posts ?? (_posts = DbSet<Post>()); }
}
public virtual IDbSet<T> DbSet<T>() where T : class {
return Set<T>();
}
public virtual void Commit() {
base.SaveChanges();
}
}
Define a IDatabaseFactory and implement it with DatabaseFactory:
IDatabaseFactory.cs
public interface IDatabaseFactory : IDisposable
{
Database Get();
}
DatabaseFactory.cs
public class DatabaseFactory : Disposable, IDatabaseFactory {
private Database _database;
public Database Get() {
return _database ?? (_database = new Database());
}
protected override void DisposeCore() {
if (_database != null)
_database.Dispose();
}
}
Disposable extension method:
Disposable.cs
public class Disposable : IDisposable
{
private bool isDisposed;
~Disposable()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if(!isDisposed && disposing)
{
DisposeCore();
}
isDisposed = true;
}
protected virtual void DisposeCore()
{
}
}
Now we can define our IRepository and our RepositoryBase
IRepository.cs
public interface IRepository<T> where T : class
{
void Add(T entity);
void Delete(T entity);
void Update(T entity);
T GetById(long Id);
IEnumerable<T> All();
IEnumerable<T> AllReadOnly();
}
RepositoryBase.cs
public abstract class RepositoryBase<T> where T : class
{
private Database _database;
private readonly IDbSet<T> _dbset;
protected RepositoryBase(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
_dbset = Database.Set<T>();
}
protected IDatabaseFactory DatabaseFactory
{
get; private set;
}
protected Database Database
{
get { return _database ?? (_database = DatabaseFactory.Get()); }
}
public virtual void Add(T entity)
{
_dbset.Add(entity);
}
public virtual void Delete(T entity)
{
_dbset.Remove(entity);
}
public virtual void Update(T entity)
{
_database.Entry(entity).State = EntityState.Modified;
}
public virtual T GetById(long id)
{
return _dbset.Find(id);
}
public virtual IEnumerable<T> All()
{
return _dbset.ToList();
}
public virtual IEnumerable<T> AllReadOnly()
{
return _dbset.AsNoTracking().ToList();
}
}
Now you can create your IPostRepository and PostRepository:
IPostRepository.cs
public interface IPostRepository : IRepository<Post>
{
//Add custom methods here if needed
Post ByTitle(string title);
}
PostRepository.cs
public class PostRepository : RepositoryBase<Post>, IPostRepository
{
public PostRepository(IDatabaseFactory databaseFactory) : base(databaseFactory)
{
}
public Post ByTitle(string title) {
return base.Database.Posts.Single(x => x.Title == title);
}
}
Lastly, the UoW:
IUnitOfWork.cs
public interface IUnitOfWork
{
void Commit();
}
UnitOfWork.cs
private readonly IDatabaseFactory _databaseFactory;
private Database _database;
public UnitOfWork(IDatabaseFactory databaseFactory)
{
_databaseFactory = databaseFactory;
}
protected Database Database
{
get { return _database ?? (_database = _databaseFactory.Get()); }
}
public void Commit()
{
Database.Commit();
}
Using in your controller:
private readonly IPostRepository _postRepository;
private readonly IUnitOfWork_unitOfWork;
public PostController(IPostRepository postRepository, IUnitOfWork unitOfWork)
{
_postRepository = postRepository;
_unitOfWork = unitOfWork;
}
public ActionResult Add(Post post) {
_postRepository.Add(post);
_unitOfWork.Commit();
}
You will need to use an IoC container like StructureMap to make this work. You can install structure map via NuGet, or if you are using MVC 3, you can install the StructureMap-MVC NuGet package. (Links Below)
Install-Package StructureMap.MVC4
Install-Package StructureMap.MVC3
Install-Package Structuremap
If you have questions just let me know. Hope it helps.
I just love this in-depth article about Entity Framework 4 POCO, Repository and Specification Pattern
http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/
The only thing I'd do differently is in the implementation, i.e. expose the IPostRepository in the service layer and have an interface field of type IPostService in the controller just as another layer of abstraction but otherwise this is a good example - nice one, Paul.