繁体   English   中英

将数据库列映射到常量值,而不需要实体类中的属性

[英]Map database column to constant value without the need for a property in the entity class

是否可以将数据库列映射到常量值而无需实体类中的属性? 这基本上是一种解决方法,用于在数据库中该列上缺少缺省值,并结合NOT NULL约束。 数据库是外部的,无法更改,但我不需要该表中的所有列,因此不希望在我的实体类中具有相应的属性。

我问这个Hibernate JIRA问题中描述的基本相同。

根据Firos的回答,我解决了这个问题。 但是,我不太喜欢使用的语法以及我必须为每个实体的默认值创建一个新类的事实。

我现在的语法如下所示:

mapping.ConstantValue(0).Column(@"client_id");
// or
mapping.ConstantValue(0, @"client_id");

我为它创建了以下扩展方法:

public static PropertyPart
   ConstantValue<TType, TValue>(this ClasslikeMapBase<TType> map, TValue value)
{
    var getter =
        new ConstantValueGetter<TValue>(CreateUniqueMemberName(), value);
    ConstantValueAccessor.RegisterGetter(typeof(TType), getter);

    var propertyInfo =
        new GetterSetterPropertyInfo(typeof(TType), typeof(TValue), 
                                     getter.PropertyName, getter.Method, null);

    var parameter = Expression.Parameter(typeof(TType), "x");
    Expression body = Expression.Property(parameter, propertyInfo);
    body = Expression.Convert(body, , typeof(object));

    var lambda = Expression.Lambda<Func<TType, object>>(body, parameter);

    return map.Map(lambda).Access.Using<ConstantValueAccessor>();
}

public static PropertyPart
   ConstantValue<TType, TValue>(this ClasslikeMapBase<TType> map,
                                TValue value, string column)
{
    return map.ConstantValue(value).Column(column);
}

重要的区别是:

  1. 第一个扩展方法返回一个PropertyPart ,必须与Column方法一起使用,以指定常量值应映射到哪一列。 因此,在执行扩展方法时,列名称是未知的,我们需要自己创建一个。 这是由CreateUniqueMemberName完成的:

     private static string CreateUniqueMemberName() { return "Dummy" + Guid.NewGuid().ToString("N"); } 
  2. 因为您只能将类型指定为访问策略而不是实例,所以我无法创建IPropertyAccessor实现,只允许我在构造函数中传递IGetter实例。 这就是ConstantValueAccessor.RegisterGetter(typeof(TType), getter); 解决了。 ConstantValueAccessor有一个静态的getter集合:

     internal class ConstantValueAccessor : IPropertyAccessor { private static readonly ConcurrentDictionary<Type, SynchronizedCollection<IGetter>> _getters = new ConcurrentDictionary<Type, SynchronizedCollection<IGetter>>(); public static void RegisterGetter(Type type, IGetter getter) { var getters = _getters.GetOrAdd(type, t => new SynchronizedCollection<IGetter>()); getters.Add(getter); } public IGetter GetGetter(Type theClass, string propertyName) { SynchronizedCollection<IGetter> getters; if (!_getters.TryGetValue(theClass, out getters)) return null; return getters.SingleOrDefault(x => x.PropertyName == propertyName); } // ... } 

ConstantValueGetter<T>的实现与提供的链接的实现相同。

因为它并没有实现一个非常有趣GetterSetterPropertyInfo在这里它是。 一个重要的区别是,这个实现对(Fluent)NHibernate没有任何依赖性。

如果您不想在实体类中引入属性,我看到的唯一解决方案是创建自定义属性访问器,它始终返回常量值。 这是可能的实现:

public class ConstantAccessor : IPropertyAccessor
{
    #region IPropertyAccessor Members

    public IGetter GetGetter(Type theClass, string propertyName)
    {
        return new ConstantGetter();
    }

    public ISetter GetSetter(Type theClass, string propertyName)
    {
        return new NoopSetter();
    }

    public bool CanAccessThroughReflectionOptimizer
    {
        get { return false; }
    }

    #endregion

    [Serializable]
    private class ConstantGetter : IGetter
    {
        #region IGetter Members

        public object Get(object target)
        {
            return 0; // Always return constant value
        }

        public Type ReturnType
        {
            get { return typeof(object); }
        }

        public string PropertyName
        {
            get { return null; }
        }

        public MethodInfo Method
        {
            get { return null; }
        }

        public object GetForInsert(object owner, IDictionary mergeMap,
                                               ISessionImplementor session)
        {
            return null;
        }

        #endregion
    }

    [Serializable]
    private class NoopSetter : ISetter
    {
        #region ISetter Members

        public void Set(object target, object value)
        {
        }

        public string PropertyName
        {
            get { return null; }
        }

        public MethodInfo Method
        {
            get { return null; }
        }

        #endregion
    }
}

在这里如何使用它:

<property name="Value"
          access="ConsoleApplication2.ConstantAccessor, ConsoleApplication2"
          column="a_value" type="int" />

属性“值”不需要存在于您的实体中。 这是因为属性“名称”是必需的。

我的实现与hival具有相同的想法,但进一步发展。 基础是IPropertyAccessor的实现

/// <summary>
/// Defaultvalues für nicht (mehr) benötigte Spalten siehe
/// http://elegantcode.com/2009/07/13/using-nhibernate-for-legacy-databases/
/// </summary>
public abstract class DefaultValuesBase : IPropertyAccessor
{
    public abstract IEnumerable<IGetter> DefaultValueGetters { get; }

    public bool CanAccessThroughReflectionOptimizer
    {
        get { return false; }
    }

    public IGetter GetGetter(Type theClass, string propertyName)
    {
        return DefaultValueGetters.SingleOrDefault(getter => getter.PropertyName == propertyName);
    }

    public ISetter GetSetter(Type theClass, string propertyName)
    {
        return new NoopSetter();
    }
}

// taken from the link
[Serializable]
public class DefaultValueGetter<T> : IGetter {...}

// ---- and the most tricky part ----
public static void DefaultValues<T>(this ClasslikeMapBase<T> map, DefaultValuesBase defaults)
{
    DefaultValuesInternal<T>(map.Map, defaults);
}

public static void DefaultValues<T>(this CompositeElementPart<T> map, DefaultValuesBase defaults)
{
    DefaultValuesInternal<T>(map.Map, defaults);
}

private static void DefaultValuesInternal<T>(
    Func<Expression<Func<T, object>>, PropertyPart> mapFunction, DefaultValuesBase defaults)
{
    var noopSetter = new NoopSetter();
    var defaultsType = defaults.GetType();

    foreach (var defaultgetter in defaults.DefaultValueGetters)
    {
        var parameter = Expression.Parameter(typeof(T), "x");
        Expression body = Expression.Property(parameter,
            new GetterSetterPropertyInfo(typeof(T), defaultgetter, noopSetter));

        body = Expression.Convert(body, typeof(object));

        var lambda = Expression.Lambda<Func<T, object>>(body, parameter);

        mapFunction(lambda).Column(defaultgetter.PropertyName).Access.Using(defaultsType);
    }
}

// GetterSetterPropertyInfo inherits PropertyInfo with important part
public override string Name
{
    get { return m_getter.PropertyName; } // propertyName is the column in db
}

// and finally in SomeEntityMap
this.DefaultValues(new SomeEntityDefaults());

public class SomeEntityDefaults : DefaultValuesBase
{
    public override IEnumerable<IGetter> DefaultValueGetters
    {
        get
        {
            return new [] {
                new DefaultValueGetter<int>("someColumn", 1),
                new DefaultValueGetter<string>("somestrColumn", "empty"),
            };
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM