неявная ошибка преобразования ссылок с наследованием дженериков - PullRequest
0 голосов
/ 27 апреля 2020

В моем приложении есть следующий набор классов:

Это представляет собой сущность, которая является таблицей SQL. Все классы сущностей наследуются от этого интерфейса.

public interface IBaseEntity
{
     public int Id { get; set; }
}

Тогда у меня есть класс, который будет хранить все бизнес-логики c для этой сущности. BaseBLL содержит методы, которые являются общими для всех бизнес-объектов.

public class BLLPai<D>
        where D : class
{
        protected D EntidadeData;
}
public class BaseBLL<E, D> : BLLPai<D>
        where E: IBaseEntity
        where D: BaseData<E>
{
     //commom methods
}

Чтобы создать бизнес-объект для сущности, также необходимо создать объект Data, который будет содержать все команды, связанные с базой данных. .

 public class BaseData<E> : DataPai
        where E : IBaseEntity
{
     // commom methods
}

public class DataPai
{
    protected DbConnection dbCon { get; set; }
     public DataPai()
    {
        dbCon = new DbConnection();
    }
}

Это пример рабочего объекта (таблицы базы данных) в этом проекте:

public class MyTable : IBaseEntity
{
    public int Id {get;set;}
    public string Name {get;set;}
}
public MyTableData: BaseData<MyTable>
{
    public List<MyTable> GetAllItems()
    {
        // return result of SELECT
    }
}
public MyTableBLL : BaseBLL<MyTable, MyTableData>
{
    public List<MyTable> GetAllItems()
    {
         // business logic
         return this.EntidadeData.GetAllItems();
    }
}

Чтобы использовать это в моей архитектуре, я разработал базу контроллеров. класс, который я использую во всех моих контроллерах. (Я использую это в WebApi. Net Базовый проект) Класс выглядит следующим образом:

public class MyTableController : GerenciadorLogadaCadastroController<MyTable, MyTableBLL>
{
}

[ApiController]
public abstract class GerenciadorLogadaCadastroController<Entidade, BLL> : ControllerBase
        where Entidade : IBaseEntity
        where BLL : BaseBLL<Entidade, BaseData<Entidade>>
{
}

Что я не понимаю, так это то, почему он выдает ошибку при попытке создать MyTableController класс:

Ошибка:

Тип «MyTableBLL» нельзя использовать в качестве параметра типа «BLL» в типе или методе generi c «GerenciadorLogadaCadastroController<Entidade,BLL>». Не существует неявного преобразования ссылок из 'MyTableBLL' в 'BaseBLL<MyTable, BaseData<MyTable>>'.

Однако, если я заменил GerenciadorLogadaCadastroController и MyTableController на это, похоже, он будет работать нормально.

public abstract class GerenciadorLogadaCadastroController<Entidade, BLL, Data> : ControllerBase
        where Entidade : IBaseEntity
        where BLL : BaseBLL<Entidade, Data>
        where Data: BaseData<Entidade>
{
}

public class MyTableController : GerenciadorLogadaCadastroController<MyTable, MyTableBLL, MyTableData>
{
}

Но я не хочу вводить класс Data, потому что он уже включен в класс BLL. Я нашел похожий дизайн того, что я пытаюсь сделать в приложении Vb. Net, и он работает просто отлично. Что мне здесь не хватает, что вызывает эту неявную ошибку преобразования ссылок?

Ответы [ 2 ]

1 голос
/ 27 апреля 2020

Я немного упростил ваш код, чтобы привести его к примеру, показывающему, что пошло не так. Есть несколько шагов.

Вот мое первоначальное упрощение:

public interface IBaseEntity { }
public class MyTable : IBaseEntity { }
public class BaseData<E> where E : IBaseEntity { }
public class MyTableData : BaseData<MyTable> { }
public class BaseBLL<E, D> where E : IBaseEntity where D : BaseData<E> { }
public class MyTableBLL : BaseBLL<MyTable, MyTableData> { }
public abstract class Controller<E, B> where E : IBaseEntity where B : BaseBLL<E, BaseData<E>> { }
public class MyTableController : Controller<MyTable, MyTableBLL> { }

Теперь, на этом этапе, вся часть кода IBaseEntity не нужна для репликации вашей проблемы, поэтому Я удалил его:

public class BaseData { }
public class MyTableData : BaseData { }
public class BaseBLL<D> where D : BaseData { }
public class MyTableBLL : BaseBLL<MyTableData> { }
public abstract class Controller<B> where B : BaseBLL<BaseData> { }
public class MyTableController : Controller<MyTableBLL> { }

Теперь я сделал несколько хитрых переименований типов:

public class Fruit { }
public class Apple : Fruit { }
public class BowlOf<D> where D : Fruit { }
public class BowlOfApples : BowlOf<Apple> { }
public abstract class Controller<B> where B : BowlOf<Fruit> { }
public class BowlOfApplesController : Controller<BowlOfApples> { }

То, что это показывает, это то, что когда вы определяете Controller<BowlOfApples>, вы говорите, что BowlOfApples должен наследоваться от BowlOf<Fruit>. Но BowlOfApples наследуется от BowlOf<Apple>, а не BowlOf<Fruit>!

Это ограничение должно иметь смысл. Давайте предположим, что BowlOf<T> : List<T> и у нас есть Banana : Fruit, затем представьте, что этот код был допустимым:

BowlOf<Apple> apples = new BowlOf<Apple>();
BowlOf<Fruit> fruit = apples; // BOOM!!! imagine if this could be done!
Banana banana = new Banana();
fruit.Add(banana);

У нас есть код, который может добавить Banana к BowlOf<Apple>! Этого не может быть Нелегальная вещь - бросить BowlOf<Apple> в BowlOf<Fruit>.

Проще говоря, BowlOf<Apple> НЕ Унаследует от BowlOf<Fruit>. Это несмотря на Apple наследование от Fruit.

Ваш код делает ту же ошибку, когда вы пытаетесь привести BaseBLL<MyTable, MyTableData> к BaseBLL<MyTable, BaseData<MyTable>>.

Вы правильно определили в вашем вопросе, что это изменение будет работать:

public abstract class Controller<F, B> where F : Apple where B : BowlOf<F> { }
public class BowlOfApplesController : Controller<Apple, BowlOfApples> { }

Итак, когда вы говорите: «Но я не хочу вводить класс Data, потому что он уже включен в класс BLL». Ну, вы не можете избежать этого.

0 голосов
/ 27 апреля 2020

Вот предложение. Добавьте интерфейс IBaseDLL.

  public interface IBaseBLL
  {
    //commom methods
  }

Затем добавьте его в список интерфейсов вашего класса BaseBLL.

  public class BaseBLL<E, D> : BLLPai<D>, IBaseBLL
          where E : class, IBaseEntity
          where D : BaseData<E>
  {
    //commom methods
  }

Теперь вы сможете изменить свой базовый контроллер на следующее:

[ApiController]
public abstract class GerenciadorLogadaCadastroController<Entidade, BLL> : ControllerBase
        where Entidade : IBaseEntity
        where BLL : IBaseBLL
{
}

-Isaa c

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...