sircodesalot
sircodesalot

Reputation: 11439

How do you hold a reference to a NewExpression?

I have this code:

public static Func<IDataReader, T> CreateBinder<T>() {

    NewExpression dataTransferObject = Expression.New(typeof(T).GetConstructor(Type.EmptyTypes)); 
    ParameterExpression dataReader = Expression.Parameter(typeof(IDataReader), "reader");

    IEnumerable<Expression> columnAssignments = typeof(T).GetProperties().Select(property => {
        MethodCallExpression columnData = Expression.Call(dataReader, dataReaderIndexer, new[] { Expression.Constant(property.Name) });
        MethodCallExpression setter = Expression.Call(dataTransferObject, property.SetMethod, new[] { Expression.Convert( columnData, property.PropertyType ) });

        return setter;
    });

    columnAssignments = columnAssignments.Concat(new Expression[] { dataTransferObject });
    BlockExpression assignmentBlock = Expression.Block(columnAssignments);

    Func<IDataReader, T> binder = Expression.Lambda<Func<IDataReader, T>>(assignmentBlock, new[] { dataReader }).Compile();

    return binder;
}

which long story short binds the properties on database row to <T>. The problem is that when I want to use/return dataTransferObject, it's instantiating a new copy each time. How do I just get the reference, without recreating the object?

Upvotes: 1

Views: 419

Answers (2)

p.s.w.g
p.s.w.g

Reputation: 149108

I'd recommend using member intialization / binding expressions rather than a series of setters:

public static Func<IDataReader, T> CreateBinder<T>() 
{
    NewExpression dataTransferObject = Expression.New(typeof(T).GetConstructor(Type.EmptyTypes)); 
    ParameterExpression dataReader = Expression.Parameter(typeof(IDataReader), "reader");

    IEnumerable<MemberBinding > bindings = typeof(T).GetProperties().Select(property => {
        MethodCallExpression columnData = Expression.Call(dataReader, dataReaderIndexer, new[] { Expression.Constant(property.Name) });
        MemberBinding binding = Expression.Binding(property, Expression.Convert( columnData, property.PropertyType));

        return binding;
    });

    Expression init = Expression.MemberInit(dataTransferObject, bindings);

    Func<IDataReader, T> binder = Expression.Lambda<Func<IDataReader, T>>(init, new[] { dataReader }).Compile();

    return binder;
}

Upvotes: 3

Thomas Levesque
Thomas Levesque

Reputation: 292765

You just need to assign the NewExpression to a variable, and use that variable instead of the NewExpression.

var dataTransferObject = Expression.Variable(typeof(T), "dto");
var assignment = Expression.Assign(
                     dataTransferObject,
                     Expression.New(typeof(T).GetConstructor(Type.EmptyTypes)));

(add these expressions to the BlockExpression)

Upvotes: 3

Related Questions