7

From this question I asked 5 minutes ago, it's clear that the following code throws an exception, stating that

Unhandled Exception: System.InvalidOperationException: The binary operator Equal is not defined for the types 'System.Nullable`1[System.Int32]' and 'System.Int32'.

Code

public static void GetResultCollection<T>() {
        AccrualTrackingEntities db = new AccrualTrackingEntities();
        var result = db.CreateQuery<T>(String.Format("[{0}]", typeof(T).Name + "s"));

        int? ItemTypeValue = 1;

        var param = Expression.Parameter(typeof(T));

        var lambda = Expression.Lambda<Func<T, bool>>(
            Expression.Equal(
                Expression.Property(param, "ProcInstId"),
                Expression.Constant(ItemTypeValue)),
            param);

        var list = result.Where(lambda).ToList();
    }

This code, however, with the type explicitly listed in Expression.Constant does work

class Program {
    public static void GetResultCollection<T>() {
        AccrualTrackingEntities db = new AccrualTrackingEntities();
        var result = db.CreateQuery<T>(String.Format("[{0}]", typeof(T).Name + "s"));

        int? ItemTypeValue = 1;

        var param = Expression.Parameter(typeof(T));

        var lambda = Expression.Lambda<Func<T, bool>>(
            Expression.Equal(
                Expression.Property(param, "ProcInstId"),
                Expression.Constant(ItemTypeValue, typeof(int?))),
            param);

        var list = result.Where(lambda).ToList();
    }

The question is, why is Expression.Constant not able to convert implicitly from int? to ... int?

Community
  • 1
  • 1
Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
  • you could create your own extension where you can get the type and if it is nullable, etc. then use the Expression.Constant(Object, Type) with the discovered type – Kris Ivanov Feb 21 '11 at 15:14

1 Answers1

15

Expression trees work at a lower level to normal source code - you can think of them as working at the level of the output of the compiler rather than the input. So while there's an implicit conversion from int to int? in C#, that conversion has to be represented in IL whenever the compiler uses it for a normal method... so it also has to be present in an expression tree representation.

Having said that, your example is somewhat unclear, given that you're trying to use an int (namely ItemTypeValue.Value) as a value for an int? constant, and we don't know what the type of the ItemType property is either.

A short but complete example of what you'd expect to work would really help.

EDIT: Okay, I think I'm with you now. The problem is that if you use

int? foo = 1;
Expression.Constant(foo);

then that calls Expression.Constant(object) which boxes the value of foo. At that point, Expression.Constant can't tell it was originally an int?, because it's now a boxed int. That's just the way .NET boxing works:

int? foo = 1;
object o = foo;
Console.WriteLine(o.GetType()); // Prints System.Int32

That overload of Expression.Constant determines the overall type of the expression from the value that it's given - so it creates an int expression, whereas you really want an int? expression.

In order to maintain the type information properly, you have to use the overload which allows you to specify the type:

int? foo = 1;
Expression.Constant(foo, typeof(int?));

It's still not entirely clear from your question which code works and which doesn't, but hopefully that'll help...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • It's up. I worded the original question poorly. The way I had it, it was indeed trying to convert from int to int? My original question should have been a straight shot from int? to int? – Adam Rackis Feb 21 '11 at 15:17
  • I changed the original question's text so it makes more sense. Now it's got Expression.Constant(ItemValueType, int?) where ItemValueType is declared as int? (which I would think would be superfluous) If I had known you were in the neighborhood I would have triple-checked before posting :-) – Adam Rackis Feb 21 '11 at 15:20
  • @Adam: You still haven't told us what the type of "ItemType" is. That's why I was hoping for a *complete* example - a console app we can compile and run. – Jon Skeet Feb 21 '11 at 15:22
  • It's int? in the database, and in the entity. That's why this is so confusing to me. – Adam Rackis Feb 21 '11 at 15:28
  • @Adam: Okay, so a constant of type `int?` should work, but a constant of type `int` won't. I think I'm with you now... – Jon Skeet Feb 21 '11 at 15:29
  • And I've replicated this with another database. Another int? refuses to be searched with an expression.constant unless I explicitly specify the type, even if the variable I pass in is explicitly declared as int? – Adam Rackis Feb 21 '11 at 15:32
  • Brilliant as always, thanks Jon. I will clean up my question to make things crystal clear (for internet posterity). Did you get anything more than 15 from this Q, or are you already maxed out? – Adam Rackis Feb 21 '11 at 15:39
  • @Adam: Just the 15, but that's fine. Glad it helped :) – Jon Skeet Feb 21 '11 at 15:42