是否可以直接在 NHibernate 中设置引用的外键?
Posted
技术标签:
【中文标题】是否可以直接在 NHibernate 中设置引用的外键?【英文标题】:Is it possible to set the foreign key of a reference directly in NHibernate? 【发布时间】:2011-01-23 12:22:32 【问题描述】:有一个项目,它从特定系统以 XML 文件的形式收集数据(这些以 Web 请求的形式出现),将其转换为实体模型,并将其填充到数据库中以进行报告。
项目使用以下软件(与本题相关):
C# 4.0 / .Net 4 NHibernate 3.0(NuGet 中的最新版本) Fluent NHibernate(与 NH 3 一起使用的一种)假设我有一个这样的实体(简化):
public class Incident : Entity
public virtual string OriginatorSite get; set;
public virtual string DestinationSite get; set;
public virtual IncidentType IncidentType get; set;
public virtual TimeSpan TotalWaitTime get; set;
public virtual TimeSpan TotalActionTime get; set;
public virtual DateTime RegisterTime get; set;
public virtual DateTime CloseTime get; set;
public virtual DateDimension DateDimension get; set;
这是这样映射的:
public class IncidentMap : ClassMap<Incident>
public IncidentMap()
Id(c => c.Id);
Map(c => c.OriginatorSite);
Map(c => c.DestinationSite);
Map(c => c.IncidentType).CustomType<IncidentType>();
Map(c => c.TotalWaitTime);
Map(c => c.TotalActionTime);
Map(c => c.RegisterTime);
Map(c => c.CloseTime);
References<DateDimension>(c => c.DateDimension);
(Id来自Entity基类)
由于处理此类事情的人可能已经阅读了代码,因此我正在尝试在这里进行一些维度建模。我是这个主题的新手,很可能做错了(tm),但我希望至少能从这种方式中获得一些好处;每个事件都引用一个如下所示的 DateDimension 对象:
public class DateDimension : Entity
public virtual int DayOfMonth get; set;
public virtual int Weekday get; set;
public virtual string WeekdayName get; set;
public virtual int Week get; set;
public virtual int MonthPart get; set;
public virtual int Month get; set;
public virtual string MonthName get; set;
public virtual int Quarter get; set;
public virtual int Year get; set;
DateDimension 表已填充 - 我的系统从未真正在此处创建任何记录。这些是在实际使用之前生成的——系统中每个相关日期都有一行。这是我系统的功能之一。它承诺每个可能的日期都会有一行。如果缺少一个,那将是一场灾难性的失败。
您可能会问,为什么要这样做,如果您是维度建模的新手,就像我在两天前写这篇文章时一样。
好吧,每个日期我都会有很多事件记录。因此,DateDimension 表将比 Incident 表小很多,并允许我使用 NHibernate LINQ 来完成原本会很困难的事情。比如这样的:
var IncidentsPerWeekday = _incidentRepository
.GroupBy(i => i.DateDimension.Weekday)
.Select(g => new Tuple<int,int>(g.Key, g.Count()))
.ToList();
给我一份群组列表,告诉我事件在工作日中是如何划分的。当然,这里可以添加许多不同的维度,让我可以围绕一系列不同的参数来透视报告并创建有趣的报告。
但是,有一个小烦恼,我们终于来到了真正的问题。
DateDimension 有一个主键,它基本上是它以特定格式表示的日期。 2011 年 4 月 30 日,它看起来像这样:
20110430
这是通过在 NHibernate 中使用自定义 IIdentifierGenerator 来完成的。由于每个日期我们只有一条记录,因此在我看来这是一种相当干净的方式。
此外,这将是一种让新事件实体知道对其相关 DateDimension 的引用的外键的快速方法。一些工厂会知道我们从 Incident.RegisterTime DateTime 中提取此密钥,然后将其填充到 DateDimension_id 列中。
但是,这似乎是我无法找到的方法。 Incident.DateDimension 理所当然地需要实体引用。这意味着我必须从数据库中加载它(对吗?)。在某些导入场景中,这可能会太慢,因为我需要在尽可能短的时间内将大量事件实体插入数据库。
当然,对于这个特定的示例,我可以通过在每次插入实体时执行一些自定义 SQL 来执行此操作,并允许 NULL 引用。这并不理想,但它可以工作。
但是,有没有办法直接指定 DateDimension 引用的外键,而不是使用对真实存储引用对象的引用来设置它?
那肯定会让我头疼不已!
提前感谢您的任何见解!
【问题讨论】:
【参考方案1】:您可以使用 ISession.Load 方法执行此操作。
myIncident.DateDimension = mySession.Load("20110430");
这将为 DateDimension 创建一个代理,从而避免数据库访问。顺便说一句,如果您访问代理上的密钥以外的任何属性,它将被加载。
【讨论】:
根据 NHibernate Profiler,这成功了!谢谢!【参考方案2】:不是 100% 肯定这会起作用,但您可以尝试将引用设置为具有适当 Id 的 DateDimension 类的新实例。这假设您在 Incident 和 DateDimension 之间没有任何级联,我猜这是一个正确的假设,因为 DateDimensions 在您的数据模型中可能是不可变的。
或者,您可以在映射中公开 FK 列并直接设置它。
【讨论】:
您好,感谢您的反馈!听起来这会奏效,我投了赞成票,但我会选择 Jamie Ide 提供的解决方案,因为这对我来说似乎更干净。不过谢谢! @Rune 绝对是,我完全忘记了 Load。 Ayende 的场景与此处概述的完全相同 ayende.com/Blog/archive/2009/04/30/… 感谢您的链接,我记得现在读到了。Ayende 的文章是在 4 月 30 日发布的,这也是我的问题中使用的示例……我之所以选择它是因为它是我的生日?可能不会,但无论如何我都会排队播放暮光之城的音乐。再次感谢! :)以上是关于是否可以直接在 NHibernate 中设置引用的外键?的主要内容,如果未能解决你的问题,请参考以下文章
列错误中的冒号 ':' - 并非所有命名参数都已在 Nhibernate 中设置?
在一个类中设置一个函数,该函数将以一种可以在未来函数中引用的方式读取 csv 数据