使用Fluent NHibernate映射DateTime类型并设置默认值

4

我知道有很多关于这个话题的帖子,而且我已经阅读了大部分。然而,对我来说有几件事仍然不清楚,并且仍然无法正常工作。

如果我在数据库架构中有一个 DateTime 类型的字段,并且我想要为其分配一个默认值,则应该执行以下操作:

create table [mySchema].[MyTable](
    ObjectGuid uniqueidentifier CONSTRAINT Id PRIMARY KEY,
    SomeTextToStore nvarchar(128) NULL,
    CDate datetime NOT NULL DEFAULT GETDATE(),
    CUser nvarchar(64) DEFAULT CURRENT_USER
);
GO

(不知道是否重要:我正在使用SQL Server Express 2014。Fluent配置适用于SQL Server 2012。)

当从ISQL进行INSERT时,此方法可以正常工作,并插入记录添加的时间戳。

使用Fluent,我会这样写:

领域:

public class MyObject
{
    public virtual Guid Id {get; set}
    public virtual string SomeTextToStore {get; set;}
    public virtual DateTime? CDate {get; set;}
    public virtual string CUser {get; set;}
}

注意: 我将CDate变量设为可空!

下面是一个映射类示例:

class MyObjectMap : ClassMap<MyObject>
{
    public MyObjectMap()
    {
        Table("MySchema.MyTable");
        Id(x => x.Id).GeneratedBy.GuidComb();
        Map(x => x.SomeTextToStore).Length(128).Nullable();
        Map(x => x.CDate).Not.Nullable().Default("getdate()");
        Map(x => x.CUser).Not.Nullable().Default("CURRENT_USER);
    }
}

在程序中(在我的情况下,这是一个可以从多种类型的程序调用的库)我做了如下操作:
public void EnterSomeText()
{
    using (var session = sessionManager.OpenSession())
    {
        using (var transaction = session.BeginTransaction())
        {
            var myObj = new MyObject();
            myObj.SomeTextToStore("bla bla bla");
            session.SaveOrUpdate(myObj);
            transaction.Commit();
        }
        session.Close();
    }
}

这总是导致日期时间溢出异常!(SqlDateTime 溢出。必须介于1/1/1753 12:00:00 AM和12/31/9999 11:59:59 PM之间)

看起来不传递值给文件CDate会导致问题。当我在我的库中添加默认值时,它可以正常工作:

...
myObj.SomeTextToStore("bla bla bla");
myObj.CDate = DateTime.Now;   // <---- Set the date here
session.SaveOrUpdate(myObj);
...

但这并不是真正的解决方案...

问题:

  1. 我做错了什么/遗漏了什么?
  2. 定义默认值的正确策略是什么?在数据库中还是只在代码中?(当开始一个全新的项目时,我更喜欢从C#代码开始做所有的事情,甚至创建和更新架构...)
  3. 关于领域类:创建填充字段默认值的构造函数是否明智?

我对字段CUser这样做是因为我想添加当前用户上下文

public MyObject()
{
   var o = System.Security.Principal.WindowsIdentity.GetCurrent();
   if (o != null)
   {
       CUser = o.Name;
   }  
}

在我的DB访问层库中,我可以在构造函数中填充域类的CDate字段为当前日期,而不是直接在库中进行填充:

public MyObject()
{
   var o = System.Security.Principal.WindowsIdentity.GetCurrent();
   if (o != null)
   {
       CUser = o.Name;
   } 
   CDate = DateTime.Now; 
}

非常感谢您的帮助和评论!

你读过这篇关于默认值的帖子吗?在映射中定义默认值几乎只用于DML生成(在数据库中生成默认约束),而不是为新实例化的实体定义默认值。这应该通过构造函数或字段初始化器来完成,就像任何类一样。 - Frédéric
+1 @Frédéric。这澄清了构造函数的问题。它需要编写一个私有的Init()方法,并从构造函数中调用它,因为不应该在构造函数中为虚拟属性分配值... - ThommyB
2个回答

3
通常我在这种映射中会按照以下步骤进行映射:
DynamicInsert();
DynamicUpdate();

这样,如果您在C#中使用可空类型并且没有给它们设置任何值,那么nhibernate将不会将它们包含在插入或更新语句中。我从来没有真正喜欢nhibernate在任何情况下更新未在代码中更改的列。
此外,当您在映射中指定 .Not.Nullable();.Default("getdate()")时,所有这些仅用于模式生成。 它实际上并未被nhibernate用于进行任何验证或默认操作。 这留给数据库处理。

谢谢你。但有时我需要将值更新为空。我一直无法让.Default(defaultValue)以任何方式工作-唯一的方法是在MsSql数据库中设置它,或者将默认值包含到实体中。 - Andrew Rebane

1
您已在数据库中将此列定义为NOT NULL,并在映射中将其定义为Nullable()。FluentNHibernate正在向数据库发送DbNull,但由于数据库列不可为空,因此被拒绝。
您应该决定逻辑驻留的位置。这是您的代码还是数据库是主控?
例如,在this question中讨论了在FlientNHibernate中设置默认值的问题。
从那里建议的代码是:
Map(x => x.SubmitionDate).Default("getdate()").Not.Nullable();

一般来说,我建议使用NHibernate Profiler(付费工具)查看查询数据库的内容以及为什么会失败。如果你正在使用ORM,这是优化的无价之宝,现在要小心了!


谢谢,Nullable() 是一个打字错误,我不得不稍微简化我的代码,在实际情况中它更加复杂。 - ThommyB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接