c# - 通用基本控制器中的通用服务
问题描述
为了避免 DRY,我冒险尝试为我的所有控制器生成一个通用基类。在插入服务类之前一切都很好。我的基本控制器是:
基本控制器.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;
}
然后,在其中一个控制器中:
公司控制器.cs
public class CompaniesController : BaseController<Company, CompanySimpleDTO, long>
{
public CompaniesController(TavoraContext context, IMapper mapper, CompaniesService companiesService) : base(companiesService)
{
}
CompaniesService 从实现 IGeneric 的 GenericService 继承,所以在我看来应该没有错误,我得到“不可能从 CompaniesService 转换为 IGeneric”
公司服务.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);
}
通用服务.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);
}
解决方案
问题在于你IGeneric<TEntity, TKey, Tdto>
是不变的。Tdto
这意味着它在泛型类型中既不是协变的也不是逆变的。
从文档中:
协变和逆变是指使用比最初指定的更多派生类型(更具体)或更少派生类型(更具体)的能力的术语。泛型类型参数支持协变和逆变,以便在分配和使用泛型类型时提供更大的灵活性。当您提到类型系统时,协变、逆变和不变性具有以下定义。这些示例假定一个名为 Base 的基类和一个名为 Derived 的派生类。
协方差
使您能够使用比最初指定更多的派生类型。
您可以将 IEnumerable 的实例(在 Visual Basic 中为 IEnumerable(Of Derived))分配给 IEnumerable 类型的变量。
逆变
使您能够使用比最初指定的更通用(较少派生)的类型。
您可以将 Action 实例(Visual Basic 中的 Action(Of Base))分配给 Action 类型的变量。
不变性
意味着您只能使用最初指定的类型;所以一个不变的泛型类型参数既不是协变的也不是逆变的。
您不能将 List 的实例(Visual Basic 中的 List(Of Base))分配给 List 类型的变量,反之亦然。
(来源:https ://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance )
由于您的IGeneric
接口在所有泛型类型参数中都是不变的,因此不允许您尝试 ( IGeneric<Company, long, CompanySimpleDTO>
to 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
.接口的泛型类型
Tdto
不在IGeneric
接口的任何地方使用。如果这是错误的,并且它的使用方式实际上也需要脱离界面,那么这个解决方案将不起作用,恐怕你需要重新设计一下。
推荐阅读
- javascript - 如何使用类 javascript 使线条跟随鼠标事件 arctanget
- flutter - 简单代码以这个错误结束:Gradle task assembleDebug failed with exit code 1
- batch-file - 我想在批处理文件和 yaml 文件中设置变量路径
- python - 为什么在带有“下一个”和“上一个”按钮的音乐播放器中使用双向链表,而使用列表更简单?
- google-cloud-functions - Cloud Vision 和 Cloud Functions 返回空响应
- jenkins - 从 jenkins 管道启动 appium 服务器
- python - 我如何在多个文本文件中搜索模式并写入新的多个文本文件
- android - 如何在没有 WebXrviewer 的情况下在 iPhone 上进行 AR 开发
- python - 如何使用序列化程序以嵌套方式从 django 模型中检索数据?
- javascript - Javascript:检查数组是否为空