repository pattern in ef6 with Interfaces - c#

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.

Related

Inject a repository which has Generic as well as Concrete Implementation to a controller

I am making a generic repository but for some entities I also need functionalities not provided by the generic repository. I have an interface IGenericRepository and concrete implementation as GenericRepository with basic CRUD operations. Further I have a studentRepository that uses the generic repository but also has functionalities of its own independent from the Generic Repository for which i have an Interface called IStudentRepository.
Here is the sample code:
public interface IGenericEntityRepository<T>
{
Delete(T entity);
T Get(int id);
IEnumerable<T> GetAll();
Add(T entity);
Update(T entity);
}
public class GenericEntityRepository<T> : IGenericEntityRepository<T> where T : class
{
protected readonly ApplicationDbContext _applicationDbContext;
public GenericEntityRepository(ApplicationDbContext applicationDbContext)
{
this._applicationDbContext = applicationDbContext;
}
//Generic Repository Implementations....
}
public interface IStudentRepository
{
string GetFullName(Student student)
double GetGpa(Student student)
}
public class StudentRepository: GenericRepository<Student>, IStudentRepository
{
public StudentRepository(ApplicationDbContext applicationDbContext) : base(applicationDbContext)
{}
//IStudentRepository functions' implementations...
}
Now I need to inject this StudentRepository to my StudentsController
public class StudentsController : Controller
{
private readonly IGenericEntityRepository<Student> _genericStudentRepository;
public StudentsController(IGenericEntityRepository<Student> _genericStudentRepository)
{
this._genericStudentRepository = genericRepository;
}
public void testAccessibility()
{
this._genericStudentRepository.GetAll() //valid call
this._genericStudentRepository.GetAllGpa() //invalid Call
***As expected cause IGenericEntityRepository doesn't have that ***function
}
}
As you can see the probelem here, if I inject IGenericEntityRepository I only get the genericrepository functionalities. If i want the functionalities of Student repository not included in genericRepository I have to inject both IGenericEntityRepository and IStudentRepository like below and vice versa.
public class StudentsController : Controller
{
private readonly IGenericEntityRepository<Student> _genericStudentRepository;
private readonly IStudentRepository _studentsRepository;
public StudentsController(IGenericEntityRepository<Student> _genericStudentRepository, IStudentRepository studentsRepository)
{
this._genericStudentRepository = genericRepository;
this.__studentsRepository = studentsRepository;
}
public void testAccessibility()
{
this._genericStudentRepository.GetAll() //valid call
this._studentsRepository.GetAllGpa() //valid call
}
}
Is there a better way to do this? Doesn't feel right injecting two contextually same but coding wise different objects like this.
You can have IStudentRepository extend IGenericEntityRepository<T>:
public interface IStudentRepository : IGenericEntityRepository<Student>
{
string GetFullName(Student student)
double GetGpa(Student student)
}
Now injecting IStudentRepository should be enough to use all the functions.

The type is defined in an assembly that is not referenced. c#, Generic Repository Pattern, URF

I'm using a generic repository. My service layer talks to my repository and maps entities to domain models with automapper. My controllers talk to my service layer and know nothing of entities or the repository.
I am trying to create a generic service class for all the basic CRUDs.
My generic service looks like this (cut down):
public interface IService<TModel, TEntity>
{
void Add(TModel model)
}
public abstract class Service<TModel, TEntity> : IService<TModel, TEntity>
{
private readonly IGenericRepository<TEntity> _repository;
protected Service(IGenericRepository<TEntity> repository) { _repository = repository; }
public virtual void Add(TModel model) { _repository.Add(AutoMapper.Mapper.Map<TEntity>(model)); }
}
My Student service:
public interface IStudentService : IService<Model.Student, Entity.Student>
{ }
public class StudentService : Service<Model.Student, Entity.Student>, IStudentService
{
private readonly IGenericRepository<Entity.Student> _repository;
public StudentService (IGenericRepository<Entity.Student> repository) : base(repository)
{
_repository = repository;
}
}
And my controller
public class StudentController
{
private readonly IStudentService _studentService;
public StudentController(IStudentService studentService)
{
_studentService = studentService;
}
public ActionResult AddStudent(Student model)
{
_studentService.Add(model); //ERROR
}
}
I get the following when calling add from my controller (line marked with ERROR above).
The type is defined in an assembly that is not referenced. You must add a reference to MyProject.Entities
I understand the reason for the error but didn't think it would be a problem as my service accepts and returns models only and doesn't need to know about entities?
Is there another way to accomplish what I want so I can keep from referencing entities in my controller class?
For completeness, I should probably make this as an answer.
Just change the Service interface not to take the Entity Type Parameter:
public interface IService<TModel> {
// ...
}
and keep the type parameter on the abstract class.
public abstract class Service<TModel, TEntity> : IService<TModel> {
// ...
}

Generic Repository for Lookup Values

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

c# Generic cannot convert source type to target type

