Как создать интерфейс базы данных

чтобы обеспечить доступ к объектам в моей базе данных, я создал интерфейс для всех членов команды, которые будут использоваться следующим образом (упрощенный пример):

public interface IDatabase
{
   ObservableCollection<Animal> Animals{ get; }
}

Я не хотел, чтобы команда получала доступ к внутренним данным, таким как контекст базы данных или некоторые объекты oracle (инкапсуляция)...

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

public class TestDatabase : IDatabase
{ }

public class OracleDatabase : IDatabase
{ }

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

public interface IDatabase
{
   ObservableCollection<Animal> Animals{ get; }
   ObservableCollection<Animal> Animals(Gender gender);
   ObservableCollection<Animal> Animals(Gender gender, Race race);
}

некоторые из фильтрации и сортировки материала, конечно, может быть сделано самим разработчиком, но это лучше расположен внутри базы данных.


моя проблема теперь, когда мой интерфейс взрывается, он становится все более специализированными функциями каждый день, он далек от стабильности и постоянно меняется.

мой дизайн испорчен прямо из начать?

некоторые идеи для решения этой проблемы:

  1. выставить объект контекста базы данных всем разработчикам (плохо, я думаю)
  2. добавить функцию, которая принимает запрос linq

2 ответов


вы пытаетесь изобрести Шаблоны репозитория / UnitOfWork, и вы делаете это не совсем правильно.

правильный подход был бы близок к этому:

// shared between repositories
public interface IGenericRepository<T> 
{
    T CreateNew();

    void Delete( T item );
    void Update( T item );
    void Insert( T item );

    IEnumerable<T> FindAll();
    T FindOne( int id );
}

// specific repositories
public interface IAnimalRepository : IGenericRepository<Animal>
{
    IEnumerable<Animal> FindByNumberOfLegs( int NumberOfLegs );
    // ... anything specific follows
}

public interface IHumanRepository : IGenericRepository<Human>
{
    IEnumerable<Human> FindByGender( Gender gender );
    //  ... specific repository logic follows
}

// unit of work - a service for clients
public interface IUnitOfWork : IDisposable
{
    IAnimalRepository AnimalRepository { get; }
    IHumanRepository  HumanRepository { get; }
    // .. other repositories follow

    void SaveChanges(); 
}

таким образом, ваш уровень сервиса зависит от уровня репозитория, и вы можете легко переключаться между реализациями, например, для модульного тестирования. Ваши клиенты пишут

// example code
using ( IUnitOfWork uow = new YourImplementationOfUnitOfWork() )
{
   var animals = uow.AnimalRepository.FindByNumberOfLegs( 3 );

   var person = uow.HumanRepository.CreateNew();
   person.Name = "John";
   uow.HumanRepository.Insert( person );

   uow.SaveChanges();
}

если вы планируете ограничить количество методов, вы можете просто немного изменить репозиторий интерфейс:

// shared between repositories
public interface IGenericRepository<T> 
{
    T CreateNew();

    void Delete( T item );
    void Update( T item );
    void Insert( T item );

    IQueryable<T> Query { get; }
}

таким образом, ваши клиенты могут использовать LINQ:

// example code
using ( IUnitOfWork uow = new YourImplementationOfUnitOfWork() )
{
   var animals = uow.AnimalRepository.Query.Where( a => a.NumberOfLegs == 3 );

   var person = uow.HumanRepository.CreateNew();
   person.Name = "John";
   uow.HumanRepository.Insert( person );

   uow.SaveChanges();
}

могу ли я предложить применить принцип сегрегации интерфейса? т. е. разделите интерфейсы на логические группы. Это также позволит пользователям вашего интерфейса не реализовывать части, которые они не используют / не нуждаются. Стабильность также должна увеличиваться, поскольку у вас будут более сдержанные части тестируемого кода.