强制惰性实体加载真实实例

2024-03-02

我有一个惰性实体的代理,它是通过加载子实体在会话中创建的。对父实体的后续提取仅返回 NH 代理。我需要实际实例来检查类型(实体已加入子类)。我一定错过了一些东西,但我找不到办法做到这一点。 Session.Refresh(proxy) 似乎没有帮助,我尝试过的任何 HQL 风格也没有帮助。

有人可以帮忙吗?


在我看来,与其解决这个问题,不如重新考虑你的设计。您绝对确定在这种情况下不能使用多态性 - 要么直接让实体负责您尝试执行的操作,要么使用访问者模式。我几次遇到这个问题,并且总是决定改变设计 - 它导致了更清晰的代码。我建议您也这样做,除非您完全确定依赖类型是最好的解决方案。

问题

为了让示例至少与现实世界有一些相似之处,我们假设您有以下实体:

public abstract class Operation
{
    public virtual DateTime PerformedOn { get; set; }
    public virtual double Ammount { get; set; }
}

public class OutgoingTransfer : Operation
{
    public virtual string TargetAccount { get; set; }
}

public class AtmWithdrawal : Operation
{
    public virtual string AtmAddress { get; set; }
}

它自然是更大模型的一小部分。现在您面临一个问题:对于每种具体类型的操作,都有不同的显示方式:

private static void PrintOperation(Operation operation)
{
    Console.WriteLine("{0} - {1}", operation.PerformedOn,
                      operation.Ammount);
}

private static void PrintOperation(OutgoingTransfer operation)
{
    Console.WriteLine("{0}: {1}, target account: {2}",
                      operation.PerformedOn, operation.Ammount,
                      operation.TargetAccount);
}

private static void PrintOperation(AtmWithdrawal operation)
{
    Console.WriteLine("{0}: {1}, atm's address: {2}",
                      operation.PerformedOn, operation.Ammount,
                      operation.AtmAddress);
}

简单的重载方法将在简单的情况下工作:

var transfer = new OutgoingTransfer
               {
                   Ammount = -1000,
                   PerformedOn = DateTime.Now.Date,
                   TargetAccount = "123123123"
               };

var withdrawal = new AtmWithdrawal
                 {
                     Ammount = -1000,
                     PerformedOn = DateTime.Now.Date,
                     AtmAddress = "Some address"
                 };

// works as intended
PrintOperation(transfer);
PrintOperation(withdrawal);

不幸的是,重载方法是在编译时绑定的,因此一旦引入数组/列表/任何操作,只会调用通用(操作操作)重载。

Operation[] operations = { transfer, withdrawal };
foreach (var operation in operations)
{
    PrintOperation(operation);
}

这个问题有两种解决方案,但都有缺点。您可以在操作中引入抽象/虚拟方法来将信息打印到选定的流。但这会将 UI 问题混合到您的模型中,因此这对您来说是不可接受的(稍后我将向您展示如何改进此解决方案以满足您的期望)。

您还可以以以下形式创建大量 if:

if(operation is (ConcreteType))
   PrintOperation((ConcreteType)operation);

这个解决方案很丑陋并且容易出错。每次添加/更改/删除操作类型时,您都必须检查使用这些 hack 的每个地方并对其进行修改。如果您错过了一个地方,您可能只能捕获该运行时 - 对某些错误(例如缺少一种子类型)没有严格的编译时检查。

此外,一旦引入任何类型的代理,该解决方案就会失败。

代理如何工作

下面的代码是非常简单的代理(在这个实现中,它与装饰器模式相同 - 但这些模式通常并不相同。需要一些额外的代码来区分这两种模式)。

public class OperationProxy : Operation
{
    private readonly Operation m_innerOperation;

    public OperationProxy(Operation innerOperation)
    {
        if (innerOperation == null)
            throw new ArgumentNullException("innerOperation");
        m_innerOperation = innerOperation;
    }


    public override double Ammount
    {
        get { return m_innerOperation.Ammount; }
        set { m_innerOperation.Ammount = value; }
    }

