如何设计数据库接口

本文关键字:数据库 接口 | 更新日期: 2023-09-27 18:25:58

为了提供对数据库中对象的访问,我为所有团队成员创建了一个接口,如下所示(简化示例):

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查询的函数

如何设计数据库接口

您正在尝试重新发明Repository/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();
}

我可以建议应用接口隔离原则吗?即将您的接口分成逻辑组。这也将允许界面的用户不实现他们不使用/不需要的部分。稳定性也应该提高,因为您将拥有更谨慎的可测试代码。