如何在 NHibernate 中插入后启用延迟加载关系

Posted

技术标签:

【中文标题】如何在 NHibernate 中插入后启用延迟加载关系【英文标题】:How to enable lazy loading of relations after INSERT in NHibernate 【发布时间】:2015-04-01 13:46:34 【问题描述】:

我在 NHibernate 中的两个实体之间存在一对多关系:

public class Application

    public string TaskId  get; set;  // Foreign key reference
    public Task Task  get; set;      // The relation/navigation property

db.Web.Applications.Create 方法只是在事务中调用 NHibernate 会话的“SaveOrUpdate”方法。

相关映射:

internal class ApplicationMap : ClassMap<Application>

    public ApplicationMap() : base()
    
        Schema(...);
        Table(...);

        CompositeId()
            .KeyProperty(app => app.UserId, "...")
            .KeyProperty(app => app.TaskId, "task_id")
            .KeyProperty(app => app.TransactionId, "...");

        // Relations

        References(app => app.Task, "task_id")
            .ForeignKey("taskid")
            .Unique()
            .Not.Insert()
            .Cascade.Persist();
    


internal class TaskMap : ClassMap<Task>

    public TaskMap()
    
        Schema(..);
        Table(...);

        Id(task => task.Id, "task_id");

        HasMany(task => task.Applications)
            .KeyColumn("task_id");
    

当我针对真实数据库编写测试以创建新的Application 时,我发现导航属性在插入后没有延迟加载:

var app = new Application(...)

    TaskId = "..."
;

db.Web.Applications.Create(app);
db.SaveChanges();

var actual = db.Web.Applications.Find(app.UserId, app.TaskId, app.TransactionId);

// actual.Task is null

映射按预期工作,但在插入新的Application 对象后,访问Task 属性会返回null,而不是从数据库中延迟加载该实体。这可以做到吗?如果可以,怎么做?

【问题讨论】:

【参考方案1】:

我认为您的问题在于您映射 Application 类的方式及其对应的 Task 对象关系。我认为你应该像这样映射它:

public class Application

    public Task Task  get; set;      // The relation/navigation property


internal class ApplicationMap : ClassMap<Application>

    public ApplicationMap() : base()
    
        Schema(...);
        Table(...);

        CompositeId()
            .KeyProperty(app => app.UserId, "...")
            .KeyReference(app => app.Task, "task_id")
            .KeyProperty(app => app.TransactionId, "...");
    


internal class TaskMap : ClassMap<Task>

    public TaskMap()
    
        Schema(..);
        Table(...);

        Id(task => task.Id, "task_id");

        HasMany(task => task.Applications)
            .KeyColumn("task_id");
    

无需为外键关系映射特定的 id 属性。我总是只映射实体。这种方式要容易得多。那么您上面的插入代码将是这样的:

var app = new Application(...)

    Task = session.Load<Task>(taskId)
;

db.Web.Applications.Create(app);
db.SaveChanges();

var actual = db.Web.Applications.Find(app.UserId, app.TaskId, app.TransactionId);

【讨论】:

问题是我到处都需要任务 ID。我将尝试这种映射技术,并可能为 TaskId 创建一个自定义 getter 属性,而不必一直输入 app.Task.Id @GregBurghardt app.Task.Id vs app.TaskId 真的有那么大的区别吗?我们在代码中的任何地方都有这样的关系。同样这样您就不必重复映射同一事物。 我知道我现在做错了什么。起初我没有选择这个解决方案,因为通常我会尝试直接在对象上调用方法或属性,而不是对象的属性,因为这样会创建将来更容易重构的代码。我认为只是在Application 类中包含public string TaskId get return Task.Id; 委托属性会破坏映射,但我在QueryOver 调用中使用了TaskId 委托属性。 搞砸了 NHibernate。我需要session.QueryOver&lt;Application&gt;().Where(app =&gt; app.Task.Id == "...") 是的,我牢记的一个很好的经验法则是,如果它不在映射中,您就无法查询它。 现在,当我使用 QueryOver().Where(app =&gt; app.Task.Id == "...") 查询应用程序时,我得到“无法将 'TaskProxy' 类型的对象强制转换为 'System.String'”。我就是赢不了这个。我准备放弃这种关系作为复合键的一部分。

以上是关于如何在 NHibernate 中插入后启用延迟加载关系的主要内容,如果未能解决你的问题,请参考以下文章

NHibernate 延迟非常高

nhibernate 可以在没有代理的情况下进行延迟加载吗?

一个简单的延迟加载问题?流畅的 NHibernate

Fluent nHibernate - 不延迟加载

NHibernate之(13):初探立即加载机制

如何在启用分页的 UIScrollView 中延迟加载 100 多个页面?