【问题标题】:Strategy to persist entity's associated objects with NHibernate使用 NHibernate 持久化实体关联对象的策略
【发布时间】:2017-12-13 11:02:11
【问题描述】:

我正在开发一个更大的应用程序,它试图遵循分层架构模式。在 DBAL 上,我使用流畅配置的 NHibernate。数据库对象有时具有如下关联:

public class HeaderDbo
{
    public HeaderDbo()
    {
        Details = new List<DetailDbo>();
    }
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<DetailDbo> Details { get; set; }
}

public class DetailDbo
{
    public virtual int Id { get; set; }
    public virtual string DetailName { get; set; }
    public virtual HeaderDbo Header { get; set; }
    public virtual RelevantObjectDbo RelevantObject { get; set; }
}

public class RelevantObjectDbo
{
    public virtual int Id { get; set; }
    public virtual string RelevantText { get; set; }
}

映射如下:

public class HeaderDboMap : ClassMap<HeaderDbo>
{
    public HeaderDboMap()
    {
        Table("Header");
        Id(x => x.Id).Column("Id");
        Map(x => x.Name);
        HasMany(x => x.Details)
            .Inverse()
            .Cascade.All();
    }
}

public class DetailDboMap : ClassMap<DetailDbo>
{
    public DetailDboMap()
    {
        Id(x => x.Id).Column("Id");
        Table("Detail");
        Map(x => x.DetailName);
        References(x => x.Header).Column("HeaderId");
        References(x => x.RelevantObject).Column("RelevantObjectId")
            .Cascade.SaveUpdate();  //??
    }
}

public class RelevantObjectDboMap : ClassMap<RelevantObjectDbo>
{
    public RelevantObjectDboMap()
    {
        Id(x => x.Id).Column("Id");
        Table("RelevantObject");
        Map(x => x.RelevantText);
    }
}

现在,DBO 映射到的应用程序域实体不一定一对一地反映数据库结构。例如,Header 可能保留为 header,但 Detail 会,例如,由 DetailDbo 和 RelevantObjectDbo 的部分组成。然后应用程序对实体执行它的操作 - 发生 Detail 的一些转换,现在需要持久化。

假设我只影响了 Detail 实体中需要进入 Detail 表的部分,而不会以任何方式影响 RelevantObject 表。考虑模型的方式可能是错误的,但我们还需要对持久化的工作原理进行实际操作。所以,比如说,我只想让 NHibernate 更新 Detail 表而不“接触” RelevantObject 表上的任何内容。实际上,这正是问题所在:我该如何实现?

当然,在现实中,DB 模型更大更复杂,应用程序逻辑也是如此。可能有一部分 BL 根本不处理数据的 RelevantObject 部分,因此即使 DBO 完全从数据库加载,并非所有数据都能进入应用程序模型。但是要将数据持久化回数据库 - 似乎我需要完全水合数据库模型,但这并不总是实用的。那么,我们如何指示 NHibernate “不接触” RelevantObject - 换句话说,不更新 dbo.Detail.RelevantObjectId?

我尝试将不同的 Cascade 选项应用于 DetailDbo.RelevantObject 属性,但如果它保持为空,NHibernate 总是希望将 RelevantObjectId 设置为 NULL - 我想,这是正确的。

我不明白如何在不必通过所有关联加载和保存数据库的一半的情况下对与我的 BL 的“部分”相关的数据进行更改。

谢谢!

【问题讨论】:

  • 我发现如果我在 DetailDbo.RelevantObject 上将 Cascade 设置为 None 并将属性设置为一个“空”对象并设置了正确的 ID,那么更新会按照我想要的方式进行,即参考 dbo.Detail.RelevantObjectId 保持正确设置并且 dbo.RelevantObject 没有被触及。但是,我认为这太笨拙了 - 没有更好的方法吗?
  • 我现在正在研究的一个策略是:在负责从 db 加载实体和保存的存储库中,我们首先读取 DBO - 在存储库“想要”的范围内,更新它实体中的对象并将其保存回来。这也有点笨拙,但我喜欢这种方法,因为“持久化什么”的逻辑包含在 DBAL 中,这似乎很合适,因为实体应该与持久性细节无关。现在正在尝试以某种经济的方式实现这一点......

标签: c# nhibernate architecture fluent-nhibernate multi-layer


【解决方案1】:

您如何执行这些更新? 如果您没有在RelevantObject 上修改任何内容,NHibernate 将不会发送该表的更新。例如:

var header = session.Get<HeaderDbo>(1);
header.Details.First().DetailName = "Whatever";
session.Flush();

不应导致向RelevantObject 表发布更新。

【讨论】:

  • 确实如此。但是,问题是有时我们需要保留一个不一定是 DBO 副本的实体。例如,我们处理的是 BusinessEntity 对象,而不是 HeaderDbo、DetailDbo、RelevantObjectDbo(它们是数据库表的反射)。而且这个实体需要持久化在 dbo.Header 和 dbo.Detail 表中,所以使用 HeaderDbo 和 DetailDbo 来执行这个“保存”是很自然的,但是我们没有足够的关于 BusinessEntity 的信息来重新创建整个 DBO 星座——这就是冲突。
  • 我不确定我是否完全遵循。您不会从 BusinessEntity 上的信息中获取可以访问的实体,然后仅更新这些实体吗?如果您将某些属性更新为它们已经拥有的值,那很好。我想我需要一个更完整的例子来了解你的情况。无论如何,如果避免列级更新对您有帮助,您可以使用“DynamicUpdate();”在您的 NH 映射中,仅对实体的已修改列发布更新。
  • 所以合法的(和使用的?)策略是从数据库加载 DBO 并从 BusinessEntity 更新它们 - 我需要的特定业务操作方式?这就是我最终所做的,实际上,我喜欢这个解决方案。只是想知道我是否错过了更好的东西。
  • 这听起来不错,我通常会这样做,从数据库加载当前状态并根据需要进行修改。然后你需要考虑你想在版本/并发方面做什么(例如,如果其他人同时从其他地方更新了你的 BusinessEntity 相关记录会发生什么,这些更改是否总是一致的?)。
猜你喜欢
  • 1970-01-01
  • 2013-09-17
  • 1970-01-01
  • 2017-04-03
  • 1970-01-01
  • 1970-01-01
  • 2015-09-02
  • 2011-11-23
  • 1970-01-01
相关资源
最近更新 更多