【问题标题】:How do you map a DateTime property to 2 varchar columns in the database with NHibernate (Fluent)?如何使用 NHibernate (Fluent) 将 DateTime 属性映射到数据库中的 2 个 varchar 列?
【发布时间】:2010-03-26 21:35:09
【问题描述】:

我正在处理一个遗留数据库,该数据库在某些表中具有日期和时间字段作为 char(8) 列(格式分别为 yyyyMMdd 和 HH:mm:ss)。如何将 2 个字符列映射到单个 .NET DateTime 属性?我尝试了以下方法,但我当然会收到“无法访问 setter”错误,因为 DateTime Date 和 TimeOfDay 属性是只读的:

public class SweetPocoMannaFromHeaven
{    
    public virtual DateTime? FileCreationDateTime { get; set; }
}

.

mapping.Component<DateTime?>(x => x.FileCreationDateTime,
            dt =>
            {
                dt.Map(x => x.Value.Date,
                    "file_creation_date");
                dt.Map(x => x.Value.TimeOfDay,
                    "file_creation_time");
            });

我也尝试为 DateTime 定义一个 IUserType,但我无法弄清楚。我已经做了很多谷歌搜索来寻找答案,但我仍然无法弄清楚。处理这个 stupid 遗留数据库约定的最佳选择是什么?一个代码示例会很有帮助,因为关于其中一些更晦涩的场景的文档并不多。

【问题讨论】:

    标签: nhibernate fluent-nhibernate nhibernate-mapping legacy iusertype


    【解决方案1】:

    您需要一个 ICompositeUserType 来处理多个列。您需要加强错误检查、解析格式等,但这里是您的起点。

    HTH,
    浆果

    public class LegacyDateUserType : ICompositeUserType
    {
    
        public new bool Equals(object x, object y)
        {
            if (x == null || y == null) return false;
            return ReferenceEquals(x, y) || x.Equals(y);
        }
    
        public int GetHashCode(object x) {
            return x == null ? typeof (DateTime).GetHashCode() + 473 : x.GetHashCode();
        }
    
        public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
        {
            if (dr == null) return null;
    
            var datePortion = NHibernateUtil.String.NullSafeGet(dr, names[0], session, owner) as string;
            var timePortion = NHibernateUtil.String.NullSafeGet(dr, names[1], session, owner) as string;
    
            var date = DateTime.Parse(datePortion);
            var time = DateTime.Parse(timePortion);
            return date.AddTicks(time.Ticks);
        }
    
        ///<summary>
        /// Write an instance of the mapped class to a prepared statement. Implementors 
        /// should handle possibility of null values. A multi-column type should be written 
        /// to parameters starting from index.
        ///</summary>
        public void NullSafeSet(IDbCommand cmd, object value, int index, ISessionImplementor session) {
            if (value == null) {
                // whatever
            }
            else {
                var date = (DateTime) value;
                var datePortion = date.ToString("your date format");
                NHibernateUtil.String.NullSafeSet(cmd, datePortion, index, session);
                var timePortion = date.ToString("your time format");
                NHibernateUtil.String.NullSafeSet(cmd, timePortion, index + 1, session);
            }
        }
    
        public object GetPropertyValue(object component, int property)
        {
            var date = (DateTime)component;
            return property == 0 ? date.ToString("your date format") : date.ToString("your time format");
        }
    
        public void SetPropertyValue(object component, int property, object value)
        {
            throw new NotSupportedException("DateTime is an immutable object.");
        }
    
        public object DeepCopy(object value) { return value; }
    
        public object Disassemble(object value, ISessionImplementor session) { return value; }
    
        public object Assemble(object cached, ISessionImplementor session, object owner) { return cached; }
    
        public object Replace(object original, object target, ISessionImplementor session, object owner) { return original; }
    
        ///<summary>Get the "property names" that may be used in a query.</summary>
        public string[] PropertyNames { get { return new[] { "DATE_PORTION", "TIME_PORTION" }; } }
    
        ///<summary>Get the corresponding "property types"</summary>
        public IType[] PropertyTypes { get { return new IType[] { NHibernateUtil.String, NHibernateUtil.String }; } }
    
        ///<summary>The class returned by NullSafeGet().</summary>
        public Type ReturnedClass { get { return typeof(DateTime); } }
    
        ///<summary>Are objects of this type mutable?</summary>
        public bool IsMutable { get { return false; } }
    
    }
    

    === 流畅映射(假设自动映射 w/覆盖类)====

     public void Override(AutoMapping<MyClass> m)
     {
         ....
         m.Map(x => x.MyDateTime).CustomType<LegacyDateUserType>();
     }
    

    【讨论】:

    • 这个映射会是什么样子?我创建了一些 IUserType 实现,但总是针对单个属性/列对。我想知道在什么情况下names 数组会有多个元素。
    • 嗨,杰米。您只需要将属性关联到自定义类型;在 FNH 中,它看起来就像我在帖子末尾添加的代码行一样简单。如果自定义类型很普遍,您也可以设置一个约定来处理它。干杯
    • 这看起来很棒!非常感谢!我会在以后上班时尝试一下,并根据我的测试将此设置为答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多