84

Acting on the answer to my Select a model property using a lambda and not a string property name question, wanting to add properties to a collection as follows:

var props = new ExportPropertyInfoCollection<JobCard>();
props.Include(model => model.BusinessInstallNumber).Title("Install No").Width(64).KeepZeroPadding(true);
props.Include(model => model.DeviceName).Title("Device").Width(70);
props.Include(model => model.DateRequested).Title("Request Date").Format("{0:dd/MM/yyyy}").Width(83);

I wrote the following code in the Include method:

public class PropertyCollection<T>
{
    public void Include(Expression<Func<T, object>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;
        if (memberExpression != null)
        {
            var pes = new ExportPropertyInfoBuilder {Property = new ExportPropertyInfo {Property = memberExpression.Member as PropertyInfo}};
            Properties.Add(pes.Property.Property.Name, pes.Property);
            return pes;
    }

However, on running the code, I found some of the lambdas yielded MemberExpression values as expected, but others yielded UnaryExpression values. I had to change the first line of code to the following before I could add all my properties using lambdas:

var memberExpression = expression.Body as MemberExpression ?? ((UnaryExpression) expression.Body).Operand as MemberExpression;

All the properties are 'simple' types, i.e. string, DateTime, int, bool, etc. in a POCO business object. They are decorated with several varying DataAnnotations attributes.

What causes some of the lambdas in my example to yield MemberExpression values and others UnaryExpression values? In my example, the first UnaryExpression is on the third line, the DateTime property, but boolean properties also result in UnaryExpressions.

Community
  • 1
  • 1
ProfK
  • 49,207
  • 121
  • 399
  • 775
  • 1
    Does the UnaryExpression expression maybe occur in the presence (or 'unpresence') of nullable *columns*? – leppie Aug 25 '10 at 16:00
  • @leppie, I suspect it's on non-nullable columns. In my example, the first UnaryExpression is on a DateTime, where the prior MemberExpressions are on strings. A subsequent UnaryExpression is on a bool. – ProfK Aug 25 '10 at 16:45
  • I will investigate a bit. I have used (or rather abused) expressions like this before, and never had problems, meaning it was always a `MemberExpression` else my code would fail. What version of .NET are you running on? I don't use .NET 4 yet. – leppie Aug 25 '10 at 21:51
  • Not sure if it will matter, but can you include the expected type of expression that `Include` expects? (including constraints) – leppie Aug 25 '10 at 21:53
  • @leppie, I've added the method 'header' with the expression parameter as best as I remember it. I don'thave it with me. – ProfK Aug 26 '10 at 00:48
  • 1
    see also http://stackoverflow.com/questions/12975373/expression-for-type-members-results-in-different-expressions-memberexpression – Jay Wick Nov 10 '14 at 09:23

1 Answers1

71

I think I know what the problem is. Your expression returns type object.

If you change this to Expression<Func<T, R>> the return type should be correctly inferred, and UnaryExpression (which I will assume is some boxing operation) should not occur.

Update:

The signature for Include should be:

public void Include<T, R>(Expression<Func<T, R>> expression)
leppie
  • 115,091
  • 17
  • 196
  • 297
  • 1
    Excuse my stupidness here, but what should R be? I can't make it MemberExpression, because the MemberExression is in the Body property of the expression. I agree that the UnaryExpression is probably because of boxing though. – ProfK Aug 26 '10 at 15:46
  • 4
    @ProfK: R is simply inferred, it will be the type of the property that is returned. You probably wont use it, but you might :) – leppie Aug 26 '10 at 15:52
  • 2
    I think I see what is happening. Because the expression return type is object, it is boxed. With a typed return type, this doesn't happen. Thanks @leppie! Trust a functional man to help out here :-) – ProfK Aug 26 '10 at 17:26
  • 1
    Great answer. Shouldn't the signature for Include be: public void Include(Expression> expression) because T is already defined by the PropertyCollection class. – Xcalibur Mar 24 '11 at 02:47
  • @Xcalibur: No, you have to specify (or define) all the generic type parameters used. – leppie Mar 24 '11 at 04:59
  • 3
    @leppie The T generic type parameter is already defined at the class level, so either a different type parameter name should be used if the intent is different or it is redundant – Xcalibur Apr 29 '11 at 13:48