【问题标题】:How to enable lazy loading of relations after INSERT in NHibernate如何在 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,而不是从数据库中延迟加载该实体。这可以做到吗?如果可以,怎么做?

【问题讨论】:

    标签: c# nhibernate orm fluent-nhibernate


    【解决方案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'”。我就是赢不了这个。我准备放弃这种关系作为复合键的一部分。
    猜你喜欢
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    相关资源
    最近更新 更多