Repository pattern with generics and DI

馋奶兔 提交于 2020-01-01 19:50:50

问题


I have a base repository contract which other contracts extend, like follows

public interface IBaseRepository<T> where T : class 
{
  IList<T> GetContents();
}

and then there are other contracts which extend it like follows

public interface IRepository1 : IBaseRepository<MyClass1>
{
}

public interface IRepository2 : IBaseRepository<MyClass2>
{
}

I implement IRepository1 as follows

public class Repository1 : IRepository1
{
 public IList<MyClass1> GetContents()
 {
  //some code goes here
 }
} 

similarly for IRepository2

public class Repository2 : IRepository2
{
 public IList<MyClass2> GetContents()
 {
  //some code goes here
 }
} 

Now i have a service Service1 which implments IService like follows

public class Service1 : IService
{


}

I want to use my base repository (IBaseRepository) here in my service constructor, get an instance of this base repository and use it like so

 public class Service1 : IService
 {
   private IBaseRepository<T> _baseRepository;
   public Service1(IBaseRepository<T> baseRepository)
   {
     _baseRepository = baseRepository;
   }

   public MyMethod1()
   {
      var contentsOfType1 = _baseRepository<MyClass1>.GetContents();
   }

   public MyMethod1()
   {
      var contentsOfType2 = _baseRepository<MyClass2>.GetContents();
   }
  }

and this is what i am unable to do.

So i have a generic base repository contract with type T and have other contracts (interfaces) extending the base contract and also specifying what type T will be.

All these contracts (which extend generic base contract) have thier individual implementations.

What i want to do is in my service class, instantiate this generic base contract, and use it to infer the extending types (and hence implementations) and use the method from the base repository.

So if the base contract is IBaseRepository<T>

and extending contract is IRepository1 : IBaseRepository<MyClass1>

which is implemented by Repository1 : IRepository1

i want to use this in my service class like

public class service()
{
   *private IBaseRepository<T> _repo;

   public  service(IBaseRepository<T> repo)
   { 
    *_repo = repo;
   } 

   public void MyMethod()
   {

     *var x = _repo<MyClass1>.MethodFromIBaseRepository()
   }

}

So its the *marked lines i want to achieve, which i am unable to.

I am using castle windsor for DI.

Thanks for your help guys


回答1:


You should not have other repository interfaces besides your generic IRepository<T>. If you need those, you are missing an abstraction.

For instance, a common reason for people to have custom repository interfaces is because they have a custom query that some repository has, while other don't. For instance:

public interface IEmployeeRepository : IRepository<Employee>
{
    Employee GetEmployeeOfTheMonth(int month);
}

The problem here is that the IEmployeeRepository is abused for a 'custom query'. Custom queries deserve their own (generic) abstraction:

// Defines a query
public interface IQuery<TResult>
{
}

// Defines the handler that will execute queries
public interface IQueryHandler<TQuery, TResult>
    where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

With this abstraction we can add custom queries to the system, without the need of creating IRepository<T> derivatives:

public class GetEmployeeOfTheMonthQuery : IQuery<Employee>
{
    [Range(1, 12)]
    public int Month { get; set; }
}

class GetEmployeeOfTheMonthHandler : IQueryHandler<GetEmployeeOfTheMonthQuery, Employee>
{
    public Employee Handle(GetEmployeeOfTheMonthQuery query)
    {
        // todo: query the database, web service, disk, what ever.
    }
}

A consumer that needs to know the employee of the month, can now simply take a dependency on IQueryHandler<GetEmployeeOfTheMonthQuery, Employee> and execute the query as follows:

var query = new GetEmployeeOfTheMonthQuery { Month = 11 };
var employee = this.employeeOfMonthHandler.Handle(query);

This might seem like overhead, but this model is very flexible, scalable, and has many interesting benefits. For instance, it is very easy to add cross-cutting concerns by wrapping handlers with decorators.

This also allows our reposities to be hidden behind one generic interface, which allows us to easily batch register them at once and add decorators to them as well.

For more in depth information, read this article: Meanwhile… on the query side of my architecture.




回答2:


Not possible. The inversion of control container provide dependencies through the constructor, hence it must know in the constructor what type you want to get. So you should do this:

public class service()
{
   private IBaseRepository<MyClass1> _repo;

   public  service(IBaseRepository<MyClass1> repo)
   { 
       _repo = repo;
   } 

   public void MyMethod()
   {

      var x = _repo.MethodFromIBaseRepository()
   }

}


来源:https://stackoverflow.com/questions/12900078/repository-pattern-with-generics-and-di

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!