    public override DateTime PerformedOn
    {
        get { return m_innerOperation.PerformedOn; }
        set { m_innerOperation.PerformedOn = value; }
    }
}

正如您所看到的 - 整个层次结构只有一个代理类。为什么?因为您应该以不依赖于具体类型的方式编写代码 - 仅依赖于提供的抽象。该代理可以及时推迟实体加载 - 也许您根本不会使用它?也许您只会使用 1000 个实体中的 2 个?那为什么要加载它们呢?

因此 NHibernate 使用像上面这样的代理(不过更复杂)来推迟实体加载。它可以为每个子类型创建 1 个代理,但它会破坏延迟加载的整个目的。如果您仔细观察 NHibernate 如何存储子类,您会发现,为了确定实体是什么类型,您必须加载它。所以不可能有具体的代理——你只能有最抽象的,OperationProxy。

尽管 ifs 的解决方案很丑陋 - 但它是一个解决方案。现在,当您为问题引入代理时,它不再起作用。因此,我们只能使用多态方法,这是不可接受的,因为将 UI 责任混合到模型中。让我们解决这个问题。

依赖倒置和访问者模式

首先,让我们看看使用虚拟方法的解决方案是什么样子的(仅添加了代码):

public abstract class Operation
{
    public abstract void PrintInformation();
}

public class OutgoingTransfer : Operation
{
    public override void PrintInformation()
    {
        Console.WriteLine("{0}: {1}, target account: {2}",
                      PerformedOn, Ammount, TargetAccount);
    }
}

public class AtmWithdrawal : Operation
{
    public override void PrintInformation()
    {
        Console.WriteLine("{0}: {1}, atm's address: {2}",
                          PerformedOn, Ammount, AtmAddress);
    }
}

public class OperationProxy : Operation
{
    public override void PrintInformation()
    {
        m_innerOperation.PrintInformation();
    }
}

现在,当你打电话时:

Operation[] operations = { transfer, withdrawal, proxy };
foreach (var operation in operations)
{
    operation.PrintInformation();
}

一切都充满魅力。

为了消除模型中的 UI 依赖,让我们创建一个界面:

public interface IOperationVisitor
{
    void Visit(AtmWithdrawal operation);
    void Visit(OutgoingTransfer operation);
}

让我们修改模型以依赖于这个接口:

现在创建一个实现 - ConsoleOutputOperationVisitor (我已删除 PrintInformation 方法):

public abstract class Operation
{
    public abstract void Accept(IOperationVisitor visitor);
}

public class OutgoingTransfer : Operation
{
    public override void Accept(IOperationVisitor visitor)
    {
        visitor.Visit(this);
    }
}

public class AtmWithdrawal : Operation
{
    public override void Accept(IOperationVisitor visitor)
    {
        visitor.Visit(this);
    }
}

public class OperationProxy : Operation
{
    public override void Accept(IOperationVisitor visitor)
    {
        m_innerOperation.Accept(visitor);
    }
}

这里会发生什么?当您在操作上调用 Accept 并传递访问者时,将调用 Accept 的实现,其中将调用 Visit 方法的适当重载(编译器可以确定“this”的类型)。因此,您可以结合虚拟方法和重载的“力量”来调用适当的方法。正如您所看到的 - 现在 UI 参考在这里,模型仅依赖于一个接口,该接口可以包含在模型层中。

