I am using the DbContextScope described here
In his example of how to get a hold of a dbcontext outside of the class its instantiated, Mehdi writes:
public class UserRepository : IUserRepository {
private readonly IAmbientDbContextLocator _contextLocator;
public UserRepository(IAmbientDbContextLocator contextLocator)
{
if (contextLocator == null) throw new ArgumentNullException("contextLocator");
_contextLocator = contextLocator;
}
public User Get(Guid id)
{
return _contextLocator.Get<MyDbContext>.Set<User>().Find(id);
}
}
But, if im going for a generic repository, say
public abstract class RepositoryBase<T> : IRepository<T> where T : class, IDomainEntity
{
private readonly DbSet<T> set;
private IAmbientDbContextLocator contextLocator;
protected RepositoryBase(IAmbientDbContextLocator ctxLocator)
{
if (ctxLocator == null) throw new ArgumentNullException(nameof(ctxLocator));
contextLocator = ctxLocator;
}
public T Get(Guid id)
{
//return _contextLocator.Get<MyDbContext>.Set<T>().Find(userId);
}
}
then how is the dbset supposed to be resolved? how do i work with "MyDbContext" in the Get method?
i do have multiple contexts.
public abstract class RepositoryBase<T, TDbContext> : IRepository<T> where T : IDomainEntity where TDbContext : DbContext
{
private readonly DbSet<T> _dbset;
private readonly IAmbientDbContextLocator _contextLocator;
protected RepositoryBase(IAmbientDbContextLocator ctxLocator)
{
if (ctxLocator == null) throw new ArgumentNullException(nameof(ctxLocator));
_contextLocator = ctxLocator;
_dbset = _contextLocator.Get<TDbContext>.Set<T>();
}
protected DbSet<T> DbSet { get { return _dbset; } }
public T Get(Guid id)
{
return DbSet.Find(id);
}
}
If you don't want TDbContext, You can send DbContext on constructor beside of contextlocator. But he forces you to use DbContextScope, I didn't read all article but let's not break his logic.
Related
I want to add TWO DbContext in My ASP.NET MVC 5 App, One DbContext For ASPIdentity and The Other For My APP DB. I am Using Repository Pattern.
m y problem is, How to To specify the Entity of each DbContext in BaseRepository ?
Here Is What I did.
1- DatabaseFactory & IDatabaseFactory
public class DatabaseFactory<T> where T : DbContext,new()
{
private T dbContext;
public T Init()
{
return dbContext ?? (dbContext = new T());
}
}
public interface IDatabaseFactory<T> where T : DbContext
{
T Init();
}
2- IUnitOfWork & UnitOfWork
public class UnitOfWork<T> : IUnitOfWork<T> where T : DbContext
{
private readonly IDatabaseFactory<T> dbFactory;
private T dbContext;
public UnitOfWork(IDatabaseFactory<T> dbFactory)
{
this.dbFactory = dbFactory;
}
protected T DbContext
{
get { return dbContext ?? (dbContext = dbFactory.Init()); }
}
public void Commit()
{
DbContext.SaveChanges();
}
}
public interface IUnitOfWork<T> where T : DbContext, IDisposable
{
void Commit();
}
3- BaseRepository.cs
public abstract class BaseRepository<T> where T : class
{
#region Properties
private DbContext dataContext;
private readonly IDbSet<T> dbSet;
protected IDatabaseFactory DbFactory
{
get;
private set;
}
protected DbContext dbContext
{
get { return dataContext ?? (dataContext = DbFactory.Init()); }
}
#endregion
protected BaseRepository(IDatabaseFactory dbFactory)
{
this.DbFactory = dbFactory;
this.dbSet = this.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
}
I'm also trying to implement generic repository pattern but without UOW.
For creating two DbContext you should add one more type in Base Repository.
Also creation logic for DbFactory should be in UOW only not in BaseRepository.
Here's simplified code for you. And be more specific about what you tried.
2- IUnitOfWork & UnitOfWork
public class UnitOfWork<T1, T2> : IUnitOfWork<T1, T2> where T1 : DbContext where T2 : DbContext {
// FOr DbFactories
private readonly IDatabaseFactory<T1> _dbFactory1;
private readonly IDatabaseFactory<T2> _dbFactory2;
//For Seperate DbContexes
private T _dbContext1;
private T _dbContext2;
public UnitOfWork () {
_dbFactory1 = new DatabaseFactory<T1> ();
_dbFactory2 = new DatabaseFactory<T2> ();
}
//For Accessiong DbContext Objects in Base Repository
protected T DbContext1 {
get { return _dbContext1 ?? (_dbContext1 = _dbFactory1.Init ()); }
}
protected T DbContext2 {
get { return _dbContext2 ?? (_dbContext2 = _dbFactory2.Init ()); }
}
public void Commit () {
DbContext1.SaveChanges ();
DbContext2.SaveChanges ();
}
}
public interface IUnitOfWork<T1, T2> where T1 : DbContext where T2 : DbContext, IDisposable {
void Commit ();
}
}
3 - BaseRepository and Example
public abstract class BaseRepository<T1,T2,T> : IUnitOfWork<T1, T2> where T : class where T1 : DbContext where T2 : DbContext {
#region Properties
// private DbContext _dataContext1; //for first DbContext
// private DbContext _dataContext1; //for second DbContext
private readonly IDbSet<T> _dbSet1; //Going to Perform Operations using Dbsets
private readonly IDbSet<T> _dbSet2;
//For Exposing DbContext to respective Implementing Repositories This is Optional
protected DbContext DataContext1 {
get { return DbContext1; } //retuning DbCOntext Object Created in UOW class
}
protected DbContext DataContext2 {
get { return DbContext2; }
}
//For Exposing DbSets to respective Implementing Repositories This is Optional
protected IDbSet<T> DbSet1 => _dbSet1;
protected IDbSet<T> DbSet2 => _dbSet2;
protected BaseRepository () {
_dbSet1 = DataContext1.Set<T> ();
//OR
_dbSet2 = DataContext2.Set<T> ();
}
#endregion
#region Implementation
#endregion
}
//SPecific Repository Example using Person Class
public class PersonRepository:BaseRepository<AspIdentityDbContext,AppDbContext,Person> {
//can use DbContexes from BaseRepository to write Additional Methods/Queries On dbSets
}
Try this and give feedback.
I have a BaseRepository which my repositories inherit. The code is declared as the following:
public interface IBaseRepository<T> : where T : class
{
IQueryable<T> GetAll();
}
public abstract class BaseRepository<C, T> : IBaseRepository<T>
where T : class
where C : DbContext, new()
{
protected BaseRepository()
{
_context = new C();
_context.Database.Log = message => Trace.WriteLine(message);
}
private readonly C _context;
protected C Context
{
get { return _context; }
}
public virtual IQueryable<T> GetAll()
{
return _context.Set<T>();
}
}
public interface IARepository : IBaseRepository<A>
{
}
public ARepository : BaseRepository<Entities, A>, IARepository
{
}
public interface IBRepository : IBaseRepository<B>
{
}
public ARepository : BaseRepository<Entities, B>, IBRepository
{
}
I then have a serivce layer which will be using multiple repositories to fetch data for my controllers.
public class SomeService
{
private readonly IARepository _aRepository;
private readonly IBRepository _bRepository;
public EventService(IARepository aRepository, IBRepository bRepository)
{
_aRepository = aRepository;
_bRepository = bRepository;
}
public EventService() : this(new ARepository(), new BRepository())
{
}
public IEnumerable<SomeDTO> GetSomeDTOs()
{
return _aRepository.GetAll()
.Join(_bRepository.GetAll(), a => a.SomeId, b => b.SomeId, (c, d) => new SomeDTO
{
...
...
...
}).ToList();
}
}
But here's the problem. I get the following error:
A first chance exception of type 'System.NotSupportedException'
occurred in EntityFramework.SqlServer.dll
Additional information: The specified LINQ expression contains
references to queries that are associated with different contexts.
when I'm calling the GetSomeDTOs function. From what I can see it should use the same context as it is declared in the baserepository. What seems to be the problem here?
The issue is that each repository has it's own context, you cannot then join them both together. A simple fix would be to create a shared context and pass that in to your repository:
public abstract class BaseRepository<C, T> : IBaseRepository<T>
where T : class
where C : DbContext
{
protected BaseRepository(C context)
{
_context = context;
_context.Database.Log = message => Trace.WriteLine(message);
}
//snip
}
And create your repositories like this:
var context = new MyDbContext();
IARepository aRepository = new ARepository(context);
IBRepository bRepository = new BRepository(context);
What I'm trying to do is to instantiate a service using NInject and a route param.
I have something like this:
DAL
public interface IDALContract
{
object GetById(int id);
}
public class DALPeopleContract : IDALContract
{
public object GetById(int id)
{
//get person
return null;
}
}
public class DALAnimalsContract : IDALContract
{
public object GetById(int id)
{
//get animal
return null;
}
}
public static class DALContractFactory
{
public static IDALContract GetContract(int discriminator)
{
switch(discriminator)
{
case 1: return new DALPeopleContract();
case 2: return new DALAnimalsContract();
default: throw new NotSupportedException();
}
}
}
Business Layer
public interface IMyService
{
object GetById(int id);
}
public class MyService : IMyService
{
private IDALContract _contract;
public MyService(int discriminator)
{
_contract = DALContractFactory.GetContract(discriminator)
}
public object GetById(int id)
{
return _contract.GetById(id);
}
}
Controller
public class MyController
{
private IMyService _myService;
public MyController()
{
//how do I get the discriminator here? (the discriminator should be a route param)
IParameter param = new Parameter("MyParam", discriminator, true);
_myService = NInjectKernel.TryGet<IMyService>(param);
}
ActionResult Index(id)
{
_myService.GetById(id);
return View(model);
}
}
So the issue that I'm having is how to get the param, or is there a better approach to this. Basicaly what I'm trying to do is to have one controller that handles the same actions for different models, but I'm having an issue with the Data Access Layer (DAL).
Would it be a good idea to get the value on OnActionExecuting and instantiate there the service?
I found a clean (in my opinion) solution to my issue.
So now I have a fairly easy way to deal with simple operation on related classes.
I replaced the DALContractFactory with some NInject binding. And I have a custom route defined for my controller that requires a "type" that is read in OnActionExecuting on my controller.
For the models, I have a factory defined and a custom mapper (I haven't posted them here because they were not relevant to the question). If anybody is interested, I can post a sample sln with this approach.
So now I have something like:
DAL
public interface IDALContract
{
object GetById(int id);
}
public class DALPeopleContract : IDALContract
{
public object GetById(int id)
{
//get person
return null;
}
}
public class DALAnimalsContract : IDALContract
{
public object GetById(int id)
{
//get animal
return null;
}
}
Business Layer
public enum Discriminator
{
Animal,
Person
}
public interface IMyService
{
object GetById(int id);
}
public class MyService : IMyService
{
private IDALContract _contract;
public MyService(IDALContract contract)
{
_contract = contract;
}
public object GetById(int id)
{
return _contract.GetById(id);
}
}
Controller
public class MyController : Controller
{
private IMyService _myService;
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
base.OnActionExecuting(ctx);
int type;
var routeValue = ControllerContext.RouteData.Values["type"];
Discriminator type;
if(!Enum.TryParse<Discriminator>(routeValue.ToString(), out type))
{
//set a default value
type = Discriminator.Animal;
}
_myService = NInjectKernel.Instance.GetService<IMyService>("type", type);
}
public ActionResult Index(id)
{
_myService.GetById(id);
return View(model);
}
}
NInjectConfiguration
public class NInjectKernel
{
private readonly IKernel _kernel;
private NInjectKernel()
{
_kernel = new StandardKernel();
}
private static volatile Irr2NInjectKernel _instance;
private static readonly object SyncRoot = new object();
public static Irr2NInjectKernel Instance
{
get
{
if (_instance == null)
{
lock (SyncRoot)
{
if (_instance == null)
{
var temp = new Irr2NInjectKernel();
temp.BindAllDependencies();
_instance = temp;
}
}
}
return _instance;
}
}
private void BindAllDependencies()
{
_kernel.Bind<IMyService>().To<MyService>();
_kernel.Bind<IDALContract>().ToMethod(x =>
{
IParameter parameter = x.Parameters.SingleOrDefault(p => p.Name == "type");
if (parameter != null)
{
var recordType = (Discriminator)parameter.GetValue(x, x.Request.Target);
switch (recordType)
{
case RecordType.Animal:
return new DALAnimalsContract();
case RecordType.Person:
return new DALPeopleContract();
default:
throw new NotSupportedException("DQS type is not suppported.");
}
}
throw new NotSupportedException();
});
}
}
I don't know if the following is weird but actually need an interface for an interface< T > in order to store it in a List without specify a specific implementation.
Example:
public interface IRepository<T>
{
void Add(T newEntity);
void Remove(T entity);
IEnumerable<T> FindAll();
IEnumerable<T> Find(Expression<Func<T, bool>> predicate);
}
public interface IUnitOfWork
{
//Here i would like to store just a IRepository without implementation
IList<IRepository<**I have to specify implementation here**>> Repositories { get; set; }
bool Commit();
}
You are free to suggest me better ways to do that. This is only what i have though to do...
Thanks
EDIT
I cannot provide a non-generic interface because i'm using it like this:
public class GenericRepository<T> : IRepository<T>
{
...generic repository methods...
}
//Specific repository...
public class MyTableClassRepository<MyTable> : GenericRepository<MyTable>
{
public MyTableClassRepository(Database context) : base(context)
{
}
}
Consider the following design
public interface IUnitOfWork : IDisposable
{
T GetRepository<T>() where T : class;
void SaveChanges();
}
In the implementation of the UnitOfWork you can use IoC container (Autofac in the example below)
public class UnitOfWork : IUnitOfWork
{
private static IContainer _container;
Hashtable _repositories = new Hashtable();
public static Module CurrentRepositoriesModule { get; set; }
public UnitOfWork()
{
var builder = new ContainerBuilder();
if (CurrentRepositoriesModule != null)
builder.RegisterModule(CurrentRepositoriesModule);
_container = builder.Build();
}
public T GetRepository<T>() where T : class
{
var targetType = typeof(T);
if (!_repositories.ContainsKey(targetType))
{
_repositories.Add(targetType, _container.Resolve<T>());
}
return (T)_repositories[targetType];
}
public void SaveChanges()
{
throw new NotImplementedException();
}
}
I'm using CQRS pattern in my recent project, so I defined some Commands that I call them CommandParameter and CommandHandlers.
For CommandParameters I have these Classes and Interfaces:
public interface ICommandParameter
{
}
public abstract class BaseEntityCommandParameter<T> : IAggregateRoot,ICommandParameter
where T : ModelEntitySuperType, new()
{
public T Entity { get; set; }
protected BaseEntityCommandParameter()
{
Entity = new T();
}
}
public class InsertCommandParameter<T> : BaseEntityCommandParameter<T>
where T : class, new()
{
}
And for CommandHandlers I defined these Classes and Interfaces:
public interface ICommandHandler<TCommandParameter>
where TCommandParameter :ICommandParameter
{
void Handle(TCommandParameter parameter);
string CommandCode { get; }
}
public class InsertCommandHandler<TCommandParameter, TEntity>
: ICommandHandler<TCommandParameter>
where TCommandParameter : BaseEntityCommandParameter<TEntity>, new()
where TEntity : ModelEntitySuperType, IAggregateRoot, new()
and I used them to make appropriate CommandParameters and CommandHandlers for each Entity for example for Order I have:
public class OrderInsertCommandParameter:InsertCommandParameter<Order>
{
}
public class OrderInsertCommandHandler
: InsertCommandHandler<OrderInsertCommandParameter, Order>
{
private readonly IUnitOfWorkFactory _factory;
public OrderInsertCommandHandler(IUnitOfWorkFactory factory,
IRepository<Order> repository)
: base(repository)
{
_factory = factory;
}
public override void Handle(OrderInsertCommandParameter parameter)
{
var uow = _factory.Create();
parameter.Entity.OrderCreationTime = DateTime.Now;
base.Handle(parameter);
uow.Commit();
}
}
I want to register these CommandParameters and appropriate CommandHandlers using structuremap automatically, How could I define a custom Conversion to do this?
The following should do the trick:
container.Configure(r =>
{
r.Scan(s =>
{
s.Assembly(typeof(ICommandHandler<>).Assembly);
s.ConnectImplementationsToTypesClosing(typeof(ICommandHandler<>));
});
});