是否可以直接在NHibernate中设置引用的外键?

时间:2011-01-23 12:22:32

标签: nhibernate fluent-nhibernate dimensional-modeling

有一个项目将数据作为XML文件从特定系统收集(这些文件作为Web请求),将其转换为实体模型,并将其填充到数据库中进行报告。

该项目使用以下软件(与此问题相关):

  • C#4.0 / .Net 4
  • NHibernate 3.0(最新的NuGet版本)
  • 流利的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基类)

由于处理此类事情的人可能已经从代码中读过,我试图在这里做一些维度建模。我是这个主题的新手,并且尽可能地做错了(但是)我希望至少可以通过这种方式获得一些好处;每个Incident都引用一个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引用的外键,而不是通过引用真实的存储引用对象来设置它?

这肯定会让我头疼不已!

提前感谢任何见解!

2 个答案:

答案 0 :(得分:5)

您可以使用ISession.Load方法执行此操作。

myIncident.DateDimension = mySession.Load("20110430");

这将为DateDimension创建一个代理,从而避免数据库跳闸。如果您访问代理上的密钥以外的任何属性,顺便说一下,它将被加载。

答案 1 :(得分:1)

这不会100%肯定,但是您可以尝试设置对具有相应Id的DateDimension类的新实例的引用。这假设您在Incident和DateDimension之间没有任何级联,我猜这是一个正确的假设,因为DateDimensions可能在您的数据模型中是不可变的。

或者,您可以在映射中公开FK列并直接设置它。