现在,为了使其正常工作,需要实现该接口:

 public class ConsoleOutputOperationVisitor : IOperationVisitor
 {
    #region IOperationVisitor Members
    public void Visit(AtmWithdrawal operation)
    {
        Console.WriteLine("{0}: {1}, atm's address: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.AtmAddress);
    }

    public void Visit(OutgoingTransfer operation)
    {
        Console.WriteLine("{0}: {1}, target account: {2}",
                          operation.PerformedOn, operation.Ammount,
                          operation.TargetAccount);
    }

    #endregion
}

和代码:

Operation[] operations = { transfer, withdrawal, proxy };
foreach (var operation in operations)
{
    operation.Accept(visitor);
}

我很清楚这不是一个完美的解决方案。添加新类型时,您仍然需要修改界面和访问者。但是您可以进行编译时检查,并且永远不会错过任何内容。使用此方法确实很难实现的一件事是获得可插入的子类型 - 但无论如何我不相信这是一个有效的场景。您还必须修改此模式以满足您在具体场景中的需求,但我会将其留给您。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

强制惰性实体加载真实实例 的相关文章

  • NHibernate 具有多个数据库和事务

    我们在理解如何最好地使用 NHibernate 时遇到了一些问题 我们通常拥有相对大量 就表数量而言 的 SQL Server 数据库 而不是一个包含大量对象的数据库 我们正在研究处理多个会话工厂的各种选项 并且可能已经控制住了这一点 但是
  • 如何使用 NHibernate 标准来做到这一点

    假设我有 2 张桌子 表1 a b 和表2 c a 我需要做这样的事情 但符合 NHibernate 标准 select a b select count from table2 t2 where t1 a t2 a x from tabl
  • 流畅的 Nhibernate AutoMapping 继承和忽略抽象属性

    我有一个已成功映射的继承结构 产品 基础 PdfProduct 继承自Product 和OtherProduct 继承自Product 这些工作正常 我之前用 hbm xml 文件做过类似的事情 在上一个项目中 当我试图找出产品是什么类型时
  • 如何在 NHibernate 查询中仅选择几列?

    我有一个一类到一表的映射 不幸的是 这个表有 110 多个列 查询需要很长时间 尤其是大多数时候我只想查看 我的问题是查询是根据用户想要查看的内容动态生成的 我无法真正使用不同的列创建不同的映射 因为会有大量的组合 我正在使用条件 API
  • NHibernate代理异常

    我是新 NHibernate 我正在编写一个简单的应用程序 其中的客户类包含 id 和名称 并使用 nhibernate 将对象存储到数据库中 但我得到以下信息 未配置 ProxyFactoryFactory 使用可用的 NHibernat
  • NHibernate HQL 相当于 T-SQL 的 TOP 关键字

    NHibernate HQL 相当于 T SQL 的 TOP 关键字是什么 另外 用非 HQL 方式说 给我一门课的前 15 个 是什么意思 在 HQL 中这实际上非常简单 var top15 session CreateQuery fro
  • 对 id 属性使用自定义类型

    我有以下课程 public class UserId public UserId int id some validation public override string ToString public class User public
  • NHibernate:如何从会话缓存中获取实体实例?

    我在工作单元开始时开始会话 并在工作单元结束时关闭 工作单元分布在多种方法中 在一种方法中 我使用加载实体Get方法 所以它位于会话缓存中 实体实例对于方法来说是本地的 因此 当方法范围结束时 实体实例将无法访问 但实体仍在会话缓存中 现在
  • NHibernate:级联保存到子级不会插入

    我有一个像这样的双向关联 public class Parent public int ParentId get set other properties public IEnumerable
  • Haskell 中的内部爆炸模式是否总是强制使用外部构造函数?

    在 Haskell 中 是否存在对于数据类型 LANGUAGE BangPatterns import Control DeepSeq data D D Int 实例 instance NFData D where rnf D 与具有另一个
  • 更改 NHibernate Session.Save 命令超时

    我们有几个长时间运行的后端进程 其运行时间超过默认的 30 秒 我们的NHibernate版本是2 0 1 4000 Spring NET是1 2 0 20313 NHibernate 通过 Spring NET 进行配置
  • 无法转换类型为“NHibernate.Collection.Generic.PersistentGenericBag”的对象

    public List
  • 我可以将 NHibernate 的 AdoNetTransactionFactory 与分布式事务一起使用吗?

    我正在处理一个与 NHibernate 和 WCF 服务中的分布式事务相关的奇怪问题 看NHibernate 和分布式事务导致 服务器无法恢复事务 的死锁 https stackoverflow com questions 8581956
  • 最好的 NHibernate 二级缓存提供程序是什么?

    我见过有很多这样的 NCache Velocity 等等 但我还没有找到比较它们的表格 考虑以下标准什么是最好的 容易明白 最近正在维护中 是免费的或者有足够好的免费版本 Works 我无法说出什么是最好的或最差的 但我会分享我使用 NCa
  • MSTest 和 NHibernate

    有谁有让 MSTest 将 hibernate cfg xml 正确复制到输出目录的经验吗 我的所有 MSTest 都因找不到 hibernate cfg xml 错误而失败 我将其设置为 始终复制 但我的 MBUnit 测试通过了 您可以
  • NHibernate 将多个表映射到一个类

    在我的旧数据库中 我遇到这样的情况 TableA id A PK cod A TableB id B PK cod B id A FK TableC id C PK cod C id B FK 由于多种原因 我需要将这些表映射到单个类中 本
  • NHibernate Session.Flush & Evict 与 Clear

    在一个测试中 我想要持久化一个对象 然后通过从数据库 而不是会话 获取它来证明它是持久化的 我注意到以下内容没有区别 save it session Clear fetch it or save it session Flush sessi
  • 如何在 nHibernate 中添加 NOLOCK?

    使用nhibernate时如何添加NOLOCK 条件查询 SetLockMode LockMode None http nhforge org doc nh en index html configuration optional dial
  • NHibernate Session.Evict()

    我无法访问我面前的代码 所以我只是想知道是否有人可以帮助我解决 Session Evict 问题 假设我有一个包含地址子集合的 Person 对象 我从会话中填充 Person 对象并延迟加载 Addresses 集合 然后 我调用 Ses
  • 如何获取常量内存中的统计数据

    我有一个函数 它会创建一些随机的数值结果 我知道 结果将是 a 小 a b 约 50 范围内的整数a b 我想创建一个执行上述函数 1000000 次的函数 并计算每个结果出现的频率 该函数使用随机生成器来生成结果 问题是 我不知道如何在常

随机推荐

  • 查找包中的所有函数(包括私有函数)

    I know ls package grid and find funs package grid in mvbutils但显然他们都找不到只能在内部或通过以下方式访问的非导出函数和方法 or getAnywhere 我必须在以下位置获取文
  • 我可以在 VS 中构建这个项目,但不能使用 msbuild

    我正在尝试在 nuget 应用程序中构建一个 ms 测试项目 我的项目结构是这样的 MyPackage gt MyPackage gt MyPackageTest 如果我打开 VS 并右键单击并构建 MyPackageTest 它可以工作
  • 找不到与给定名称“@style/Theme.Holo.Light.DarkActionBar”匹配的资源

    平台 4 3 API级别 18 AndroidManifest xml
  • 单击 IE 中图像上方的 div

    我有一个图像 上面可能有一些 div 指定该图像中的某些选择 这些 div 应该是可点击的 像这样的东西 divOuter width 500px height 500px border 2px solid 0000FF position
  • Cheerio / jquery 选择器:如何获取标签 a 中的文本?

    我正在尝试访问网站上的链接 该网站看起来像第一个代码示例 链接位于不同的 div 容器中 div div class class1 div class item class1 a href http www example com 1 ex
  • Spring Boot 的外部库文件夹

    我想知道如何为我的 Spring Boot 应用程序外部化所有 jdbc 驱动程序 我不想在构建应用程序后将 jdbc 驱动程序插入到我的 fat jar 中 有没有办法设置 java vm 参数 通知 jar 执行应包含哪个外部文件夹 或
  • 在 MSBuild 参数中设置应用程序名称

    我正在使用这个从命令行部署我的网络应用程序 msbuild WebApplication1 csproj t Package p configuration release 它工作正常 但部署的应用程序与项目设置页面中使用的名称相同 我想使
  • 在 R 中创建滚动列表

    给定一个向量 数据框的列 我想创建一个滚动向量 l 0 10 将返回 窗口为 3 0 1 2 1 2 3 2 3 4 3 4 5 1 滚动应用 r是一个 9x3 矩阵 其每一行都是所要求的列表元素之一 并且split将其转换为向量列表 尽管
  • Godot:调用外部方法

    经过大量谷歌搜索 我仍然不明白什么可能是一个简单的解决方案 场景 主要 包含一个 TileMap Grid 并附有一个脚本 Grid gd 场景 玩家 包含一个 KinematicBody2D Player 及其附加脚本 Player gd
  • 将嵌入资源保存到文件系统

    我使用此代码加载嵌入资源 位图图像 HRSRC hResInfo FindResource hInstance MAKEINTRESOURCE resourceId RT BITMAP HGLOBAL hRes LoadResource h
  • 在php联系表单中捕获用户的IP地址

    我正在尝试从 php 联系表单获取用户 IP 地址 我有以下代码 但我想知道以这种方式使用 clean string 向自己发送 IP 地址是否有效
  • Django:禁止(未设置 CSRF cookie。)

    我遇到了 CSRF cookie 未设置 的问题 我所需要的只是外部计费平台将更新发送到 django 服务器 在本地它可以与 Postman 一起使用 但在演示服务器中它不起作用 Code views py from django vie
  • 更改应用程序和任务栏图标 - Python/Tkinter

    我一直在使用 Tkinter 编写一个非常简单的 Python 脚本 我正在使用Python 2 7 3 如何更改其应用程序图标 资源管理器窗口中显示的 文件 图标和开始 所有程序例如 窗口 不是 文件类型 图标 也不是应用程序图标的主窗口
  • VirtualTreeView 的 Firemonkey 版本

    有谁知道是否有流行的 Firemonkey 版本在准备 另外 是否有人收集了一些将自定义控件移植到 Firemonkey 的经验 并且可以估计将虚拟树视图移植到 Firemonkey 需要多少工作 我们需要这个控件 并且只有当我们能让这个控
  • log4j 打印错误的字符

    有人报告我给他的使用 log4j 的程序无法正确打印字符 他告诉我 在文件中打印为 例如 Vid o 变成 Vid o 这可能是一些编码问题 但我喜欢重现问题以证明它已修复 我无法找到有关该主题的良好 且简短 文档 因此 是什么导致了这个问
  • 首次登录后 React 不重定向。但在强制重定向并再次执行登录操作后重定向

    我有一个登录表单 单击后会调用以下函数 总结一下 从 api 获取响应 如果有效 则设置 cookie 然后使用this props history push handleSubmit event event preventDefault
  • Apache 或其他一些 CLIENT JAVA 实现是否支持 HTTP/2?

    我正在寻找可以连接到基于 HTTP 2 的服务器的 java 客户端 该服务器已经支持 HTTP 2 API 我没有看到最流行的 Apache Http 客户端https hc apache org https hc apache org
  • 适用于移动设备的 Javascript onClick

    我正在开发一个导航子菜单 我需要可以通过移动和平板电脑设备访问它 我知道使用 onClick return true 可以解决问题 但是 我还需要在用户单击列表项时关闭列表项 基本上我需要它来切换子菜单 如果我添加这一行简单的 Javasc
  • 在 Kotlin 中使用线程还是协程更好?

    我正在从应用程序发送邮件 由于邮件发送需要时间并阻塞主线程 因此我正在创建一个新线程并将邮件发送任务移交给新线程 对于发送邮件的大量并发请求 我必须创建大量线程 但创建线程似乎很慢 我的问题是 如果我使用 Kotlin 的协程 它是否能提供
  • 强制惰性实体加载真实实例

    我有一个惰性实体的代理 它是通过加载子实体在会话中创建的 对父实体的后续提取仅返回 NH 代理 我需要实际实例来检查类型 实体已加入子类 我一定错过了一些东西 但我找不到办法做到这一点 Session Refresh proxy 似乎没有帮