我可以安全地使用NHibernate中实体代理接口的基本类型进行查询吗?

And*_*ker 5 c# nhibernate

我目前处于多个相当复杂的系统的设计阶段,这些系统具有共同的功能(例如,它们都具有客户关系管理(CRM)和销售功能).因此,我试图提取域的公共部分并在两个应用程序中重用它.

假设我有应用程序A和应用程序B,都使用CRM功能.在大多数情况下,CRM功能是相同的,但两个应用程序都喜欢增加与CRM相关的一些东西.

我想创建一个实现CRM系统基本版本的库,例如客户被抽象化

interface ICustomer {
  string CustomerNumber {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

并且基础库具有ICustomer的基本实现

class Customer: ICustomer
Run Code Online (Sandbox Code Playgroud)

应用程序A现在可以增加这个:

interface IACustomer : ICustomer {
    bool ReceivesNewsletter {get;set;}
}

class ACustomer : Customer, IACustomer {
   ...
}
Run Code Online (Sandbox Code Playgroud)

同样,B有自己的变体:

interface IBCustomer : ICustomer {
    string NickName {get;set;}
}

class BCustomer : Customer, IBCustomer {
    ....
}
Run Code Online (Sandbox Code Playgroud)

出于抽象的目的,我从不在基础库中实例化具体类型,而是使用工厂或DI容器,例如:

interface ICrmFactory {
    ICustomer CreateCustomer();
}
Run Code Online (Sandbox Code Playgroud)

A和B相应地实施工厂,以便分别产生ACustomers或BCustomer.

这是我的意思的UML图(应用程序B未显示): 应用设计

对于持久性,我使用NHibernate.A和B都提供自己的映射(按代码映射)以包含额外的属性.此外,A将IACustomer定义为ACustomer的代理类型,B将IBCustomer定义为BCustomer的代理类型.

我现在可以使用NHibernate来处理A和B中的实体,例如在A中

session.QueryOver<ACustomer>()
    .Where(c=>c.ReceivesNewsletter)
    .List()
Run Code Online (Sandbox Code Playgroud)

现在让我们说我想在基础库中对客户做一些事情(例如在某种服务对象中).我只是原型一个非常简单的版本,到目前为止这似乎工作正常:

session.QueryOver<ICustomer>()
   .Where(c => c.CustomerNumber == "1234ABC")
   .List()
Run Code Online (Sandbox Code Playgroud)

也就是说,我可以在QueryOver中使用特定实体的代理类型的基类,并且NHibernate创建正确的查询,例如在A:

SELECT
    this_.Id as Id0_0_,
    this_.CustomerNumber as Customer2_0_0_,
    this_.ReceivesNewsletter as Receives3_0_0_ 
FROM
    ACustomer this_ 
WHERE
    this_.CustomerNumber = @p0;
@p0 = '1234ABC' [Type: String (4000)] 
Run Code Online (Sandbox Code Playgroud)

我的问题

这些查询是否始终按预期工作?我是否可以在基础库中的查询中始终安全地使用基本类型的代理接口?NHibernate会不会总是找到"正确的"实体类型和表来检索?或者是否会出现这种影响查询效率的情况,而不是b/c NHibernate需要做猜测工作?在这种情况下我应该注意的任何其他陷阱?

我在文档中找不到相关信息.这种方法可以让我巧妙地将某些域的公共部分移动到他们自己的基础库中,但我想确保它有效.

小智 1

我认为这种情况没有问题。特别是因为每个应用程序中只有一个 ICustomer 实现。

即使您对每个应用程序都有多个实现,您也可能会在按 id 加载时遇到一些问题(session.Load(42),因为可能有多个客户具有该 id,具体取决于您的映射)。但接收客户列表的查询仍然有效。

当我过去使用 NHibernate 研究与您类似的问题时,我读了这篇文章 - 它讨论的是派生基类而不是接口,但它是相同的想法: http: //codebetter.com/jameskovacs/2011/02/ 16/getload-nhibernate-3/中的多态性/

NHibernate 不会进行猜测工作 - 在初始化时,它会构建一个存储任何给定类型的所有映射的存储,当您为任何类型的类型编写查询时,它会找到它所映射的正确类型,这些类型继承自它并进行相应的查询。