3

我第一次使用 fluent nhibernate 来逐步淘汰我现有的数据库访问层以访问远程 MySql 服务器。

客户端大多是虚拟化的,可以在任何时间点进入睡眠/休眠状态,持续任何时间长度(几秒到几天)。即使它们通常与无法保证的服务器时间同步,这意味着我无法使用DateTime为某些字段提供的客户端。

如何让 nhibernate 使用服务器时间设置/更新某些字段(最好通过调用UTC_TIMESTAMP()函数)?

目的:

public class MyObject{
  public virtual UInt64 Id { get; set; }
  public virtual String Status{ get; set; }
  public virtual String SomeData{ get; set; }

  //Set only once when the Object is inserted
  public virtual DateTime TimeCreated { get; set; }
  //Set every time the object is updated
  public virtual DateTime TimeLastUpdate { get; set; }
  //Set every time the 'Status' column is updated
  public virtual DateTime TimeStatusLastChanged { get; set; }
  //This is a user provides standard datetime field
  public virtual DateTime SomeUserSpecifiedTime { get; set; }
}

映射:

Id(x => x.Id)
  .GeneratedBy.Native();
Map(x => x.Status);
Map(x => x.SomeData);
Map(x => x.SomeUserSpecifiedTime);
//? -->
Map(x => x.TimeCreated)
  .Not.Update();
Map(x => x.TimeLastUpdate);
Map(x => x.TimeStatusLastChanged);
//<-- ?

我希望这三个Time*字段在设置/更新时使用该UTC_TIMESTAMP()函数,以便插入服务器端日期/时间。该DateTime SomeUserSpecifiedTime字段是标准映射字段。

到目前为止,我的数据库访问类包含以这种方式创建查询的逻辑。我可以使用映射创建数据库触发器.Generated.Insert/Always,但我希望有一种 nhibernate/code-only 方式来做到这一点。

我发现的一种解决方案是从服务器获取时间并将其提供给插入/更新,但这已经过时了,因为获取和插入/更新之间的时间可能很长。

4

2 回答 2

1

我决定使用触发器来解决这个问题,这不是最佳的,但现在可以使用。不幸的是nhibernate没有触发器的概念,所以你必须将它们声明为辅助数据库对象,这会导致一些问题

  • SchemaValidator 和 SchemaUpdate 不会选择 auxDbObjects,因此不会更新/创建它们。SchemaExport 将创建它们,但会这样做两次,这会导致更多的代码重复。
  • 需要为每个触发器创建和注册一个实例,比较麻烦。

它可能看起来像这样

public class MyTrigger : IAuxiliaryDatabaseObject{

  public string SqlCreateString(Dialect dialect, IMapping p, string defaultCatalog, string defaultSchema){
    //The drop is important, because the shema export calls and executes this twice.
    return @"
DROP TRIGGER IF EXISTS myTrigger;

CREATE TRIGGER myTrigger BEFORE INSERT ON myObjectTable FOR EACH ROW 
BEGIN
set new.TimeAdded = UTC_TIMESTAMP();
END";
    }

    public string SqlDropString(Dialect dialect, string defaultCatalog, string defaultSchema){
    return @"DROP TRIGGER IF EXISTS myTrigger";
    }

  public void AddDialectScope(string dialectName){
    throw new NotImplementedException();
    }

  public bool AppliesToDialect(Dialect dialect){
    return true;
    }

  public void SetParameterValues(IDictionary<string, string> parameters){
    throw new NotImplementedException();
    }
  }

在配置中每个触发器/辅助。必须单独添加数据库对象:

Fluently.Configure()
  .Database(...)
  .ExposeConfiguration(conf =>
    {
    conf.AddAuxiliaryDatabaseObject(new MyTrigger());
    });

映射

Map(x => x.TimeCreated)
  .Not.Update()
  .CustomType("UtcDateTime")
  .ReadOnly()
  .Generated.Insert()   //.Generated.Always() for columns that track lastModification
  ;

不幸的是,自定义用户类型或拦截器不起作用,因此这似乎是目前实现这一目标的唯一可能方法。

于 2012-09-18T13:26:06.210 回答
0

这应该可以解决问题

Map(x => x.TimeStatusLastChanged)
             .Not.Nullable()
             .Default("UTC_TIMESTAMP()");
于 2012-09-11T20:36:03.950 回答