通用基础控制器中的通用服务
Generic service in Generic Base Controller
我为了避免 DRY 我冒昧地想到为我所有的控制器生成一个通用基础 class。一切都很好,直到插入服务 class。我的基地控制器是:
basecontroller.cs
public class BaseController<TEntity, Tdto, TKey> : Controller
{
protected TavoraContext _context;
protected IMapper _mapper;
private IGeneric<TEntity, TKey, Tdto> _srv;
public BaseController(IGeneric<TEntity, TKey, Tdto> srv)
{
_srv = srv;
}
然后,在其中一个控制器中:
companiescontroller.cs
public class CompaniesController : BaseController<Company, CompanySimpleDTO, long>
{
public CompaniesController(TavoraContext context, IMapper mapper, CompaniesService companiesService) : base(companiesService)
{
}
CompaniesService 继承自实现 IGeneric 的 GenericService,因此,在我看来应该没有错误,我得到“无法从 CompaniesService 转换为 IGeneric”
companiesservice.cs
public class CompaniesService : GenericService<Company, long, CompanyDTO>
{
public CompaniesService(TavoraContext context, IMapper mapper) : base(context, mapper)
{
_runner = new RunnerWriteDb<CompanyDTO, Company>(
new WriteCompanyAction(
new WriteCompanyDBAccess(context), mapper), context);
}
genericservice.cs
public class GenericService<TEntity, TKey, Tdto> : IGeneric<TEntity, TKey, Tdto> where TEntity : BaseEntity<TKey>
{
protected RunnerWriteDb<Tdto, TEntity> _runner;
protected readonly int PAGESIZE = 20;
protected readonly TavoraContext _context;
protected DbSet<TEntity> _currentEntity;
protected IMapper _mapper;
public GenericService(TavoraContext context, IMapper mapper)
{
_context = context;
_currentEntity = _context.Set<TEntity>();
_mapper = mapper;
}
IGeneric.cs
public interface IGeneric<TEntity, TKey, Tdto>
{
IQueryable<TEntity> GetAll();
IQueryable<DTO> GetAll<DTO>();
//void Add(TEntity newItem);
//void AddRange(List<TEntity> newItems);
bool Update(TEntity updateItem);
void UpdateRange(List<TEntity> updateItems);
bool Delete(TKey id);
bool DeleteRange(List<TEntity> removeItems);
TEntity GetById(TKey id);
RunnerWriteDbResult<TKey> Write(Tdto dto);
}
使用依赖注入将服务注入控制器。使用像 autofac 这样的 pkgs 来解决依赖关系
问题在于你的IGeneric<TEntity, TKey, Tdto>
是不变的。这意味着它在 Tdto
泛型中既不是协变也不是逆变。
来自文档:
Covariance and contravariance are terms that refer to the ability to
use a more derived type (more specific) or a less derived type (less
specific) than originally specified. Generic type parameters support
covariance and contravariance to provide greater flexibility in
assigning and using generic types. When you are referring to a type
system, covariance, contravariance, and invariance have the following
definitions. The examples assume a base class named Base and a derived
class named Derived.
Covariance
Enables you to use a more derived type than originally specified.
You can assign an instance of IEnumerable (IEnumerable(Of
Derived) in Visual Basic) to a variable of type IEnumerable.
Contravariance
Enables you to use a more generic (less derived) type than originally
specified.
You can assign an instance of Action (Action(Of Base) in Visual
Basic) to a variable of type Action.
Invariance
Means that you can use only the type originally specified; so an
invariant generic type parameter is neither covariant nor
contravariant.
You cannot assign an instance of List (List(Of Base) in Visual
Basic) to a variable of type List or vice versa.
(来源:https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance)
由于您的 IGeneric
接口在其所有泛型类型参数中都是不变的,因此您尝试的转换(IGeneric<Company, long, CompanySimpleDTO>
到 IGeneric<Company, long, CompanyDTO>
)是不允许的,因为它不能保证它的 Tdto
类型参数只进入接口。
因此,如果您将 IGeneric<TEntity, TKey, Tdto>
更改为:
public interface IGeneric<TEntity, TKey, in Tdto> where TEntity: BaseEntity<TKey>
{
IQueryable<TEntity> GetAll();
IQueryable<DTO> GetAll<DTO>();
//void Add(TEntity newItem);
//void AddRange(List<TEntity> newItems);
bool Update(TEntity updateItem);
void UpdateRange(List<TEntity> updateItems);
bool Delete(TKey id);
bool DeleteRange(List<TEntity> removeItems);
TEntity GetById(TKey id);
}
应该可以。不过这里要注意两点:
我假设 CompanyDTO
继承自 CompanySimpleDTO
。
IGeneric
接口的泛型Tdto
在接口的任何地方都没有使用。如果那是错误的,并且它的使用方式实际上也需要离开界面,那么这个解决方案将行不通,恐怕您需要重新设计一下。
我为了避免 DRY 我冒昧地想到为我所有的控制器生成一个通用基础 class。一切都很好,直到插入服务 class。我的基地控制器是:
basecontroller.cs
public class BaseController<TEntity, Tdto, TKey> : Controller
{
protected TavoraContext _context;
protected IMapper _mapper;
private IGeneric<TEntity, TKey, Tdto> _srv;
public BaseController(IGeneric<TEntity, TKey, Tdto> srv)
{
_srv = srv;
}
然后,在其中一个控制器中:
companiescontroller.cs
public class CompaniesController : BaseController<Company, CompanySimpleDTO, long>
{
public CompaniesController(TavoraContext context, IMapper mapper, CompaniesService companiesService) : base(companiesService)
{
}
CompaniesService 继承自实现 IGeneric 的 GenericService,因此,在我看来应该没有错误,我得到“无法从 CompaniesService 转换为 IGeneric”
companiesservice.cs
public class CompaniesService : GenericService<Company, long, CompanyDTO>
{
public CompaniesService(TavoraContext context, IMapper mapper) : base(context, mapper)
{
_runner = new RunnerWriteDb<CompanyDTO, Company>(
new WriteCompanyAction(
new WriteCompanyDBAccess(context), mapper), context);
}
genericservice.cs
public class GenericService<TEntity, TKey, Tdto> : IGeneric<TEntity, TKey, Tdto> where TEntity : BaseEntity<TKey>
{
protected RunnerWriteDb<Tdto, TEntity> _runner;
protected readonly int PAGESIZE = 20;
protected readonly TavoraContext _context;
protected DbSet<TEntity> _currentEntity;
protected IMapper _mapper;
public GenericService(TavoraContext context, IMapper mapper)
{
_context = context;
_currentEntity = _context.Set<TEntity>();
_mapper = mapper;
}
IGeneric.cs
public interface IGeneric<TEntity, TKey, Tdto>
{
IQueryable<TEntity> GetAll();
IQueryable<DTO> GetAll<DTO>();
//void Add(TEntity newItem);
//void AddRange(List<TEntity> newItems);
bool Update(TEntity updateItem);
void UpdateRange(List<TEntity> updateItems);
bool Delete(TKey id);
bool DeleteRange(List<TEntity> removeItems);
TEntity GetById(TKey id);
RunnerWriteDbResult<TKey> Write(Tdto dto);
}
使用依赖注入将服务注入控制器。使用像 autofac 这样的 pkgs 来解决依赖关系
问题在于你的IGeneric<TEntity, TKey, Tdto>
是不变的。这意味着它在 Tdto
泛型中既不是协变也不是逆变。
来自文档:
Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types. When you are referring to a type system, covariance, contravariance, and invariance have the following definitions. The examples assume a base class named Base and a derived class named Derived.
Covariance
Enables you to use a more derived type than originally specified.
You can assign an instance of IEnumerable (IEnumerable(Of Derived) in Visual Basic) to a variable of type IEnumerable.
Contravariance
Enables you to use a more generic (less derived) type than originally specified.
You can assign an instance of Action (Action(Of Base) in Visual Basic) to a variable of type Action.
Invariance
Means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant.
You cannot assign an instance of List (List(Of Base) in Visual Basic) to a variable of type List or vice versa.
(来源:https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance)
由于您的 IGeneric
接口在其所有泛型类型参数中都是不变的,因此您尝试的转换(IGeneric<Company, long, CompanySimpleDTO>
到 IGeneric<Company, long, CompanyDTO>
)是不允许的,因为它不能保证它的 Tdto
类型参数只进入接口。
因此,如果您将 IGeneric<TEntity, TKey, Tdto>
更改为:
public interface IGeneric<TEntity, TKey, in Tdto> where TEntity: BaseEntity<TKey>
{
IQueryable<TEntity> GetAll();
IQueryable<DTO> GetAll<DTO>();
//void Add(TEntity newItem);
//void AddRange(List<TEntity> newItems);
bool Update(TEntity updateItem);
void UpdateRange(List<TEntity> updateItems);
bool Delete(TKey id);
bool DeleteRange(List<TEntity> removeItems);
TEntity GetById(TKey id);
}
应该可以。不过这里要注意两点:
我假设
CompanyDTO
继承自CompanySimpleDTO
。IGeneric
接口的泛型Tdto
在接口的任何地方都没有使用。如果那是错误的,并且它的使用方式实际上也需要离开界面,那么这个解决方案将行不通,恐怕您需要重新设计一下。