tags:

views:

195

answers:

2

From my recent question, I try to centralize the domain model by including some silly logic in domain interface. However, I found some problem that need to include or exclude some properties from validating.

Basically, I can use expression tree like the following code. Nevertheless, I do not like it because I need to define local variable ("u") each time when I create lambda expression. Do you have any source code that is shorter than me? Moreover, I need some method to quickly access selected properties.

public void IncludeProperties<T>(params Expression<Func<IUser,object>>[] selectedProperties)
{
    // some logic to store parameter   
}

IncludeProperties<IUser>
(
    u => u.ID,
    u => u.LogOnName,
    u => u.HashedPassword
);

Thanks,

+8  A: 

Lambdas are great for many scenarios - but if you don't want them, perhaps simply don't use them? I hate to say it, but simple strings are tried and tested, especially for scenarios like data binding. If you want fast access, you could look at HyperDescriptor, or there are ways of compiling a delegate to the property accessors, or you can build an Expression from the string and compile it (including a cast to object if you want a known signature, rather than calling the (much slower) DynamicInvoke).

Of course, in most cases even crude reflection is fast enough, and isn't the bottleneck.

I suggest starting with the simplest code, and check it is actually too slow before worrying about it being fast. If it isn't too slow, don't change it. Any of the above options would work otherwise.


Another thought; if you are using Expression, you could do something like:

public void IncludeProperties<T>(
    Expression<Func<T,object>> selectedProperties)
{
    // some logic to store parameter   
}

IncludeProperties<IUser>( u => new { u.ID, u.LogOnName, u.HashedPassword });

and then take the expression apart? A bit tidier, at least... here's some sample code showing the deconstruction:

public static void IncludeProperties<T>(
    Expression<Func<T, object>> selectedProperties)
{
    NewExpression ne = selectedProperties.Body as NewExpression;
    if (ne == null) throw new InvalidOperationException(
          "Object constructor expected");

    foreach (Expression arg in ne.Arguments)
    {
        MemberExpression me = arg as MemberExpression;
        if (me == null || me.Expression != selectedProperties.Parameters[0])
            throw new InvalidOperationException(
                "Object constructor argument should be a direct member");
        Console.WriteLine("Accessing: " + me.Member.Name);
    }
}
static void Main()
{
    IncludeProperties<IUser>(u => new { u.ID, u.LogOnName, u.HashedPassword });
}

Once you know the MemberInfos (me.Member in the above), building your own lambdas for individual access should be trivial. For example (including a cast to object to get a single signature):

var param = Expression.Parameter(typeof(T), "x");
var memberAccess = Expression.MakeMemberAccess(param, me.Member);
var body = Expression.Convert(memberAccess, typeof(object));
var lambda = Expression.Lambda<Func<T, object>>(body, param);
var func = lambda.Compile();
Marc Gravell
oooh, nice one :)
Thomas Levesque
What's the best way to keep selected properties for quick accessing? List<MemberInfo>?
Soul_Master
`MemberInfo` would be fine; `Func<T,object>` (or `Func<object,object>`) would be fine; a `PropertyDescriptor` would be fine (esp. with HyperDescriptor). It'll work either way...
Marc Gravell
+1  A: 

Here's the shortest expression I can come up with:

public static void IncludeProperties(Expression<Action<IUser>> selectedProperties)
{
    // some logic to store parameter   
}

public static void S(params object[] props)
{
    // dummy method to get to the params syntax
}

[Test]
public void ParamsTest()
{
    IncludeProperties(u => S(
        u.Id,
        u.Name
        ));

}
Peter Lillevold
If you use an anonymous type and initializer (see my updated answer) you don't need the dummy method. Which is nice
Marc Gravell
Yes, I see that now :)
Peter Lillevold