将不可变结构映射为NHibernate中的组件

时间:2009-12-31 11:13:43

标签: nhibernate struct nhibernate-mapping

我正在测试NHibernate是我公司ORM需求的解决方案。为此,我基于学校制作了一个小型测试模型,为NHibernate提供了一些有用的边缘案例。

我在查找如何将自定义结构映射为实体的组件而不使用IUserType界面时遇到问题。我要强调的是,域类与NHibernate代码在一个单独的程序集中是一个重要的要求,并且域程序集没有对NHibernate的引用。

自定义结构为Time,用于表示以小时和分钟为单位的时间。这是一个非常简单的不可变结构,仅用于说明自定义结构的问题。构造函数接受一个小时和分钟的参数,作为hhmm形式的整数。

public struct Time
{
    public Time(int hoursAndMinutes)
    {
        // Initialize Structure //
    }

    public int Hours { get; private set; }

    public int Minutes { get; private set; }

    public int HoursAndMinutes { get; private set; }
}

此结构用作Lesson类的一个组件,用于存储课程开始的时间:

public class Lesson
{
    public int ID { get; private set; }
    public Teacher Teacher { get; internal set; }
    public DayOfWeek Day { get; set; }
    public Time StartTime { get; set; } // <-- Custom Type
    public int Periods { get; set; }
}

此类直接映射到此表:

CREATE TABLE Lessons
(
    ID INT,
    Subject NVARCHAR(128)
    TeacherID INT,
    Day VARCHAR(9),
    StartTime INT, // <-- Maps to custom type.
    Periods INT
)

我正在寻找一种方法将此结构映射为Lesson类的一个组件,以便NHibernate将读取结构上的属性值(与任何其他组件一样)以获取列的值,但是在将列中的值读入实体时,会通过将列值传递给构造函数来初始化结构的新实例。

如果您有任何建议,那就是超级。如果你想告诉我,如果不使用IUserType就无法完成,那也是一个很好的答案。

3 个答案:

答案 0 :(得分:16)

据我所知,有三种攻击计划。

  • 您可以将组件直接映射到自定义类型的属性中。在此示例中,让NHibernate设置HoursAndMinutes属性,更改该属性的setter中的代码以适当更新HoursMinutes属性,并让构造函数调用this.HoursAndMinutes = hoursAndMinutes;因此,无论是使用构造函数还是使用Hours属性上的setter,都会执行更新MinutesHoursAndMinutes属性的相同代码。如果你没有使用ORM并且知道它会干掉那个属性,你会这样写吗?可能不是。但这不是世界末日,评论可以解释一切。

  • 您编写了IUserTypeICompositeUserType实施方案。实际上,它们恰好适用于此场景,并且您可以灵活地在NullSafeGet()实现中实例化结构,然后在NullSafeSet()实现中随意提取数据。如果你愿意的话,把它放在另一个组件中,比如说MyModel.NHibernateCrap.dll。您的模型/域不需要知道IUserType实现或NHibernate存在 - 这是映射文件要指定的全部内容。

  • 您使用Miki Watts在其答案中描述的基于代码的解决方法。也就是说,NHibernate映射中的组件映射到模型类型中的字段或私有属性,这些属性执行一些魔术手动以将它们转换为应用程序使用的公共属性,反之亦然。 (它与我提供的第一个选项类似;唯一的区别是,在他的场景中,该字段是一种解决方法,它允许遗留数据库泄漏到类实现中,但不会泄漏到应用程序或模型的其他部分。对于小的,孤立的情况,或者如果遗留数据库只是生活中的事实,那么我认为这是完全合理的。)

要直接和现实地回答你的问题, NHibernate不会调用具有参数的构造函数,句点 - 这就是它的工作原理,它会新闻一个对象,然后开始设置和反思它 - 除非你开始用代理做一些奇怪的事情或告诉它使用你的IUserType实现。没有机制可以在映射文件或其他任何内容中说出<constructor><arg>HoursAndMinutes</arg></constructor>之类的内容。不要担心并喜欢炸弹。

由于IUserType是NHibernate提供的用于执行此类操作的机制,因此我不太明白为什么不想使用它。

祝你好运!

答案 1 :(得分:2)

我正在使用基于Priority ERP的旧数据库。在数据库中,时间表示为从纪元开始的整数分钟。 例如,数字0表示01/01/1988 00:00,数字1440表示02/01/1988 00:00,依此类推。

我找到的解决方案如下所示:

    [Field("CURDATE")]
    private int transactionDate = DateTimeHelper.ConvertToInternalValue(DateTime.Today);

    public DateTime TransactionDate
    {
        get { return DateTimeHelper.ConvertToDateValue(transactionDate); }
        set { transactionDate = DateTimeHelper.ConvertToInternalValue(value); }
    }

其中DateTimeHelper上的函数执行从整数分钟到实际DateTime结构的实际转换。

答案 2 :(得分:0)

在它上面做受保护的集和受保护的默认构造函数似乎对我有用

像这样:

public struct Time
{
    protected Time(){}
    public Time(int hoursAndMinutes)
    {
        // Initialize Structure //
    }

    public int Hours { get; protected set; }

    public int Minutes { get; protected set; }

    public int HoursAndMinutes { get; protected set; }
}