C # OOP: Использование интерфейса с параметрами типа в базовом конкретном классе - PullRequest
0 голосов
/ 31 августа 2018

Надеюсь, название описывает проблему, мне не удалось сформулировать проблему, которую я пытаюсь решить.

Я использую .NET Core 2.1

Я недавно начал работать в магазине C # после работы с Python в течение последних 5 лет, так что мой строго типизированный упс немного ржавый. Вот что у меня есть и что я пытаюсь сделать:

I Базовый интерфейс репозитория, который определяет основные функции CRUD для объектов базы данных:

public interface IRepository<TEntity, TPrimaryKey> where TEntity : class
{
   TEntity FindById(TPrimaryKey id)
   void Create(TEntity entity);
   void Delete (TEntity entity);
}

Я наследую от этого интерфейса, чтобы определить интерфейс, который реализует мой класс репозитория:

interface IProductRepository : IRepository<Product, int>
{
   void SomeProductRepoMethod(int someParam);
}

Затем я реализую все методы интерфейса в моем конкретном классе:

public class ProductRepository : IProductRepository
{
   public Product FindById(int id)
   {
      // lookup
   }

   public void Create(Product product)
   {
      // save 
   }

   public void SomeProductRepoMethod(int someParam)
   {
      // do product repository specific stuff
   }
   ....
}

Теперь то, что я хочу сделать, довольно просто. Я хочу добавить перегрузку Create на IRepository, которая занимает IEnumerable из TEntity:

public interface IRepository<TEntity, TPrimaryKey> where TEntity : class
   ...
   void Create(IEnumerable<TEntity> entities);
   ...

Но я бы хотел определить реализацию этой перегрузки Once : Так что я подумал, что смогу создать абстрактный базовый класс репозитория для реализации описанной выше реализации. Проблема, с которой я сталкиваюсь, заключается в том, что я не уверен, как или даже если бы я мог сделать это чисто с моделью, которая у меня сейчас Я попытался создать базовый класс, который реализует IRepository, но это будет означать, передача параметров типа в базовый класс и в интерфейс:

public abstract class BaseRepository<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey>
{
    public abstract TEntity FindById(TPrimaryKey id);

    public abstract void Create(TEntity entity);
    public abstract void Delete(TEntity entity);
    public void Create(IEnumerable<TEntity> entities)
    {
        foreach(TEntity entity in entities)
        {
            Create(entity);
        }
    }
}

Тогда в моем конкретном хранилище:

public class ProductRepository : BaseRepository<Product, int>, IProductRepository
{
   public override Product FindById(int id)
   {
      // lookup
   }

   public override void Create(Product product)
   {
      // save 
   }

   public void SomeProductRepoMethod(int someParam)
   {
      // do product repository specific stuff
   }
   ....
}

Мне это не кажется спокойным, поскольку я передаю параметры одного типа как в IProductRepository, так и ProductRepository. я чувствую Я близок, но не там, и я не уверен, какой подход наилучшей практики здесь будет. Если бы кто-то мог предложить подход, я бы действительно ценим обратную связь. Извиняюсь за длину поста, но я чувствовал, что должен четко описать, что я пытался сделать. Спасибо!

Ответы [ 2 ]

0 голосов
/ 31 августа 2018

Вот как я это сделаю. Я бы сломал наследство между IRepository и IProductRepository:

Вот ваши интерфейсы:

public interface IRepository<TEntity, TPrimaryKey> where TEntity : class
{
    TEntity FindById(TPrimaryKey id);
    void Create(TEntity entity);
    void Delete(TEntity entity);
    void Create(IEnumerable<TEntity> entities);
}

internal interface IProductRepository
{
    void SomeProductRepoMethod(int someParam);
}

Тогда пусть ваш базовый класс наследует IRepository, как вы сделали:

базовый класс:

public abstract class BaseRepository<TEntity, TPrimaryKey> : 
    IRepository<TEntity, TPrimaryKey> where TEntity : class
{
    public abstract TEntity FindById(TPrimaryKey id);
    public abstract void Create(TEntity entity);
    public abstract void Delete(TEntity entity);

    public void Create(IEnumerable<TEntity> entities)
    {
        foreach (TEntity entity in entities)
        {
            Create(entity);
        }
    }
}

и затем вы выводите свой базовый класс и также реализуете IProductRepository:

public class ProductRepository : BaseRepository<Product, int>, IProductRepository
{
    public override Product FindById(int id)
    {
        // find
    }

    public override void Create(Product product)
    {
        // save 
    }

    public void SomeProductRepoMethod(int someParam)
    {
        // do product repository specific stuff
    }

    public override void Delete(Product entity)
    {
        // delete
    }
}

Я думаю, что специфика вашего производного класса в том, что он является Product репозиторием, является подробностью реализации BaseRepository.

0 голосов
/ 31 августа 2018

Наличие одних и тех же параметров типа в интерфейсе и абстрактном классе не так уж сложно. Использование вашего абстрактного решения класса нормально, если только ваш ProductRepository не должен наследовать от другого класса.

На самом деле, с вашим абстрактным классом, ваш IRepository интерфейс больше не должен существовать. Просто справляйся со всем с BaseRepository!

Другим решением этой проблемы является метод расширения. В статическом классе вы можете написать это:

public static void Create<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repo, IEnumerable<TEntity> entities) where TEntity : class {
    // do your foreach loop here
} 

Теперь вы можете вызывать этот метод для любого экземпляра IRepository, например:

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