2016-07-08 12 views
0

Ich habe den folgenden Code geschrieben, um Mapping-Parameter von meiner Datenbank zu meinen Datentypen zu verarbeiten (vertraue mir Ich wünschte, ich könnte ein Std. ORM verwenden, aber das ist nicht machbar viele Gründe)Wie man einen Parameter während der Verwendung von Ausdruck setzt <Func<T>>

public void LoadDatabaseValue<T>(DataTable partData, string identifier, string mappingName, Expression<Func<T>> mappingProperty) 
    { 
     var partAttributeValue = mappingProperty.Name; 
     var memberExpression = (MemberExpression)mappingProperty.Body; 
     var prop = (PropertyInfo)memberExpression.Member; 
     try 
     { 
      var selectedRow = partData.Select($"partattributename = '{mappingName}'"); 
      var selectedValue = selectedRow[0]["PartAttributeValue"]; 

      var typedOutput = (T)Convert.ChangeType(selectedValue, typeof(T)); 

      prop.SetValue(memberExpression.Expression, typedOutput, null); 
     } 
     catch (Exception exception) 
     { 
      _databaseImportError = true; 
      // code to log this error 
    } 

Wenn ich versuche, das ich die folgende Ausnahme

{System.Reflection.TargetException: Object does not match target type. 
at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target) 
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) 
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index) } 

bekommen zu laufen, wenn ich es debuggen meine typedOutput Linien mit meiner Art der Immobilie, so dass ich bin mir nicht sicher, warum es wirft diese Ausnahme.

ich mit ihm zum Beispiel nenne

LoadDatabaseValue(partData, identifier, "Offset",() => Offset); 
+0

Sie eine 'Func' sind vorbei, aber erwartet, dass es' Expression > 'zu sein. Ändern Sie einen von ihnen zum gleichen Typ. – Venky

+1

@Venky Er ist in einem Ausdruck. – Servy

Antwort

1

Wenn Sie Ihre aktuelle Methode Design behalten möchten, müssen Sie einen Weg, um die memberExpression.Expression, um irgendwie bewerten zu können, rufen SetValue Methode.

So ändern Sie die Zeile

prop.SetValue(memberExpression.Expression, typedOutput, null); 

zu

prop.SetValue(Evaluate(memberExpression.Expression), typedOutput, null); 

und dann eine der folgenden Implementierungen verwenden:

(A) Dies ist ausreichend, wenn Sie nur Eigenschaftenaccessoren verwenden:

static object Evaluate(Expression e) 
{ 
    if (e == null) return null; 
    var me = e as MemberExpression; 
    if (me != null) 
     return ((PropertyInfo)me.Member).GetValue(Evaluate(me.Expression), null); 
    return ((ConstantExpression)e).Value; 
} 

(B) Dies ist universeller, aber langsamer:

static object Evaluate(Expression e) 
{ 
    if (e == null) return null; 
    return Expression.Lambda(e).Compile().DynamicInvoke(); 
} 
0

Der erste Parameter von SetValue ein Objekt sein muss, legen Sie die Eigenschaft, deren Wert Sie enthält.

Jetzt sollte obj.Offset den gewünschten Wert haben.

Es gibt also zwei Arten beteiligt: ​​Der Typ des Objekts, das die Eigenschaft und den Typ der Eigenschaft selbst enthält (z. B. int, string usw.).

Deshalb sollte Ihr Ausdruck aussehen:

Expression<Func<TEntity, object>> mappingProperty 

wo TEntity die Art des Objekts und object ist die noch unbekannte Art einer Eigenschaft dieses Objekts. Es sei denn, Sie die Art der Unterkunft im Voraus wissen, in welchem ​​Fall Sie

Expression<Func<TEntity, TProperty>> mappingProperty 

Sie es so nennen würde, hätte:

LoadDatabaseValue(partData, identifier, "Offset", x => x.Offset); 

Sie müssen die Art, wie dies ändern (es sei denn, die selectedValue ist bereits den richtigen Typ):

object typedOutput = Convert.ChangeType(selectedValue, prop.PropertyType); 
+0

Ich habe versucht, dies zu implementieren, aber die Signatur LoadDatabaseValue (partData, Bezeichner, "Offset", x => x.Offset); würde nicht kompilieren. Was ist der Vorteil dieser Methode gegenüber der anderen vorgestellten Lösung? – PlTaylor

+0

Der Typ 'TEntity' muss natürlich eine 'Offset'-Eigenschaft haben. Sie versuchen, den Wert von 'memberExpression.Expression' festzulegen. Dieser Ausdruck enthält jedoch nur Metainformationen über den realen Ausdruck und repräsentiert nicht das reale Objekt selbst. Ich verstehe nicht vollständig, was Sie zu tun versuchen, aber ich würde die Methode so deklarieren: 'public void LoadDatabaseValue (DataTable partData, TEntity entity, string mappingName, Ausdruck > mappingProperty)' und call 'prop.SetValue (entity, typedOutput);' um eine Eigenschaft des Entitätsobjekts zu füllen. –

+0

Und auch 'typedOutput' muss einen Wert vom richtigen Typ enthalten, kann aber selbst als' Objekt' eingegeben werden, da der Parametertyp in 'SetValue' sowieso' object' ist. –

Verwandte Themen