[英]Fluent NHibernate - How to do Additional Processing when Converting from SQL to C# type?
我使用Fluent NHibernate作为我的数据访问层,并且每次将SQL中的值映射到DateTime类型时都需要这样做:
var newDateTime = DateTime.SpecifyKind(oldDateTime, DateTimeKind.Local);
在上面的代码中,newDateTime表示所有SQL到C#转换应返回的值,而oldDateTime表示NHibernate的默认转换器自动转换为的值。
除了Fluent NHibernate文档非常暗淡的问题外,我还尝试在互联网上搜索可以使我做到这一点的约定,但是IUserType太繁琐了(而且我无法找到有关如何实现该规则的全面解释。从IUserType派生的方法)和IPropertyConvention似乎仅提供修改C#转换为SQL的方式的方法(没有其他方法,这是我在此方案中需要的方法)。
有人可以指出正确的方向吗? 和/或提供一些高质量的链接以阅读约定? 维基页面均未详细解释任何内容,因此请不要链接这些内容。 谢谢。
NHibernate (不仅是Fluent)支持设置,区分如何处理存储在数据库中的DateTime (无论某些DB支持偏移量,例如datetimeoffset(Transact-SQL) ) 。 参见5.2.2。 基本值类型
从数据库获取:
因此,我们可以显式定义如何处理从表列返回的值,如下所示:
Map(x => x.ExpiryDate).CustomType<UtcDateTimeType>(); // UTC
Map(x => x.MaturityDate).CustomType<LocalDateTimeType>(); // local
因此,一旦从数据库检索到,所有DateTime属性将自动提供正确的Kind
设置:
Assert.IsTrue(entity.ExpiryDate.Kind == DateTimeKind.Utc);
Assert.IsTrue(entity.MaturityDate.Kind == DateTimeKind.Local);
组
让我从NHibernate的日期/时间支持中提供一些摘录:
请注意,当使用错误的DateTimeKind保存/加载DateTime值时,NHibernate不会执行任何转换或引发异常。 (可以争论的是,当NHibernate被要求保存本地DateTime并且该属性被映射为UtcDateTime时,应该引发异常。)由开发人员负责确保适当类型的DateTime位于适当的字段/属性中。
换句话说,来自客户端(在绑定,反序列化等过程中)的Entity DateTime
必须在自定义==我们的代码中正确设置。
在Web API中,从JSON转换DateTime期间的示例。 当JSON为UTC时,数据库设置为lcoal。 我们可以注入此转换器,并确保:
class DateTimeConverter : IsoDateTimeConverter
{
public DateTimeConverter()
{
DateTimeStyles = DateTimeStyles.AdjustToUniversal;
}
public override object ReadJson(JsonReader reader, Type objectType
, object existingValue, JsonSerializer serializer)
{
var result = base.ReadJson(reader, objectType, existingValue, serializer);
var dateTime = result as DateTime?;
if (dateTime.Is() && dateTime.Value.Kind == DateTimeKind.Utc)
{
return dateTime.Value.ToLocalTime();
}
return result;
}
现在我们可以确定:
.CustomType<LocalDateTimeType>()
我们的映射.CustomType<LocalDateTimeType>()
将正确通知应用程序来自DB的数据位于本地区域 我最终使用IUserType实现了新的用户类型。 实际上,我发现了一种新颖的方法,可以最大限度地减少为不可变类型实现新用户类型所需的工作,因为在扩展不可变类型(如DateTime)时,仅需要真正实现少数成员。 这是我的代码:
[Serializable]
public class DateTimeKindLocalType : BaseImmutableUserType<DateTime>
{
public override object NullSafeGet(IDataReader rs, string[] names, object owner)
{
//This is the line that I needed
return DateTime.SpecifyKind((DateTime)NHibernateUtil.DateTime2.NullSafeGet(rs, names), DateTimeKind.Local);
}
public override void NullSafeSet(IDbCommand cmd, object value, int index)
{
NHibernateUtil.DateTime2.NullSafeSet(cmd, value, index);
}
public override SqlType[] SqlTypes
{
get { return new[] {NHibernateUtil.DateTime2.SqlType}; }
}
}
[Serializable]
public class DateTimeKindLocalTypeConvention
: UserTypeConvention<DateTimeKindLocalType>
{
}
[Serializable]
public class DateTimeKindLocalNullableType : BaseImmutableUserType<DateTime?>
{
public override object NullSafeGet(IDataReader rs, string[] names, object owner)
{
if (owner == null)
return null;
return DateTime.SpecifyKind((DateTime)NHibernateUtil.DateTime2.NullSafeGet(rs, names), DateTimeKind.Local);
}
public override void NullSafeSet(IDbCommand cmd, object value, int index)
{
NHibernateUtil.DateTime2.NullSafeSet(cmd, value, index);
}
public override SqlType[] SqlTypes
{
get { return new[] { NHibernateUtil.DateTime2.SqlType }; }
}
}
[Serializable]
public class DateTimeKindLocalNullableTypeConvention
: UserTypeConvention<DateTimeKindLocalNullableType>
{
}
[Serializable]
public abstract class BaseImmutableUserType<T> : IUserType
{
public abstract object NullSafeGet(IDataReader rs, string[] names, object owner);
public abstract void NullSafeSet(IDbCommand cmd, object value, int index);
public abstract SqlType[] SqlTypes { get; }
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, y))
{
return true;
}
if (x == null || y == null)
{
return false;
}
return x.Equals(y);
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object DeepCopy(object value)
{
return value;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
public Type ReturnedType
{
get { return typeof(T); }
}
public bool IsMutable
{
get { return false; }
}
}
基本上,我已经创建了(从其他地方复制,很遗憾,从忘记的地方复制了)一个名为BaseImmutableUserType<T>
的基类,该基类实质上是基于不可变类型创建IUserType
的,但是允许子类扩展NullSafeGet()
和NullSafeSet()
操作(本质上是Get和Set操作以及SqlType,这就是我需要在子类中重写的所有操作)。 我想从长远来看,我将需要更多地覆盖不同的类型,因此我决定对不可变类型使用通用解决方案。 还要注意的一点是,我必须同时执行DateTime
和DateTime?
。 这是我的Fluent配置:
_fnhConfig = Fluently.Configure().Database(
MsSqlConfiguration.MsSql2008.ConnectionString(ConnectionString)
).Mappings(m => m.FluentMappings.AddFromAssemblyOf<DataAccess.NHMG.Fluent.Mapping.DBBufferMap>()
.Conventions.Add(
DefaultLazy.Never()
,DefaultCascade.None()
===> ,new DateTimeKindLocalTypeConvention()
===> ,new DateTimeKindLocalNullableTypeConvention()
));
如果有问题,请告诉我,我将尽快回答。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.