I have a bunch of Lookup Entities in the database (About 10 in total) that all implement the following interface
interface ILookupValue
{
int Id { get; set; }
string Name { get; set; }
string Description { get; set; }
}
At the moment i have a repository for each entity that implements an ILookupRepository interface
public interface ILookupRepository<T> where T : class
{
IEnumerable<T> GetLookupData();
}
Example Implementation
public class CustomerRepository : ILookupRepository<Customer>
{
public IDbContext _context;
public CustomerRepository(IDbContext context)
{
context = _context;
}
public IEnumerable<Customer> GetLookupData()
{
return _context.Set<Customer>();
}
}
I don't anticipate any of the repositories needing any other methods, so is there a way of making a generic repository for this scenario without having to have have additional code wiring up repository for each lookup type?
Edit: based on Dennis_E's answer, this is what i'm going with
public class LookupRepository<T> : ILookupRepository<T> where T : class, ILookupValue
{
public IDbContext _context;
public LookupRepository(IDbContext context)
{
context = _context;
}
public IEnumerable<T> GetLookupData()
{
return _context.Set<T>();
}
}
The class looks pretty generic to me.
public class LookupRepository<T> : ILookupRepository<T>
{
public IDbContext _context;
public LookupRepository(IDbContext context)
{
context = _context;
}
public IEnumerable<T> GetLookupData()
{
return _context.Set<T>();
}
}
Then instantiate with new LookupRepository<Customer>();
You will need a generic base class and then have your CustomerRepository inherit from that:
public class GenericRepository<T> : ILookupRepository<T>
{
protected readonly IDbContext _context;
protected GenericRepository(IDbContext context)
{
_context = context;
}
public IEnumerable<T> GetLookupData()
{
return _context.Set<T>();
}
}
Then you can create an instance of GenericRepository<Customer> directly, or if you prefer, have your IoC container inject that dependency for you.
It looks generic, though one method might come handy when you need to make join statements hitting DB in one go.
One that returns IQueryable
Related
Could somebody explain why it doesn't work.
I have two db context. And one common method with different return types and different queries.
public interface IDataFetcher<T> where T : IMarker
{
public List<T> GetData();
}
public interface IFetchServiceOne<T> : IDataFetcher<T> where T : IMarker
{
//maybe some methods here
}
public interface IFetchServiceTwo<T> : IDataFetcher<T> where T : IMarker
{
//maybe some different methods here
}
Implementation:
public class FetchServiceOne<T> : IFetchServiceOne<T> where T : IMarker
{
private readonly DBContext _dbContext;
public FetchServiceOne(DBContext dbContext) => _dbContext = dbContext;
public List<CrucialData> GetData()
{
var example = _dbContext.Test.ToList();
return example;
}
}
public class FetchServiceTwo<T> : IFetchServiceOne<T> where T : IMarker
{
private readonly DBContext _dbContext;
public FetchServiceTwo(DBContext dbContext) => _dbContext = dbContext;
public List<CrucialDataTwo> GetData()
{
var example = _dbContext.Test2.ToList();
return example;
}
}
public class CrucialData: IMarker
{
//some properries
}
public class CrucialDataTwo: IMarker
{
//another properries
}
In the output I'm getting compile error:
Error (active) CS0738 'FetchService' does not implement interface member 'IDataFetcher.GetData()'. 'FetchService.GetData()' cannot implement 'IDataFetcher.GetData()' because it does not have the matching return type of 'List'.
It looks like IFetchServiceOne and IFetchServiceTwo (and their implementations) should be "concrete" interfaces which implement closed versione of IDataFetcher. I.e.:
public interface IFetchServiceOne : IDataFetcher<CrucialData>
{
//maybe some methods here
}
public class FetchServiceOne : IFetchServiceOne
{
// ...
public List<CrucialData> GetData()
{
// implementation ...
return default;
}
}
Otherwise you can't guarantee that T requested by user will be CrucialData for FetchServiceOne (i.e. FetchServiceOne<CrucialDataTwo>.GetData() ...)
Or just:
public class FetchServiceOne : IDataFetcher<CrucialData>
{
// ...
}
Without need for introduction of the intermediate interface.
Note that for EF you can create a generic implementation via Set method if needed:
public class FetchServiceOne<T> : IDataFetcher<T> where T : IMarker
{
private readonly DBContext _dbContext;
public FetchServiceOne(DBContext dbContext) => _dbContext = dbContext;
public List<T> GetData()
{
var example = _dbContext.Set<T>.ToList();
return example;
}
}
But this will fail at runtime if there is no matching DbSet property setup for provided T type.
I am out to sea with this.
I received this answer which was corrected here by Nkosi.. however whilst it indicated how to rewrite the factory I am not up to the task of implementing it.. I need some gaps filled in.
As I understand it, this is what I need to do..
define the interface:
public interface IContextFactory<TContext> where TContext : DbContext
{
TContext Create(string connectionString);
}
Create the factory:
public class ContextFactory<TContext> : IContextFactory<TContext>
where TContext : DbContext
{
public TContext Create(DbContextOptions<TContext> options)
{
return (TContext)Activator.CreateInstance(typeof(TContext), options);
}
}
register the factory as a singleton in startup.
This is what I did and I dont know if this is correct?
services.AddSingleton(typeof(IContextFactory<>), typeof(ContextFactory<>));
Next how the heck do I use this factory?
In the first answer, it was suggested I use this:
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntityBase, new()
{
private JobsLedgerAPIContext _context;
public string ConnectionString { get; set; }
public EntityBaseRepository(IContextFactory<JobsLedgerAPIContext> factory)
{
_context = factory.CreateDbContext(ConnectionString);
}
public virtual IQueryable<T> GetAll()
{
return _context.Set<T>().AsQueryable();
}
public virtual int Count()
{
return _context.Set<T>().Count();
}
}
Nkosi identified that you cannot have the constraint "new()" and a parameterized constructor. He rewrote it. How do I change the above code to reflect the fact the factory now does not have a parameterized constructor but still take in the connection string given Nkosi's factory?
ClientRepository inherits the above.
public class ClientRepository : EntityBaseRepository<Client>, IClientRepository
{
private new JobsLedgerAPIContext _context;
public ClientRepository(JobsLedgerAPIContext context) : base(context)
{
_context = context;
}
Does this constructor need to be changed.
And finally how do I Inject the ClientRepository now that I have to supply a connection string?
aLets have a simple scenario:
public interface IMember
{
string Name { get; set; }
}
public class MemberEF6Impl : IMember
{
//some annotations...
public string Name { get; set; }
}
public class MemberVMImpl : IMember
{
//some other annotations...
public string Name { get; set; }
//some functionality...
}
I have two concrete implementation of all interfaces in our programm. One implementation especially for database migration, one for our viewmodel. Now I want to realize the factory-pattern and add one more interface and two more concrete implementations of it:
public interface IRepository
{
ICollection<TModel> GetAll<TModel>() where TModel : class;
//some more functionality...
}
public class RepositoryEF6Impl : IRepository
{
protected readonly DbContext context;
public RepositoryEF6Impl(DbContext context)
{
this.context = context;
}
public ICollection<TModel> GetAll<TModel>() where TModel : class
{
return context.Set<TModel>().ToList();
}
//some more functionality...
}
Now I can use the repository straight forward as follows:
IRepository repo = new RepositoryEF6Impl(context);
repo.GetAll<MemberEF6Impl>();
So far so good. But I want to use it this way:
IRepository repo = new RepositoryEF6Impl(context);
repo.GetAll<IMember>(); //note the difference
The problem is that in the database there is no IMember, but a MemberEF6Impl.
Why I want to use it this way:
Because we have different concrete classes for databse stuff and for viewmodel, I have to create a 2nd repository as well for viewmodel, which is only a layer between the concrete VMImpl class and the EF6 repository.
public class RepositoryVMImpl : IRepository
{
protected readonly IRepository repository;
public RepositoryVMImpl(IRepository repository)
{
this.repository = repository;
}
public ICollection<TModel> GetAll<TModel>() where TModel : class
{
return repository.GetAll<TModel>();
}
}
Is there a way to achive this?
My suggestion is to use single repository, but with some method overloading for projecting the requested generic type.
Method overload:
public ICollection<TProjection> GetAll<TModel, TProjection>(Expression<Func<TModel, TProjection>> projection)
{
return context.Set<TModel>().Select(projection).ToList();
}
then you can use the method like this, which will give control over the return type.
repository.GetAll<MemberEF6Impl, IMember>(memberEF => new MemberVMImp { ... })
If you still need the EF entity model as a result type you can use your current method:
repository.GetAll<MemberEF6Impl>();
More information about EF projections: https://www.tektutorialshub.com/projection-queries-entity-framework/
Also Automapper provides such functionality - it can save you some time. You should check it out.
If I have the following Context :
public partial class HRMainDataCTX : DbContext
{
public HRMainDataCTX()
: base("name=HRMainDataCTX")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
//DbSets
public virtual int SEARCHEMPLOYEE(Nullable<decimal> p_EMP_NUM, string p_EMP_NAME)
{
var p_EMP_NUMParameter = p_EMP_NUM.HasValue ?
new ObjectParameter("P_EMP_NUM", p_EMP_NUM) :
new ObjectParameter("P_EMP_NUM", typeof(decimal));
var p_EMP_NAMEParameter = p_EMP_NAME != null ?
new ObjectParameter("P_EMP_NAME", p_EMP_NAME) :
new ObjectParameter("P_EMP_NAME", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("SEARCHEMPLOYEE", p_EMP_NUMParameter, p_EMP_NAMEParameter);
}
}
Now i implement Unit of work like this :
public class HRCTX : IDisposable
{
private readonly HRMainDataCTX _context;
public HRCTX()
{
_context = new HRMainDataCTX();
}
public HRCTX(HRMainDataCTX context)
{
_context = context;
}
public int Save()
{
return _context.SaveChanges();
}
public HRMainDataCTX Context
{
get { return _context; }
}
public void Dispose()
{
_context.Dispose();
}
}
I don't know how to access the method (stored procedure) SEARCHEMPLOYEE through UOW in my code behind.
Well, in your case you would simply add another "Proxy-Method" for this method to your HRCTX proxy / UOW class, or - since HRCTX provides access to its underlying context - call it directly on the context like this:
HRCTX uow = new HRCTX(someContext);
uow.Context.SEARCHEMPLOYEE(123, "123");
But I also wanted to emphasize that the DbContext already represents a Unit of Work pattern (combined with a Repository pattern, see here). You are basically creating a proxy for your context, which - as far as I can see in this example - adds no further benefits or functionality, so I'd suggest to at least think about directly using your HRMainDataCTX and possibly getting rid of the HRCTX class.
You may need to implement repositories along with your Unit Of work pattern if you want to encapsulate your DbContext and your business logic. (As suggested in the AspNet guidelines)
In a generic manner, your unit of work can handle repositories like this:
public class HRCTX : IDisposable
{
private readonly HRMainDataCTX _context;
private Dictionary<Type, object> Repositories { get; set; }
public HRCTX()
{
_context = new HRMainDataCTX();
this.Repositories = new Dictionary<Type, object>();
}
//Get and add a repository to the dictionary if ot does not exist
public IRepository<TEntity> GetNonGenericRepository<TEntity, TRepository>() where TEntity : class
{
if (this.Repositories.Keys.Contains(typeof(TRepository)))
{
return this.Repositories[typeof(TRepository)] as IRepository<TEntity>;
}
var repoType = typeof(TRepository);
var constructorInfo = repoType.GetConstructor(new Type[] { typeof(DbContext)});
IRepository<TEntity> repository = (IRepository<TEntity>) constructorInfo.Invoke(new object[] { this._context});
this.Repositories.Add(typeof(TRepository), repository);
return repository;
}
public IRepository<TEntity> GetGenericRepository<TEntity>() where TEntity : class
{
if (this.Repositories.Keys.Contains(typeof(TEntity)))
{
return this.Repositories[typeof(TEntity)] as IRepository<TEntity>;
}
IRepository<TEntity> repository = new Repository<TEntity>(this._context);
this.Repositories.Add(typeof(TEntity), repository);
return repository;
}
}
The interface and base class of your repositories:
public interface IRepository<TEntity> where TEntity : class
{
TEntity Find(Expression<Func<TEntity, bool>> match);
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DbContext Context { get; set; }
public Repository(DbContext context)
{
this.Context = context;
}
public TEntity Find(Expression<Func<TEntity, bool>> match)
{
return Context.Set<TEntity>().SingleOrDefault(match);
}
}
Now is the part where you clearly encapsulate your business logic:
public class EmployeeRepository : Repository<Employee>
{
public EmployeeRepository(DbContext context) : base(context) {
}
public override Employee Find(Expression<Func<TEntity, bool>> match)
{
// You can either use the base class method or implement your custom logic
}
//This is where you encapsulate your business logic
public Employee FindSpecific(Nullable<decimal> employeeNum, string employeeName){
return this.Context.SEARCHEMPLOYEE(employeeNum, employeeName);
}
}
Then you can use your Unit Of Work to access you business logic in a domain driven design way.
HRCTX unitOfWork= new HRCTX(dbContext);
unitOfWork.GetNonGenericRepository<Employee, EmployeeRepository>().FindSpecific(1337,"1337");
It can seem to be too much for what you expected as an answer but I think you need to structure your application that way if you don't want to expose you DbContext / Dal directly.
Hope it helps !
This webpage documents exactly how to accomplish your goal.
http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
I'm developing an ASP.NET MVC 4 Web Api, with C#, .NET Framework 4.0, Entity Framework Code First 6.0 and Ninject.
I have two different DbContext custom implementations to connect with two different databases.
This is my NinjectConfigurator class (partial):
private void AddBindings(IKernel container)
{
container.Bind<IUnitOfWork>().
To<TRZICDbContext>().InRequestScope().Named("TRZIC");
container.Bind<IUnitOfWork>().
To<INICDbContext>().InRequestScope().Named("INIC");
container.Bind<IGenericRepository<CONFIGURATIONS>>().
To<GenericRepository<CONFIGURATIONS>>();
container.Bind<IGenericRepository<INCREMENTAL_TABLE>>().
To<GenericRepository<INCREMENTAL_TABLE>>();
// More implementation...
}
CONFIGURATIONS is a TRZIC table and INCREMENTAL_TABLE is an INIC table.
I'm using a IGenericRepository and here it's where I have the problems:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
protected DbSet<TEntity> DbSet;
private readonly DbContext dbContext;
public GenericRepository(IUnitOfWork unitOfWork)
{
dbContext = (DbContext)unitOfWork;
DbSet = dbContext.Set<TEntity>();
}
// Hidden implementation..
}
I don't know how to use the [Named("TRZIC")] here public GenericRepository(IUnitOfWork unitOfWork) or maybe I need to use it elsewhere.
Here the IUnitOfWork implementation depends on TEntity.
Any advice?
Let's start with the basics.
As far as i know named bindings work only with constant values attributed in code, like the [Named("foo")] attribute, or otherwise by using "service location" like IResolutionRoot.Get<T>(string name). Either does not work for your scenario, so a named binding is out of the question.
That leaves you with conditional bindings (.When(...) methods).
You've got 2 database with n entities each.
2 Database means two configurations means 2 different IUnitOfWork configuration.
However, the "user" is not requesting a specific database, but a specific entity.
Thus you'll need a map entity-->database (a dictionary). I don't think there's a way to get around that, but you may devise some kind of convention & implement it by convention, so you don't have to type and maintain a lot of code.
Solution 1: .WhenInjectedInto<>
with out of the box ninject features, and lots of manual labor:
Bind<IUnitOfWork>().To<UnitOfWorkOfDatabaseA>()
.WhenInjectedInto<IRepository<SomeEntityOfDatabaseA>>();
Bind<IUnitOfWork>().To<UnitOfWorkOfDatabaseA>()
.WhenInjectedInto<IRepository<SomeOtherEntityOfDatabaseA>>();
Bind<IUnitOfWork>().To<UnitOfWorkOfDatabaseB>()
.WhenInjectedInto<IRepository<SomeEntityOfDatabaseB>>();
you get the drift,.. right?
Solution 2.1: Custom When(..) implementation
Not so much manual labor and maintenance anymore.
Let me just dump the code on you, see below:
public interface IRepository
{
IUnitOfWork UnitOfWork { get; }
}
public class Repository<TEntity> : IRepository<TEntity>
{
public IUnitOfWork UnitOfWork { get; set; }
public Repository(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
}
public interface IUnitOfWork { }
class UnitOfWorkA : IUnitOfWork { }
class UnitOfWorkB : IUnitOfWork { }
public class Test
{
[Fact]
public void asdf()
{
var kernel = new StandardKernel();
kernel.Bind(typeof (IRepository<>)).To(typeof (Repository<>));
kernel.Bind<IUnitOfWork>().To<UnitOfWorkA>()
.When(request => IsRepositoryFor(request, new[] { typeof(string), typeof(bool) })); // these are strange entity types, i know ;-)
kernel.Bind<IUnitOfWork>().To<UnitOfWorkB>()
.When(request => IsRepositoryFor(request, new[] { typeof(int), typeof(double) }));
// assert
kernel.Get<IRepository<string>>()
.UnitOfWork.Should().BeOfType<UnitOfWorkA>();
kernel.Get<IRepository<double>>()
.UnitOfWork.Should().BeOfType<UnitOfWorkB>();
}
private bool IsRepositoryFor(IRequest request, IEnumerable<Type> entities)
{
if (request.ParentRequest != null)
{
Type injectInto = request.ParentRequest.Service;
if (injectInto.IsGenericType && injectInto.GetGenericTypeDefinition() == typeof (IRepository<>))
{
Type entityType = injectInto.GetGenericArguments().Single();
return entities.Contains(entityType);
}
}
return false;
}
}
Solution 2.2 Custom convention based When(...)
Let's introduce a small convention. Entity names of database TRZIC start with TRZIC, for example TRZIC_Foo. Entity names of database INIC start with INIC, like INIC_Bar. We can now adapt the previous solution to:
public class Test
{
[Fact]
public void asdf()
{
var kernel = new StandardKernel();
kernel.Bind(typeof (IRepository<>)).To(typeof (Repository<>));
kernel.Bind<IUnitOfWork>().To<UnitOfWorkA>()
.When(request => IsRepositoryFor(request, "TRZIC")); // these are strange entity types, i know ;-)
kernel.Bind<IUnitOfWork>().To<UnitOfWorkB>()
.When(request => IsRepositoryFor(request, "INIC"));
// assert
kernel.Get<IRepository<TRZIC_Foo>>()
.UnitOfWork.Should().BeOfType<UnitOfWorkA>();
kernel.Get<IRepository<INIC_Bar>>()
.UnitOfWork.Should().BeOfType<UnitOfWorkB>();
}
private bool IsRepositoryFor(IRequest request, string entityNameStartsWith)
{
if (request.ParentRequest != null)
{
Type injectInto = request.ParentRequest.Service;
if (injectInto.IsGenericType && injectInto.GetGenericTypeDefinition() == typeof (IRepository<>))
{
Type entityType = injectInto.GetGenericArguments().Single();
return entityType.Name.StartsWith(entityNameStartsWith, StringComparison.OrdinalIgnoreCase);
}
}
return false;
}
}
This way we don't need explicit mapping (EntityA, EntityB, EntityC) => DatabaseA, (EntityD, EntityE, EntityF) => DatabaseB).
If you say that IUnitOfWork depends on TEntity why not make IUnitOfWork generic too?
public class TRZIC {}
public class INIC {}
public interface IUnitOfWork<TEntity> {}
public class TRZICDbContext : DbContext, IUnitOfWork<TRZIC> {}
public class INICDbContext : DbContext, IUnitOfWork<INIC> {}
public interface IGenericRepository<TEntity> {}
public class GenericRepository<TEntity> : IGenericRepository<TEntity>
where TEntity : class
{
public GenericRepository(IUnitOfWork<TEntity> unitOfWork)
{
var dbContext = (DbContext) unitOfWork;
}
}
private static void AddBindings(IKernel container)
{
container
.Bind<IUnitOfWork<TRZIC>>()
.To<TRZICDbContext>();
container
.Bind<IUnitOfWork<INIC>>()
.To<INICDbContext>();
container
.Bind<IGenericRepository<TRZIC>>()
.To<GenericRepository<TRZIC>>();
container
.Bind<IGenericRepository<INIC>>()
.To<GenericRepository<INIC>>();
}
Another solution that also leverages code readability:
public interface IUnitOfWork {}
// both named A and B
public class UnitOfWorkA : IUnitOfWork {}
public class UnitOfWorkB : IUnitOfWork {}
public abstract class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
protected DbSet<TEntity> DbSet;
private readonly DbContext dbContext;
public GenericRepository(IUnitOfWork unitOfWork)
{
dbContext = (DbContext)unitOfWork;
DbSet = dbContext.Set<TEntity>();
}
// other IGenericRepository methods
}
public class GenericRepositoryForA<TEntity> : GenericRepository<TEntity>
{
public GenericRepositoryForA([Named("A")]IUnitOfWork unitOfWork)
: base(unitOfWork)
{
}
}
public class GenericRepositoryForB<TEntity> : GenericRepository<TEntity>
{
public GenericRepositoryForB([Named("B")]IUnitOfWork unitOfWork)
: base(unitOfWork)
{
}
}
This allows you to ask for a specific database context as a dependency, or get both of them if required. And you only need to implement GenericRepository once.
It greatly improvises code visiblity because you actually know which database context you are using by looking at the variable type/name, instead of getting a IUnitOfWork injected without any visual detail on its actual type.
I'd suggest adding some extra interfaces if you want to unittest it (you should!).
Simply adding
public interface IGenericRepositoryForA<TEntity> : IGenericRepository<TEntity>
and just let GenericRepositoryForA<TEntity> implement it aswell.
Another solution:
private void AddBindings(IKernel container)
{
container.Bind<IUnitOfWork>().To<TRZICDbContext>().InRequestScope();
container.Bind<IGenericRepository<CONFIGURATIONS>>().
To<GenericRepository<CONFIGURATIONS>>();
container.Bind<IGenericRepository<INCREMENTAL_TABLE>>().
To<GenericRepository<INCREMENTAL_TABLE>>().WithConstructorArgument("unitOfWork", new INICDbContext());
// More code..
}
I have used WithConstructorArgument to indicate that I want to use INICDbContext.
I don't know if this is correct or not.