Database database column for constant value without the need for a property in an entity class

Is it possible to map a database column to a constant value without the need for a property in an entity class? This is basically a workaround for the missing default value for this column in the database, combined with the NOT NULL constraint. The database is external and cannot be modified, but I do not need all the columns in this table and, therefore, I do not want to have the corresponding properties in the entity class.

I ask basically the same as described in this JIRA issue for hibernation .

+4
source share
3 answers

My implementation accepts the same idea as hival, but goes much further. the foundation is the implementation of 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"), }; } } } 
+1
source

Based on Firos answer, I solved the problem. However, I didn’t quite like the syntax used and the fact that I would need to create a new class for the default values ​​for each object.

The syntax I received 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); } 

Important differences are:

  • The first of these extension methods returns a PropertyPart and should be used in conjunction with the Column method to indicate in which column the constant value should be mapped. Because of this, the column name is not known when the extension method is executed, and we must create it ourselves. This is done using CreateUniqueMemberName :

     private static string CreateUniqueMemberName() { return "Dummy" + Guid.NewGuid().ToString("N"); } 
  • Since you can only specify a type as an access strategy, not an instance, I could not create an IPropertyAccessor implementation IPropertyAccessor that I could just pass the IGetter instance in the constructor. What ConstantValueAccessor.RegisterGetter(typeof(TType), getter); solves ConstantValueAccessor.RegisterGetter(typeof(TType), getter); . ConstantValueAccessor has a static set of getters:

     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 referenced link.

Because it wasn’t so much fun to implement GetterSetterPropertyInfo , here . An important difference is that this implementation has no dependencies on (Fluent) NHibernate.

+2
source

If you don't want to introduce a property into your entity class, the only solution I see is to create a custom accessor property that will always return a constant value. Here is a 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's how to use it:

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

The Value property does not have to exist in your organization. This is where the name attribute is required.

+1
source

Source: https://habr.com/ru/post/1392780/


All Articles