[英]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);
}
重要的區別是:
第一個擴展方法返回一個PropertyPart
,必須與Column
方法一起使用,以指定常量值應映射到哪一列。 因此,在執行擴展方法時,列名稱是未知的,我們需要自己創建一個。 這是由CreateUniqueMemberName
完成的:
private static string CreateUniqueMemberName() { return "Dummy" + Guid.NewGuid().ToString("N"); }
因為您只能將類型指定為訪問策略而不是實例,所以我無法創建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.