I use the Repository pattern in my project. Every component has a Bll class.
I want to create a base bll class, the sub Bll class can work without the same Repository Curd method.
But It appears the "Cannot convert ..DriverRepository to target type ...Repository IEntity>"
language:c#
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class , IEntity
{
protected string ConnectionStringName;
protected IDatabase Db;
public Repository(string connStringName)
{
ConnectionStringName = connStringName;
Db = new Database(ConnectionStringName);
}
public Repository()
{
ConnectionStringName = "DefaultConnection";
Db = new Database(ConnectionStringName);
}
}
public abstract class BaseBll
{
protected Repository<IEntity> DefaultRepository;
protected BaseBll(Repository<IEntity> repository)
{
_defaultRepository = repository;
}
protected virtual List<IEntity> GetAll()
{
return DefaultRepository.GetAll();
}
}
public class DriverRepository : Repository<Driver>
{
public Driver GetDriverByLicenseNumber(string licenseNumber)
{
return Db.SingleOrDefault<Driver>("where LicenseNumber = #0", licenseNumber);
}
}
public class DriverBll
{
public DriverBll()
{
DefaultRepository = new DriverRepository();
//***Throw the Cannot convert ... to ... Error. Why?****
}
}
But...
You have no zero-parameter constructor in Repository, but DriverRepository inherits from it. Because you have no zero-parameter constructor in Repository, when DriverRepository tries to create itself, it has no constructor to call on Repository. You need to invoke the Repository(connectionString) constructor from DriverRepository's constructor like so.
public DriverRepository(string connectionString) : base(connectionString)
{
...
}
or
public DriverRepository() : base("YourConnectionString")
{
...
}
EDIT: Upon clarification.
Firstly in this code example, DriverBll does not extend from BaseBll, so it doesnt' know about the DefaultRepository property. Secondly, Driver must be implementing IEntity.

Using multiple repositories based on a base concrete class

I've found that in my UnitOfWork I have a repository for each type of entity and am not using aggregate roots, so I'm trying to fix that. Tackling the idea of computer inventory, I currently have my UnitOfWork structured as such:
public class UnitOfWork : IUnitOfWork
{
private readonly ReportingDbContext _dbContext = null;
public UnitOfWork()
{
_dbContext = new ReportingDbContext();
}
public void Commit()
{
_dbContext.SaveChanges();
}
// Inventory
public IRepository<ComputerEntity> Computers {get { return new Repository<ComputerEntity>(_dbContext); }}
public IRepository<NetworkAdapterEntity> NetworkAdapters { get { return new Repository<NetworkAdapterEntity>(_dbContext); } }
// plus a bunch more
}
I want only my aggregate root to appear there, which should be easy enough to do. I think the issue is that I'm using a single repository class and feeding in the type when I new it up. I believe the answer is to have multiple repositories, each one corresponding to an aggregate root. What is nice about this one generic repository that I'm using for each type is that it handles all my Entity Framework stuff like finding by ID, saving to the DbSet, etc. My generic repository is setup as such:
public class Repository<T> : IRepository<T> where T : class
{
protected DbContext DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public Repository(DbContext dbContext)
{
if (dbContext == null)
{
throw new ArgumentNullException("dbContext");
}
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
public IQueryable<T> GetAll()
{
return DbSet;
}
public IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
return DbSet.Where(predicate);
}
// the rest of the implementation omitted for brevity
}
This repository uses an interface that all my yet-to-be-created aggregate root repositories should use:
public interface IRepository<T> where T : class
{
IQueryable<T> GetAll();
IQueryable<T> Find(Expression<Func<T, bool>> predicate);
T GetById(int id);
void Remove(T entity);
void Add(T newEntity);
}
Now here is the real meat of the question. I have the above interface implemented nicely in my concrete Repository class, and I want that same functionality in all the aggregate root repositories that I will be making. I don't want to ever directly use this generic repository, as I just want to use it for a base to get at the basic CRUD stuff it does with Entity Framework. I don't want to repeat the already implemented generic repository stuff, just inherit it. More importantly, I want to design this correctly the first time.
Would it be appropriate to create my aggregate root based repository as such:
public interface IComputerRepository
{
string ComputerSpecificMethod(string param);
}
public class ComputerRepository : Repository<ComputerEntity>, IComputerRepository
{
public ComputerRepository(DbContext dbContext) : base(dbContext)
{
//
}
public string ComputerSpecificMethod(string param)
{
// do stuff
return "test";
}
}
Then use this new fancy repository (and others like it) in my UnitOfWork as such:
public IRepository<ComputerEntity> Computers {get { return new ComputerRepository(_dbContext); }}
Instead of:
public IRepository<ComputerEntity> Computers {get { return new Repository<ComputerEntity>(_dbContext); }}
The goal is to stick to the UnitOfWork/Repository pattern, and I'm unsure if this is the proper way of doing this.
I found that the way to do this that works for me is to have the interface for each custom repository in my unit of work class as such:
public IInventoryRepository Computers { get { return new InventoryRepository(_dbContext); } }
It is implemented in its own class of course. To get it to inherit properly, I did this:
public class InventoryRepository : GenericRepository<ComputerEntity>, IInventoryRepository
{
public InventoryRepository(DbContext dbContext) : base(dbContext)
{
}
// your custom methods go here
}
I then can use this in my WCF service as such:
using (var uoW = new UnitOfWork())
{
var repo = uoW.Computers;
var computerEntity = repo.FindComputerByHostname(hostname, client);
// do more stuff
}

Categories

Resources