存储库模式上具有泛型的多态性

Fra*_*ers 5 c# generics asp.net-mvc design-patterns repository

我在 C# 中使用 Repository 模式时遇到问题,尤其是当我也尝试实现 Façade 模式时。我的概念如下:

当我第一次启动通用存储库时,我从一个在单个文件中包含所有 CRUD 操作(以及在一个单独的单个文件中的相关接口)开始的存储库。但是由于 SOLID 原则,尤其是 ISP 原则,我决定将所有接口隔离​​到单独的文件中,并对类进行同样的处理。

因此,例如,不要在一个文件中使用 IGenericRepo 和各种 Create、Read ... 方法,而在另一个文件中使用相应的 GenericRepo。我把它们全部隔离开来,有一个基本的回购来做任何常见的事情。所以我最终得到了一个 ICreateRepo、一个 IReadOneRepo、一个 IReadManyRepo ......等等。

随着时间的推移,我的项目需求不断增长,我发现自己需要多个“读取”操作:

  • 读取具有给定 id 的单个记录,
     public T Read(int id)
Run Code Online (Sandbox Code Playgroud)
  • 读取单个记录,该记录可能具有必须传入多个值的复合原色。像 EF Find Method 一样工作......即
     public T Read(params object[] keyValues)
Run Code Online (Sandbox Code Playgroud)
  • 根据对任何字段的搜索返回第一条记录……就像 EF Where 方法,其参数类似于……
     public T Read(Expression<Func<T>,bool>> predicate)
Run Code Online (Sandbox Code Playgroud)

这很好,直到我遇到需要读取多条记录并返回符合条件的所有记录的列表的情况。本质上,读取操作具有与上一个提到的相同的方法签名,不同之处仅在于其返回类型。一个返回单个实体,另一个返回匹配实体的列表。

   public IQueryable<T> Read(Expression<Func<T>,bool>> predicate)
Run Code Online (Sandbox Code Playgroud)

虽然分成他们自己的班级,但没有问题。然而,我在我的控制器(我使用 MVC)中发现我有大约 6 或 9 个存储库的长列表,我想将其简化为一个类似于我对单个泛型的存储库列表。所以我转向了 Facade 模式。

现在,当我将读取功能组合在一起时,我会遇到多态行为的问题,因为签名是相同的。

    //ReadOne 1
    public T Read(int id)
    { }
    //ReadOne 2
    public T Read(params object[] keyValues)
    { }
    //ReadOne 3 *** Signature same as search except for return type.
    public T Read(Expression<Func<T, bool>> predicate)
    {
        //SingleOrDefault used purposefully instead of FirstOrDefault to cause exception if
        //there is more than one instance that meets the predicate.
        return dbSet.Where(predicate).SingleOrDefault<TEntity>();
    }

    //Search
    public IQueryable<T> Read(Expression<Func<T, bool>> predicate)
    {
        return dbSet.Where(predicate);
    }

    //ReadAll
    public IQueryable<T> Read()
    { }
Run Code Online (Sandbox Code Playgroud)

如前所述......当分成单独的文件时,这些工作正常,因为它们根据需要被显式调用。但是我想使用 Façade 模式来简化代码。

我当然遇到的问题是没有基于返回类型的多态性。我理解并完全理解这样做的原因。

我正在考虑在搜索的末尾添加一个可选的 bool 参数。但不知为何,感觉不太对劲。就像是...

    //Search
    public IQueryable<T> Read(Expression<Func<T, bool>> predicate, bool returnOne = false)
    { }
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,有没有人知道如何解决这个限制?

Jor*_*dão 3

您可以将返回的单个实体重命名ReadReadSingle,以明确表示,即使它采用谓词作为参数,它也只返回单个实体。我会尝试重命名。

如果您更喜欢其他选项,您可以在参数中区分方法签名。您可以为单返回奇怪的情况创建谓词包装类型:

public class ExpressionToFindSingle<T> {
  private Expression<Func<T, bool>> predicate;
  public ExpressionToFindSingle(Expression<Func<T, bool>> predicate) {
    this.predicate = predicate;
  }
  public static implicit operator Expression<Func<T, bool>>(ExpressionToFindSingle<T> wrapper) {
    return wrapper.predicate;
  }
}
Run Code Online (Sandbox Code Playgroud)

给定转换运算符,您可以直接使用包装器作为其谓词。为了让调用者更简单,您还可以扩展Expression<..>以更轻松地创建此包装器:

public static class Predicates {
  public static ExpressionToFindSingle<T> ForSingle<T>(this Expression<Func<T, bool>> predicate) {
    return new ExpressionToFindSingle<T>(predicate);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,您的类可以具有这些方法(仅显示它们的签名):

T Read(ExpressionToFindSingle<T> predicate);

IQueryable<T> Read(Expression<Func<T, bool>> predicate);
Run Code Online (Sandbox Code Playgroud)