如何在 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<Application>().Where(app => app.Task.Id == "...")
。
是的,我牢记的一个很好的经验法则是,如果它不在映射中,您就无法查询它。
现在,当我使用 QueryOver().Where(app => app.Task.Id == "...")
查询应用程序时,我得到“无法将 'TaskProxy' 类型的对象强制转换为 'System.String'”。我就是赢不了这个。我准备放弃这种关系作为复合键的一部分。以上是关于如何在 NHibernate 中插入后启用延迟加载关系的主要内容,如果未能解决你的问题,请参考以下文章