c# - 如何使用存储库模式搜索子类?

标签 c# domain-driven-design repository

我有几个支付交易基本类型的子类(信用卡、支票、现金、billMeLater 等)。每个子类都有自己的存储库,因为每个子类都有自己的属性和获取方式。我需要能够搜索支付交易,而我已经走上了一条最终导致更多麻烦的道​​路。诀窍在于,有时客户需要搜索通用属性,如金额或客户姓名,有时客户需要搜索支付类型特定的属性,如信用卡号或银行路由号码......但域级搜索方法需要能够返回所有类型的交易基础。

我有以下抽象层次:

  • 具有 SearchTransactions() 方法的 WCF 层。

  • 具有 SearchTransactions() 方法的领域层。

  • 具有多个存储库的数据层,每个存储库都有一个 Search() 方法 (特定于付款类型)。

  • 数据库(通过 EF)具有典型的 DBA 所需的难以理解的困惑

你会怎么做?

编辑:

为了增加上下文,这里有一些可能的支付类型及其基础的示例:

public abstract class TransactionBase
{
    public int TransactionId { get; set; }
    public decimal Amount { get; set; }
}

public class CreditCardTransaction : TransactionBase
{
    public string CardNumber { get; set; }
    public int ExpirationMonth { get; set; }
    public int ExpirationYear { get; set; }
}

public class CheckTransaction : TransactionBase
{
    public string BankAccountNumber { get; set; }
    public string RoutingNumber { get; set; }
}

因此,客户端应该能够通过一种方法搜索 CardNumber、RoutingNumber、Amount 等。如果客户端搜索 Amount(基本参数),该方法应返回 CreditCardTransaction 和 CheckTransaction。如果客户端搜索 BankAccountNumber,它应该只返回 CheckTransactions。

客户要求和早期解决方案:

客户端要求一次调用查询多种交易类型。客户不太关心他们作为参数传递的内容,除非他们需要多次调用来涵盖所有支付类型。我之前的一个想法是使用带有搜索条件的类。然后我可以拥有搜索更具体的支付类型属性的搜索条件类的子类。像这样:

public class TransactionSearchCriteriaBase
{
    public int TransactionId { get; set; }
    public decimal Amount { get; set; }
}

public class CreditCardTransactionSearchCriteria : TransactionSearchCriteriaBase
{
    public string CardNumber { get; set; }
    public int ExpirationMonth { get; set; }
    public int ExpirationYear { get; set; }
}

因此,如果客户端想要搜索 Amount 等常见属性,他们会传入 TransactionSearchCriteriaBase。如果他们传入 CreditCardTransactionSearchCriteria,他们最终会搜索信用卡交易。例如:

var listOfTransactions = _transactionService.Search(new CreditCardTransactionSearchCriteria{ Amount = 10m, CardNumber = "1111" });

我用存储库工厂替换了几乎不可避免的 switch/if block ,该存储库工厂根据传入工厂的标准对象的类型返回适用存储库的列表。

兔子洞越来越深。我想要少一个兔子洞。

另一条信息:

由于我们在 EF 3.5 中执行此操作,因此我们不支持 POCO。因此,我们不将 EF 生成的对象视为域对象。我们的存储库将各种断开连接的 EF 对象映射到域对象,并将它们返回给调用它们的域服务。

最佳答案

我会重新考虑您的 Entity Framework 模型。

您提供的领域模型看起来非常适合Table-per-type Inheritance

然后您可以使用 LINQ .OfType<T>()根据存储库上的通用类型参数过滤不同交易类型的方法:

public class TransactionRepository : IRepository<Transaction>
{
   public TTransaction Find<TTransaction>(int transactionId) 
      where TTransaction  : TransactionBase, new()
   {
      return _ctx.Transactions.OfType<TTransaction>().SingleOrDefault(tran => tran.TransactionId == transactionId);
   }
}

然后你可以这样做:

var ccTran = _repository.Find<CreditCardTransaction>(x => x.TransactionId == 1);
var cTran = _repository.Find<CheckTransaction>(x => x.TransactionId == 2);

关于您的问题:

So, the client should be able to search on CardNumber, RoutingNumber, Amount, etc., all from one method

我认为使用一种方法是不可能的,如果没有某种 if/switch 语句肯定不行。

最重要的是过滤 - 这一切都归结为存储库方法的签名 - 提供什么,什么通用约束等。

如果您说每个子类型都有自己的存储库,那么使用一个方法为所有三个存储库提供服务真的有意义吗?这个神奇的方法应该住在哪里?

总的来说,我认为您已经达到了许多人已经达到的地步,您的领域正在与 Entity Framework 作斗争。

基本上,如果您处理 AbstractA 类型的对象集,则不能“向下转换”为 DerivedA 类型的对象集以进行过滤。

这取决于您愿意妥协多少域模型。我自己也有类似的问题,我最终使用了 TPT 继承(然后切换到 TPH,因为性能更好)。

因此,除了您提到的以外,对您的领域了解不多,我认为您需要重新考虑“每个子类都有自己的存储库,因为每个子类都有自己的属性和获取方式”这一说法.

看起来您应该有一个存储库,EF 模型中的 TPT/TPH,以及存储库上的“搜索”方法,对事务类型具有通用类型约束。

如果您热衷于使用神奇的单一方法,您将需要一个讨厌的 switch/if 语句,并将过滤委托(delegate)给特定的方法。

关于c# - 如何使用存储库模式搜索子类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6919589/

相关文章:

c# - 从 lambda 表达式获取自定义属性

java - DDD 延迟验证或始终有效的聚合根验证

Java Event Sourcing/DDD 框架不污染域层

c# - XML 存储库实现

android - 使用带有 repo 的 local_manifest.xml

c# - 如何使用循环简化 GameObject 按键代码?

c# - 窗口标题文本颜色的主题资源是什么?

c# - 当我们在 Xamarin.Forms Android 应用程序中将目标 API 版本更改为 26 时无法读取/写入文件

domain-driven-design - 领域驱动设计——它在技术领域的相关性如何?

c# - 如何首先使用 EF 代码在 .SaveChanges() 期间记录所有实体更改?