jeudi 16 juin 2016

How to get/set property in LINQ as object?

With reflection, you can set an object's property like this:

System.Reflection.PropertyInfo pi = [...];
pi.SetValue(object obj, object value);

Now, I'm trying to replace reflection with LINQ, because this is faster.
I'm using the below SetProperty method to replace reflection.

This works fine, but it requires me to know the field/property type at compile time.
I want to be a little more flexible.
I want to have SetProperty to have this signature:

SetProperty<TTarget>(TTarget target, string fieldName, object newValue)

However, it I do just this, I get an invalid cast exception.
How can I use an object in SetProperty (again: with reflection I can do it myself; I want it WITHOUT reflection) ?
[Note: for better performance one would of course store the compiled setter/getter. This is just for testing.]

public static object GetProperty<T>(T obj, string fieldName)
{
    System.Linq.Expressions.ParameterExpression p = System.Linq.Expressions.Expression.Parameter(typeof(T));

    var prop = System.Linq.Expressions.Expression.PropertyOrField(p, fieldName);
    var con = System.Linq.Expressions.Expression.Convert(prop, typeof(object));
    var exp = System.Linq.Expressions.Expression.Lambda(con, p);

    var getValue = (System.Func<T, object>)exp.Compile();
    return getValue(obj);
}


// http://ift.tt/23dct2I
public static void SetProperty<TTarget, TValue>(TTarget target, string fieldName, TValue newValue)
{
    System.Type tt = newValue.GetType();
    System.Type ttt = typeof(TValue);

    System.Linq.Expressions.ParameterExpression targetExp = System.Linq.Expressions.Expression.Parameter(typeof(TTarget), "target");
    System.Linq.Expressions.ParameterExpression valueExp = System.Linq.Expressions.Expression.Parameter(typeof(TValue), "value");


    System.Linq.Expressions.ParameterExpression p = System.Linq.Expressions.Expression.Parameter(typeof(TTarget));
    // System.Linq.Expressions.MemberExpression fieldExp = System.Linq.Expressions.Expression.PropertyOrField(p, fieldName);

    // Expression.Property can be used here as well
    System.Linq.Expressions.MemberExpression fieldExp =
        // System.Linq.Expressions.Expression.Field(targetExp, fieldName);
        // System.Linq.Expressions.Expression.Property(targetExp, fieldName);
        System.Linq.Expressions.Expression.PropertyOrField(targetExp, fieldName);

    System.Linq.Expressions.BinaryExpression assignExp = 
        System.Linq.Expressions.Expression.Assign(fieldExp, valueExp);

    System.Action<TTarget, TValue> setter = System.Linq.Expressions.Expression
        .Lambda<System.Action<TTarget, TValue>>(assignExp, targetExp, valueExp).Compile();


    setter(target, newValue);
}



public class Person
{
    public string Name { get; set; }
    public Person Brother { get; set; }
    public string Email { get; set; }

    public string SnailMail;
    public int Anumber;
}


public class T_User
{
    public int BE_ID { get; set; }
    public string BE_Name { get; set; }

    public string SnailMail;
    public int Anumber;
}


public static void LinqTest()
{
    System.Collections.Generic.List<string> ls = new System.Collections.Generic.List<string>();
    ls.Add("foo");
    ls.Add("bar");
    ls.Add("foobar");

    int oobj = 123;
    Person someOne = new Person() { Name = "foo", Email = "foo@bar.com", SnailMail="Snail" };
    // SetProperty(someOne, "Anumber", oobj);
    // SetProperty(someOne, "SnailMail", "Turtle Mail");
    // SetProperty(someOne, "Email", "SpamMail");
    T_User ben = new T_User();

    GetProperty(ls, "Count");

    string SQL = @"SELECT TOP 1 BE_ID, BE_Name FROM T_User";
    using (System.Data.Common.DbDataReader rdr = SQL.ExecuteReader(SQL))
    {
        do
        {
            int fieldCount = rdr.FieldCount;
            System.Type[] ts = new System.Type[fieldCount];
            string[] fieldNames = new string[fieldCount];
            for (int i = 0; i < fieldCount; ++i)
            {
                ts[i] = rdr.GetFieldType(i);
                fieldNames[i] = rdr.GetName(i);
            } // Next i 


            if (rdr.HasRows)
            {
                while (rdr.Read())
                {
                    for (int i = 0; i < fieldCount; ++i)
                    {
                        object objValue = rdr.GetValue(i);

                        System.Console.WriteLine(ts[i]);
                        int abc = 123;
                        SetProperty(ben, fieldNames[i], abc);
                        SetProperty(ben, fieldNames[i], objValue);
                        System.Console.WriteLine(objValue);
                    } // Next i 

                } // Whend 

            } // End if (rdr.HasRows)

        } while (rdr.NextResult( ));

    } // End Using rdr 

} // End Sub LinqTest 





Aucun commentaire:

Enregistrer un commentaire