NHibernate - 获取引用的列值

Posted

技术标签:

【中文标题】NHibernate - 获取引用的列值【英文标题】:NHibernate - Fetch referenced column value 【发布时间】:2012-09-10 10:19:03 【问题描述】:

我有这个 NHibernate 模型:

public class RootTable

    public virtual string Name  get; set; 
    public virtual string Description  get; set; 
    public virtual DateTime? Start  get; set; 
    public virtual DateTime? Finish  get; set; 
    public virtual IList<Leaf1> ChildCollection1  get; set; 


public class Leaf1

    public virtual int ID  get; set; 
    public virtual string Info1  get; set; 
    public virtual string Info2  get; set; 
    public virtual RootTable Parent  get; set; 


public class RootMapping : ClassMap<RootTable>

    public RootMapping()
    
        Table("RootTable");
        Id(c => c.Name);
        Map(c => c.Description, "Desc").Length(20);
        Map(c => c.Start).Length(20);
        Map(c => c.Finish).Length(20);
        HasMany(c => c.ChildCollection1)
            .Cascade.All()
            .LazyLoad()
            .Inverse();
    


public class Leaf1Mapping : ClassMap<Leaf1>

    public Leaf1Mapping()
    
        Table("LeafTable1");
        Id(c => c.ID, "RowID").GeneratedBy.Identity();
        Map(c => c.Info1).Length(20);
        Map(c => c.Info2).Length(20);
        References(c => c.Parent).Column("Parent").LazyLoad();
    

我想要做的是访问 Leaf1 中引用列的 ,而不延迟加载 RootTable。

换句话说,我有这个:

this.LogMessage("Loading leaves...");
var allleafs = session.CreateCriteria(typeof(Leaf1)).List<Leaf1>();
this.LogMessage("Loaded leaves...");
var leaf = allleafs[0];

this.LogMessage("Leaf metadata:");
// This causes a lazy-load of the RootTable object.
this.LogMessage("Leaf parent is " + leaf.Parent);

现在我真正想要的是“父”的 ,因为它存储在底层数据库中 - 我不关心父对象,我不想加载它,所有我想做得到原始价值。我无法访问包含该值的字段(即leaf.Parent.Name),因为我希望它以通用方式工作...

[背景] 最终,这是插入到使用 NHibernate 拦截器的审计框架中,因此这需要以通用方式工作,以便对于传入的任何对象,我都可以报告更改的值。子节点完全有可能发生变化而根节点没有变化,因此当调用拦截器的 OnFlushDirty() 时,我确实希望拦截器导致其他对象的延迟加载。

我知道我可以直接引用父属性(例如,我可以说“leaf.Parent.Name”),这将使我得到没有延迟加载的值,但似乎没有快速的方法来确定“名称”是我要返回的关键属性。

[编辑添加...] 当我得到一个空引用异常时,走树似乎不起作用:

var theType = leaf.Parent.GetType();

// This line returns a NULL due to the proxy class.
var metadata = factory.GetClassMetadata(theType);
var idProp = metadata.IdentifierPropertyName;

var prop = theType.GetProperty(idProp);
var val = prop.GetValue(leaf.Parent, null);

this.LogMessage("Leaf parent is " + val);

现在,theType 以RootTableProxy 返回,所以它只是一个占位符,因为没有加载主类。这意味着metadata 为空,因为没有类元数据,因此idProp 失败并出现空引用异常。

所以我实际上看不到如何在没有延迟加载的情况下获取引用的列值:这肯定不对吗?

编辑添加(更多!) 我认为使用 session.GetIdentifier() 找到了一个简单的解决方案。然而,这似乎并非在所有情况下都有效:在对某些对象调用 session.GetIdentifier(state[i]) 的拦截器中导致异常指出该对象不是当前会话的一部分,因此仍在寻找更可靠的解决方案诉诸反思。欢迎任何想法...

【问题讨论】:

【参考方案1】:

向您的 Leaf1 类添加另一个属性怎么样。

例如

public class Leaf1

    public virtual int ID  get; set; 
    public virtual string Info1  get; set; 
    public virtual string Info2  get; set; 
    public virtual RootTable Parent  get; set; 
    public virtual int ParentId  get; set; 

然后map是只读的

public class Leaf1Mapping : ClassMap<Leaf1>

    public Leaf1Mapping()
    
        Table("LeafTable1");
        Id(c => c.ID, "RowID").GeneratedBy.Identity();
        Map(c => c.Info1).Length(20);
        Map(c => c.Info2).Length(20);
        References(c => c.Parent).Column("Parent").LazyLoad();
        Map(c => c.ParentId).Column("Parent").ReadOnly();
    

【讨论】:

这是一个想法——我们有一个很大的域模型,所以需要大量的代码来添加这个属性。我们也许可以通过仅对那些需要它的属性执行此操作来限制它 - 将仔细研究。不过对于我认为 NH 应该免费给我的东西来说,这似乎有点 hacky... 嗯。【参考方案2】:

您可以让您的实体实现接口(会更简洁),但如果您想获取代理的实际类型,请使用 NHibernateUtil.GetClass(proxy) 它将返回您正在寻找的基础类型

【讨论】:

【参考方案3】:

发现ISession 有一个方法GetIdentifier,它返回属性的缓存值。以我原始帖子中的示例为例,代码更改为:

this.LogMessage("Loading leaves...");
var allleafs = session.CreateCriteria(typeof(Leaf1)).List<Leaf1>();
this.LogMessage("Loaded leaves...");
var leaf = allleafs[0];

this.LogMessage("Leaf metadata:");
this.LogMessage("Leaf parent is " + session.GetIdentifier(leaf.Parent));

现在在测试中,这似乎运行良好,并且可以在 currentState 和 previousState 对象的拦截器中从 OnFlushDirty 调用。

我一直在尝试查找有关 GetIdentifier 的更多信息,但遗憾的是缺少 NH 文档,而且似乎很少(如果有的话)博客文章提到这一点。如果有人知道我需要知道的与此方法相关的任何警告、问题或“有趣”,我会非常乐意听到它们...

[编辑添加] ...事实证明,如果对象不是代理对象(!),这将不起作用。仍在寻找一种可靠的方法来做到这一点,它不涉及反射或用属性装饰对象来识别字段。

【讨论】:

以上是关于NHibernate - 获取引用的列值的主要内容,如果未能解决你的问题,请参考以下文章

如何引用 BigQuery SQL 中先前行的列值,以便执行操作或计算?

NHibernate 效率

NHibernate体系结构

如何获取以逗号分隔的列值

NHibernate 似乎没有批量插入 PostgreSQL

DataGrid 获取选定行的列值