2012-01-24 2 views
4

Возможно ли сопоставить столбец базы данных с постоянным значением без необходимости в свойстве в классе сущности? В основном это обходное решение для отсутствующего значения по умолчанию для этого столбца в базе данных в сочетании с ограничением NOT NULL. База данных является внешней и не может быть изменена, но мне не нужны все столбцы в этой таблице и, следовательно, я не хочу иметь соответствующие свойства в классе объектов.Столбец базы данных базы данных для постоянного значения без необходимости в свойстве класса объектов

Я прошу в основном так же, как описано в this Hibernate JIRA issue.

ответ

1

Моя реализация принимает ту же идею, что и у соперника, но идет намного дальше. основой является внедрение 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"), 
      }; 
     } 
    } 
} 
+0

Спасибо большое, это отлично выглядит. Я проверю его в выходные. –

+0

Это работает ! :) Пожалуйста, посмотрите мой ответ на несколько иной подход - что вы думаете об этом? –

1

Если вы не хотите вводить свойство в свой класс сущности, единственным решением, которое я вижу, является создание настраиваемого свойства accessor, который всегда будет возвращать постоянное значение. Вот возможная реализация:

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" /> 

недвижимости «Значение» не должны существовать в вашей организации. Именно здесь требуется атрибут «имя».

+0

Это выглядит многообещающе. Я проведу его через несколько часов. Можете ли вы предоставить плавное отображение вместо отображения HBM? Благодарю. –

+0

К сожалению, я использую только hbm и отображение по коду. – hival

+0

ОК. Спасибо, в любом случае. Если я смогу перевести его на плавное отображение, я отправлю его. –

2

На основании ответа 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 и должен использоваться в сочетании с th e Column метод для указания того, в какой столбец должно быть сопоставлено значение константы. Из-за этого имя столбца неизвестно, когда выполняется метод расширения, и мы должны создать его самостоятельно. Это делается CreateUniqueMemberName:

    private static string CreateUniqueMemberName() 
    { 
        return "Dummy" + Guid.NewGuid().ToString("N"); 
    } 
    
  2. Потому что вы можете указать только тип, стратегии доступа, а не, например, я не мог создать IPropertyAccessor реализация позволила мне просто пройти IGetter экземпляр в конструкторе. Вот что решает ConstantValueAccessor.RegisterGetter(typeof(TType), getter);. ConstantValueAccessor имеет статический сбор геттеров:

    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, here это так. Важным отличием является то, что эта реализация не имеет никаких зависимостей от (Fluent) NHibernate.

+0

Очень приятно. Я бы одобрил ваш подход, однако я не могу использовать .NET 4, и я хотел бы поддержать случай, когда объект конфигурации (сериализуется) (более 100 объектов), который не поддерживается вашей регистрацией в сопоставлениях. так же, как когда xmlmappings создаются из свободного и читаются как xml. – Firo

+0

@ Фиро: Я вижу. Он не поддерживается из-за статического словаря '_getters', правильно? Или я чего-то не хватает? –

+0

да, потому что регистрация выполняется в классах сопоставления, которые не используются при десериализации объекта конфигурации. ConcurrentDictionary - только .net 4 – Firo

Смежные вопросы