c# - 在以下情况下正确使用抽象类或接口(interface)

标签 c# entity-framework abstract-class repository-pattern

我有不同类型的文档:DocumentCitizen、DocumentOther、DocumentResolution 等。每种文档类型也有相应的存储库:DocumentCitizenRepository、DocumentOtherRepository、DocumentResolutionRepository。所有存储库都有一个名为 Documents 的属性和一个名为 SaveDocument 的方法。下面以 DocumentCitizenRepository 的内容为例:

public class DocumentCitizenRepository{

   EFDbContext context= new EFDbContext();//EFDbContext inherits DbContext        
   public IQueryable<DocumentCitizen> Documents{get{context.DocmentsFromCitizens}}
   public void SaveDocument(DocumentCitizen doc)
   {//omitted for brevity}
}

其他存储库的内容相似。因此,我希望所有存储库都扩展一个名为 Repository 的抽象类。我还为所有名为 Document 的文档类型定义了一个抽象类,内容为空:

public abstract class Document{}

public abstract class Repository{
  public abstract IQueryable<Document>{get;}
  public abstract void SaveDocument(Document doc)
}

但我收到有关无效或缺少覆盖的警告,因为编译器需要属性和方法的签名相同。要查看差异:

基础抽象类中的方法:

  public abstract void SaveDocument(Document doc)

以及继承类中的那个:

  public abstract void SaveDocument(DocumentCitizen doc)

所以我很困惑是否可以做我想做的事。你有什么想法吗?

最佳答案

最有可能的是,您甚至可以更进一步,创建一个完全通用的基础存储库:

public interface IRepository<T>
{
    IQueryable<T> GetQueryable();
    void Save(T item);
}

public abstract class BaseRepository<T> : IRepository<T>
{
    public IQueryable<T> GetQueryable() { ... }
    public void Save(T item) { ... }
}

您仍然会有特定的存储库接口(interface),允许您创建特定于实体的方法:

public interface IDocumentCitizenRepository : IRepository<DocumentCitizen>
{ 
    // this interface has no extra methods, just the plain ol' CRUD
}

public interface IDocumentResolutionRepository : IRepository<DocumentResolution>
{ 
    // this one can do additional stuff
    void DoSomethingSpecial(DocumentResolution doc);
}

抽象基类用于重用通用功能:

// this class will inherit everything from the base abstact class
public class DocumentCitizenRepository 
   : BaseRepository<DocumentCitizen>, IDocumentCitizenRepository
{ }

// this class will inherit common methods from the base abstact class,
// and you will need to implement the rest manually
public class DocumentResolutionRepository 
   : BaseRepository<DocumentResolution>, IDocumentResolutionRepository
{ 
    public void DoSomethingSpecial(DocumentResolution doc)
    {
        // you still need to code some specific stuff every once in a while
    }
}

这通常与依赖注入(inject)一起使用,这样您就可以只在业务层使用接口(interface)而不用关心实际的实现:

var repo = container.Resolve<IDocumentCitizenRepository>();
repo.Save(doc);

最后一部分(依赖项注入(inject))最好在组合根中完成,这意味着直接在整个代码中使用容器(“服务定位器”模式)并不是抽象这些依赖项的理想方式。尝试组织您的类以通过构造函数接收存储库接口(interface),这将允许您在进行单元测试时轻松模拟每个存储库。

关于c# - 在以下情况下正确使用抽象类或接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24887551/

相关文章:

c# - 允许用户使用 Entity Framework 在运行时定义表

c++ - 抽象类中的嵌套类

java - 用模式设计风格的抽象方法定义抽象类

c# - H264 -> 使用 .NET 的容器

javascript - C# MVC 站点 - 将 Canvas 保存到服务器上的图像文件 - 图像正在被裁剪

c# - 通过网络浏览器打开 .xls/.xlsx 文件并使其对用户可编辑

c# - ItemsControl 显示类名

c# - 在类构造函数中将 icollection 设置为列表时,如何通过索引从 icollection 中获取项目?

c# - Entity Framework - 填充属性后运行函数

java - 正确使用泛型类型避免不必要的转换(SuppressWarnings 未经检查的转换)