4 Stimmen

Datenbankspalte auf konstanten Wert abbilden, ohne dass eine Eigenschaft in der Entitätsklasse erforderlich ist

Ist es möglich, eine Datenbankspalte einem konstanten Wert zuzuordnen, ohne dass eine Eigenschaft in der Entitätsklasse erforderlich ist? Dies ist im Grunde ein Workaround für einen fehlenden Standardwert für diese Spalte in der Datenbank in Kombination mit einer NOT NULL-Beschränkung. Die Datenbank ist extern und kann nicht geändert werden, aber ich brauche nicht alle Spalten in dieser Tabelle und möchte daher keine entsprechenden Eigenschaften in meiner Entitätsklasse haben.

Meine Frage ist im Grunde dieselbe wie in dieses Hibernate JIRA-Problem .

2voto

Daniel Hilgarth Punkte 165768

Auf der Grundlage von Firos Antwort habe ich das Problem gelöst. Allerdings gefiel mir die zu verwendende Syntax nicht ganz und die Tatsache, dass ich für die Standardwerte für jede Entität eine neue Klasse erstellen musste.

Die Syntax, die ich jetzt habe, sieht wie folgt aus:

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

Ich habe die folgenden Erweiterungsmethoden dafür erstellt:

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

Die wichtigsten Unterschiede sind:

  1. Die erste dieser Erweiterungsmethoden gibt eine PropertyPart und muss in Verbindung mit der Option Column Methode, um anzugeben, welcher Spalte der konstante Wert zugeordnet werden soll. Aus diesem Grund ist der Spaltenname zum Zeitpunkt der Ausführung der Erweiterungsmethode nicht bekannt und wir müssen ihn selbst erstellen. Dies geschieht durch CreateUniqueMemberName :

    private static string CreateUniqueMemberName()
    {
        return "Dummy" + Guid.NewGuid().ToString("N");
    }
  2. Da man nur einen Typ als Zugriffsstrategie angeben kann und nicht eine Instanz, konnte ich nicht eine IPropertyAccessor Implementierung konnte ich einfach eine IGetter Instanz im Konstruktor. Das ist es, was ConstantValueAccessor.RegisterGetter(typeof(TType), getter); löst. ConstantValueAccessor hat eine statische Sammlung von Gettern:

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

Die Umsetzung der ConstantValueGetter<T> ist die gleiche wie die unter dem angegebenen Link.

Weil es nicht so viel Spaß gemacht hat, sie zu implementieren. GetterSetterPropertyInfo , aquí es ist. Ein wichtiger Unterschied ist, dass diese Implementierung keine Abhängigkeiten von (Fluent) NHibernate hat.

1voto

hival Punkte 675

Wenn Sie nicht wollen, Eigenschaft in Ihre Entitätsklasse einzuführen, ist die einzige Lösung, die ich sehe, um benutzerdefinierte Eigenschaft Accessor, die immer konstanten Wert zurückgeben wird erstellen. Hier ist eine mögliche Implementierung:

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

Hier erfahren Sie, wie Sie es verwenden können:

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

Die Eigenschaft "Wert" muss in Ihrer Entität nicht vorhanden sein. Sie ist hier, weil das Attribut "Name" erforderlich ist.

1voto

Firo Punkte 29738

Meine Implementierung basiert auf der gleichen Idee wie hival, geht aber viel weiter. Die Basis ist eine Implementierung von 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"),
            };
        }
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X