我在 C# 中使用 Repository 模式时遇到问题,尤其是当我也尝试实现 Façade 模式时。我的概念如下:
当我第一次启动通用存储库时,我从一个将所有 CRUD 操作都放在一个文件中(以及它们的相关接口(interface)放在一个单独的文件中)开始的。但由于 SOLID 原则,尤其是 ISP 原则,我决定将所有接口(interface)隔离到单独的文件中,对类也是如此。
因此,例如,不要在一个文件中使用 IGenericRepo 和各种 Create、Read ... 方法,而在另一个文件中使用相应的 GenericRepo。我将它们全部隔离开来,有一个基本的 repo 协议(protocol)来做任何常见的事情。所以我最终得到了一个 ICreateRepo、一个 IReadOneRepo、一个 IReadManyRepo 等。
随着时间的推移,我的项目需求不断增长,我发现自己需要多个“读取”操作:
- 读取给定id的单条记录,
public T Read(int id)
- 读取可能具有必须传入多个值的复合初选的单个记录。像 EF Find 方法一样工作......即
public T Read(params object[] keyValues)
- 根据对任何字段的搜索返回第一条记录...例如 EF Where 方法,其参数如下...
public T Read(Expression<Func<T>,bool>> predicate)
这很好,直到我遇到需要读取多条记录并返回符合条件的所有记录的列表的情况。本质上,读取操作与上一个提到的具有相同的方法签名,不同之处仅在于其返回类型。一个返回单个实体,另一个返回匹配实体列表。
public IQueryable<T> Read(Expression<Func<T>,bool>> predicate)
虽然分成了自己的类(class),但没有问题。然而,我在我的 Controller (我使用 MVC)中发现我有大约 6 或 9 个存储库的长列表,我想将其简化为一个类似于我对单个通用的存储库。所以我转向了外观模式。
现在,当我将读取功能组合在一起时,我会遇到多态行为问题,因为签名是相同的。
//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()
{ }
如前所述...当分成单独的文件时,它们工作正常,因为它们会根据需要明确调用。但是我想使用 Façade 模式来简化代码。
我遇到的问题当然是没有基于返回类型的多态性。我理解并完全理解这样做的原因。
我正在考虑在搜索末尾添加一个可选的 bool 参数。但出于某种原因感觉不对。像...
//Search
public IQueryable<T> Read(Expression<Func<T, bool>> predicate, bool returnOne = false)
{ }
所以我的问题是,有没有人知道如何绕过这个限制?
最佳答案
您可以重命名返回的单个实体 Read
至 ReadSingle
, 以明确指出,即使它采用谓词作为参数,它也只返回一个实体。我会尝试重命名先。
如果您更喜欢其他选项,您可以在参数中区分方法签名。您可以为单一返回的古怪情况创建谓词包装类型:
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;
}
}
给定转换运算符,您可以直接使用包装器作为其谓词。为了让调用者更简单,您还可以扩展 Expression<..>
更容易地创建这个包装器:
public static class Predicates {
public static ExpressionToFindSingle<T> ForSingle<T>(this Expression<Func<T, bool>> predicate) {
return new ExpressionToFindSingle<T>(predicate);
}
}
然后,您的类可以拥有这些方法(仅显示它们的签名):
T Read(ExpressionToFindSingle<T> predicate);
IQueryable<T> Read(Expression<Func<T, bool>> predicate);
关于c# - 存储库模式上泛型的多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27475955/