简体   繁体   English

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

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

Is it possible to map a database column to a constant value without the need for a property in the entity class? 是否可以将数据库列映射到常量值而无需实体类中的属性? This basically is a workaround for a missing default value on that column in the database in combination with a NOT NULL constrained. 这基本上是一种解决方法,用于在数据库中该列上缺少缺省值,并结合NOT NULL约束。 The database is external and can't be changed but I don't need all of the columns in that table and thus don't want to have corresponding properties in my entity class. 数据库是外部的,无法更改,但我不需要该表中的所有列,因此不希望在我的实体类中具有相应的属性。

I am asking basically the same as described in this Hibernate JIRA issue . 我问这个Hibernate JIRA问题中描述的基本相同。

Based on Firos answer I solved the problem. 根据Firos的回答,我解决了这个问题。 However, I didn't quite like the syntax to be used and the fact that I would have to create a new class for the default values for each entity. 但是,我不太喜欢使用的语法以及我必须为每个实体的默认值创建一个新类的事实。

The syntax I got now looks like this: 我现在的语法如下所示:

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

I created the following extension methods for it: 我为它创建了以下扩展方法:

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);
}

The important differences are: 重要的区别是:

  1. The first of those extension methods returns a PropertyPart and has to be used in conjunction with the Column method to specify which column the constant value should be mapped to. 第一个扩展方法返回一个PropertyPart ,必须与Column方法一起使用,以指定常量值应映射到哪一列。 Because of this, the column name is not known when the extension method is executed and we need to create one ourselves. 因此,在执行扩展方法时,列名称是未知的,我们需要自己创建一个。 This is done by CreateUniqueMemberName : 这是由CreateUniqueMemberName完成的:

     private static string CreateUniqueMemberName() { return "Dummy" + Guid.NewGuid().ToString("N"); } 
  2. Because you can only specify a type as access strategy and not an instance, I couldn't create an IPropertyAccessor implementation allowed me to simply pass an IGetter instance in the constructor. 因为您只能将类型指定为访问策略而不是实例,所以我无法创建IPropertyAccessor实现,只允许我在构造函数中传递IGetter实例。 That's what ConstantValueAccessor.RegisterGetter(typeof(TType), getter); 这就是ConstantValueAccessor.RegisterGetter(typeof(TType), getter); solves. 解决了。 ConstantValueAccessor has a static collection of getters: 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); } // ... } 

The implementation of ConstantValueGetter<T> is the same as the one from the provided link. ConstantValueGetter<T>的实现与提供的链接的实现相同。

Because it wasn't that much fun to implement GetterSetterPropertyInfo , here it is. 因为它并没有实现一个非常有趣GetterSetterPropertyInfo在这里它是。 One important difference is, that this implementation doesn't have any dependencies on (Fluent) NHibernate. 一个重要的区别是,这个实现对(Fluent)NHibernate没有任何依赖性。

If you don't want to introduce property in your entity class the only solution I see is to create custom property accessor which will always return constant value. 如果您不想在实体类中引入属性,我看到的唯一解决方案是创建自定义属性访问器,它始终返回常量值。 Here is possible implementation: 这是可能的实现:

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
    }
}

Here how to use it: 在这里如何使用它:

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

Property "Value" doesn't need to exist in your entity. 属性“值”不需要存在于您的实体中。 It is here because attribute "name" is required. 这是因为属性“名称”是必需的。

My implementation takes the same idea as hival but goes a lot further. 我的实现与hival具有相同的想法,但进一步发展。 the basis is an implementation of IPropertyAccessor 基础是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