Generi c сервис в Generi c Базовый контроллер - PullRequest
0 голосов
/ 12 января 2020

Я приказываю избегать DRY Я рискнул воплотить идею генерации базового c базового класса для всех моих контроллеров. Все было хорошо до момента вставки сервисного класса. Мой базовый контроллер:

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 наследуется от GenericService, который реализует IGeneri c, поэтому, по моему мнению, не должно быть никакой ошибки, и я получаю: «Невозможно преобразовать CompaniesService в IGeneri c '

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

IGeneri c .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);
}

Ответы [ 2 ]

1 голос
/ 13 января 2020

Проблема заключается в том, что ваш IGeneric<TEntity, TKey, Tdto> инвариантен. Это означает, что он не является ни ковариантным, ни контравариантным в типе Tdto generi c.

Из документации:

Ковариантность и контравариантность - это термины, которые относятся к возможности использовать более производный тип (более конкретный c) или менее производный тип (менее конкретный c), чем указано изначально. Параметры типа Generi c поддерживают ковариацию и контравариантность для обеспечения большей гибкости при назначении и использовании типов generi c. Когда вы имеете в виду систему типов, ковариация, контравариантность и инвариантность имеют следующие определения. В примерах предполагается, что базовый класс с именем Base и производный класс с именем Derived.

Covariance

Позволяет использовать более производный тип, чем было указано изначально.

Вы можете назначить экземпляр IEnumerable (IEnumerable (Of Derived) в Visual Basi c) для переменной типа IEnumerable.

Contravariance

Позволяет использовать более обобщенный c (менее производный) типа, чем изначально указано.

Вы можете назначить экземпляр Action (Action (Of Base) в Visual Basi c) переменной типа Action.

Invariance

Означает, что вы можете использовать только тот тип, который указан изначально; таким образом, инвариантный параметр типа generi c не является ни ковариантным, ни контравариантным.

Нельзя назначить экземпляр List (List (Of Base) в Visual Basi c) переменной типа List или наоборот. .

(источник: https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance)

Поскольку ваш интерфейс IGeneric инвариантен во всех своих типовых параметрах типа c, приведите вас попытка (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);
}

Должно работать. Здесь следует отметить две вещи:

  1. Я предполагаю, что CompanyDTO наследуется от CompanySimpleDTO.

  2. Тип c generic * тип Tdto интерфейса IGeneric нигде в интерфейсе не используется. Если это неправильно и используется таким образом, что на самом деле ему также нужно go из интерфейса, то это решение не будет работать, и я боюсь, что вам придется немного его переработать.

0 голосов
/ 12 января 2020

Использовать внедрение зависимостей для внедрения службы в контроллер. Используйте pkgs как autofa c для разрешения зависимости

...