【问题标题】:Generic service in Generic Base Controller通用基本控制器中的通用服务
【发布时间】:2020-01-11 23:19:58
【问题描述】:

为了避免 DRY,我冒险尝试为我的所有控制器生成一个通用基类。在插入服务类之前一切都很好。我的基本控制器是:

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 之类的工具来解决依赖关系

标签: c# generics .net-core repository-pattern


【解决方案1】:

问题在于您的IGeneric&lt;TEntity, TKey, Tdto&gt; 是不变的。这意味着它在 Tdto 泛型类型中既不是协变也不是逆变。

来自文档:

协变和逆变是指以下能力的术语 使用更派生的类型(更具体)或更少派生的类型(更少 具体)比最初指定的。泛型类型参数支持 协变和逆变提供更大的灵活性 分配和使用泛型类型。当你提到一种类型时 系统,协变,逆变和不变性具有以下 定义。这些示例假设一个名为 Base 的基类和一个派生类 名为 Derived 的类。

协方差

使您能够使用比最初指定更多的派生类型。

您可以分配一个 IEnumerable (IEnumerable(Of 派生)在 Visual Basic 中)到 IEnumerable 类型的变量。

逆变

使您能够使用比原来更通用(较少派生)的类型 指定。

您可以在 Visual 中分配一个 Action (Action(Of Base) 的实例 Basic) 到 Action 类型的变量。

不变性

表示只能使用最初指定的类型;所以一个 不变的泛型类型参数既不是协变的,也不是 逆变的。

您不能在 Visual 中分配 List (List(Of Base) 的实例 Basic) 到 List 类型的变量,反之亦然。

(来源:https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance

由于您的 IGeneric 接口在其所有泛型类型参数中都是不变的,因此您尝试的转换(IGeneric&lt;Company, long, CompanySimpleDTO&gt;IGeneric&lt;Company, long, CompanyDTO&gt;)是不允许的,因为它不能保证它是 Tdto 类型参数只进入界面。

因此,如果您将 IGeneric&lt;TEntity, TKey, Tdto&gt; 更改为:

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);
}

它应该工作。不过这里需要注意两点:

  1. 我假设 CompanyDTO 继承自 CompanySimpleDTO

  2. IGeneric 接口的泛型类型Tdto 不在接口的任何地方使用。如果这是错误的,并且它的使用方式实际上也需要脱离界面,那么这个解决方案将不起作用,恐怕你需要重新设计一下。

【讨论】:

    【解决方案2】:

    使用依赖注入将服务注入控制器。使用像 autofac 这样的 pkgs 来解决依赖关系

    【讨论】:

    • 谁给过-1,你能给出同样的理由吗?
    • 我不是给 -1 的人之一,但我认为这是因为这不是解决 OP 选角问题的答案。我实际上假设 OP 已经在他的应用程序中使用了某种依赖注入。因此,虽然使用 DI 是一个很好的建议,但它不会帮助 OP 解决他的问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-08
    • 1970-01-01
    • 2012-06-01
    • 2013-03-24
    • 1970-01-01
    • 2012-12-26
    • 1970-01-01
    相关资源
    最近更新 更多