开发者

Is it possible to set the foreign key of a reference directly in NHibernate?

开发者 https://www.devze.com 2023-02-06 19:24 出处:网络
Got a project that collects data as XML files from a particular system (these come in as web requests), converts it to an entity model, and stuffs it in a database for reporting.

Got a project that collects data as XML files from a particular system (these come in as web requests), converts it to an entity model, and stuffs it in a database for reporting.

The project uses the following software (relevant to this question):

  • C# 4.0 / .Net 4
  • NHibernate 3.0 (the latest available in NuGet)
  • Fluent NHibernate (the one that goes along with NH 3)

Say I have an entity like this (simplified):

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; }
}

and this is mapped thusly:

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);
    }
}

(The Id comes from the Entity base class)

As those who deal with such things have probably already read from the code, I am trying to do some dimensional modeling here. I am new to the subject, and in all likelyhood Doing It Wrong (tm), but I am hoping at least to get some benefits out of this way of doing it; Each Incident references a DateDimension object that looks like this:

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; }
}

The DateDimension table is already populated - my system never really creates any records here. These are generated well ahead of actually being used - one row for each relevant date in the system. That is one of the features of my system. It promises one row will be there for each date possible. If one was missing, that would be a catastrophic failure.

Why do this, you might ask, if you're new to dimensional modeling, as I was this two days ago as of writing this.

Well, I will have a LOT of Incident records for each date. So the DateDimension table will be a lot smaller than the Incident table, and allow me to do things with NHibernate LINQ that would otherwise be difficult. For instance something like this:

        var IncidentsPerWeekday = _incidentRepository
            .GroupBy(i => i.DateDimension.Weekday)
            .Select(g => new Tuple<int,int>(g.Key, g.Count()))
            .ToList();

Giving me a list of groups telling me how the incidents divide themselves among the weekdays. There are of course many different dimensions that can be added here, allowing me to pivot reports around a bunch of different parameters and create interesting reports.

However, there is one minor annoyance, and here we finally arrive at the real question.

The DateDimension has a primary key which is basically the date it represents in a specific format. For April 30th 2011, it would look like this:

20110430

This is done by using a custom IIdentifierGenerator in NHibernate. Since we'll only have a single record for each date, this is a fairly clean way to do it in my opinion.

Also, it would be a quick way to let new Incident entities know the foreign key of the reference to their relevant DateDimension. Some factory would know that we extract this key from say the Incident.RegisterTime DateTime, then stuff it in the DateDimension_id column.

However, this is what I can't seem to find a way to do. Incident.DateDimension rightfully demands an entity reference. Which means I have to load it from the database (right?). Which could be too slow in certain import scenarios where I need to insert a LOT of Incident entities into the database in the shortest amount of time possible.

Sure, I could probab开发者_JAVA技巧ly do this for this particular example by executing some custom SQL each time I insert an entity, and allow NULL references. It's not ideal, but it could work.

However, is there a way to specify the foreign key of the DateDimension reference directly instead of setting it with a reference to a real, stored reference object?

That would certainly rid me of a huge headache!

Thanks in advance for any insight!


You can do this using the ISession.Load method.

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

This will create a proxy for DateDimension thus avoiding a database trip. If you access any properties other than the key on the proxy it will be loaded, by the way.


Not 100% positive that this will work, but you can try to set your reference to a new instance of a DateDimension class that has the appropriate Id. This assumes you don't have any cascading between Incident and DateDimension, which I'm guessing is a correct assumption since DateDimensions are likely immutable in your data model.

Alternatively you can expose the FK column in your mapping and directly set that as well.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号