Recently, I came across some troubles about boxing using Expression Trees when I was developing my homemade SQLite ORM. I am still coding again C# 3.5.
To make a long story short, I'm gonna use this simple class definition:
[Table]
public class Michelle
{
[Column(true), PrimaryKey]
public UInt32 A { get; set; }
[Column]
public String B { get; set; }
}
So this from that POCO class definition I instantiated a new object and my ORM engine converted that object into a record and it's properly inserted. Now the trick is that when I get back the value from SQLite I got an Int64.
I thought that my delegate setter was OK because it's an Action<Object, Object> but I still got that InvalidCastException. Seems that the Object (parameter, an Int64) is attempted to be cast into a UInt32. Unfortunately it does not work. I tried to add some Expression.Constant and Expression.TypeAs (that one does not really help for value typed objects).
So I am wondering what sort of things are wrong in my setter generation.
Here below is my setter generation method:
public static Action<Object, Object> GenerateSetter(PropertyInfo propertyInfo)
{
if (!FactoryFastProperties.CacheSetters.ContainsKey(propertyInfo))
{
MethodInfo methodInfoSetter = propertyInfo.GetSetMethod();
ParameterExpression parameterExpressionInstance = Expression.Parameter(FactoryFastProperties.TypeObject, "Instance");
ParameterExpression parameterExpressionValue = Expression.Parameter(FactoryFastProperties.TypeObject, "Value");
UnaryExpression unaryExpressionInstance = Expression.Convert(parameterExpressionInstance, propertyInfo.DeclaringType);
UnaryExpression unaryExpressionValue = Expression.Convert(parameterExpressionValue, propertyInfo.PropertyType);
MethodCallExpression methodCallExpression = Expression.Call(unaryExpressionInstance, methodInfoSetter, unaryExpressionValue);
Expression<Action<Object, Object>> expressionActionObjectObject = Expression.Lambda<Action<Object, Object>>(methodCallExpression, new ParameterExpression[] { parameterExpressionInstance, parameterExpressionValue });
FactoryFastProperties.CacheSetters.Add(propertyInfo, expressionActionObjectObject.Compile());
}
return FactoryFastProperties.CacheSetters[propertyInfo];
}
So basically:
// Considering setter as something returned by the generator described above
// So:
// That one works!
setter(instance, 32u);
// This one... hm not really =/
setter(instance, 64);
I should probably add another Expression.Convert but I do not really know how it would help to make it work. Since there is already one supposed to (attempt to) convert from any Object to the property type (here in my example the UInt32 type).
Any idea to fix it up?
For this answer, assume the following is defined:
object myColValueFromTheDatabase = (object)64L;
Expression.Convert determines statically how the conversion is to be performed. Just like C# does. If you write (uint)myColValueFromTheDatabase this will not succeed at runtime because unboxing just does not work that way. Expression.Convert does a simple unboxing attempt as well. That's why it fails.
You would need to do either of the following:
(uint)(long)myColValueFromTheDatabase
Convert.ToUInt32(myColValueFromTheDatabase)
In case (1) you need to unbox to the exact-match type first, then change the bits. Case (2) resolves this using some helper methods. Case (1) is faster.
To do this with the expression API, insert another Expression.Convert.
This should get you started:
public static T LogValue<T>(T val)
{
Console.WriteLine(val.GetType().Name + ": " + val);
return val;
}
static void Main(string[] args)
{
Expression myColValueFromTheDatabase = Expression.Convert(Expression.Constant(1234L), typeof(object));
myColValueFromTheDatabase = Expression.Call(typeof(Program), "LogValue", new[] { myColValueFromTheDatabase.Type }, myColValueFromTheDatabase); //log
Expression unboxed = Expression.Convert(myColValueFromTheDatabase, typeof(long));
Expression converted = Expression.Convert(unboxed, typeof(uint));
var result = Expression.Lambda<Func<uint>>(converted).Compile()();
Console.WriteLine(result);
}
Related
My ultimate goal here is to make a generic version of IQueryable<T>.OrderBy() that takes a string parameter and an optional sort direction. Something like these:
return myList.OrderBy("Property1");
return myList.Orderby("Property1", SortOrder.Descending);
(The use case would be a website passing in a field the list would sort by.)
To that end, I've been trying to come up with a way of creating the following expression:
obj => obj.PropertyName
I've found a few places in StackOverflow that could help me, and they've gotten me quite close (this one in particular). Butt it doesn't quite get me all the way there. Specifically I keep getting the dreaded
Expression of type 'System.Int64' cannot be used for return type 'System.Object' for value types. I thought I could get away with return dynamic here, but that seems to have fallen over. I could do a Convert, but that seems like it should be unnecessary.
My code for generating the property selector is as follows:
public static Expression<Func<T, dynamic>> GeneratePropertySelector<T>(string propertyName)
{
var objectType = typeof(T);
var property = objectType.GetProperty(propertyName);
ParameterExpression arg = Expression.Parameter(objectType, "obj");
MemberExpression propertyExpression = Expression.Property(arg, propertyName);
// Here is the line that dies
var selectorExpression = Expression.Lambda<Func<T, dynamic>>(propertyExpression, new ParameterExpression[] { arg });
return selectorExpression;
}
And then usage inside the extension method implementation would be:
var ordered = myList.OrderBy(GeneratePropertySelector("Property1"));
For a string property, this works fine; for an int property, error.
Does anyone know how to get around this? I would have thought that dynamic would have helped here, but I'm at a loss. And given my use case, I'm trying to avoid something like this:
return myList.Orderby<int>("MyIntProperty");
I have an expression tree function from a previous SO question. It basically allows the conversion of a data row into a specific class.
This code works fine, unless you're dealing with data types that can be bigger or smaller (eg. Int32/Int64).
The code throws an invalid cast exception when going from an Int64 to an Int32 when the value would fit in an Int32 (eg. numbers in the 3000).
Should I?
Attempt to fix this in the code? (If so, any pointers?)
Leave the code as it is.
private Func<SqlDataReader, T> getExpressionDelegate<T>()
{
// hang on to row[string] property
var indexerProperty = typeof(SqlDataReader).GetProperty("Item", new[] { typeof(string) });
// list of statements in our dynamic method
var statements = new List<Expression>();
// store instance for setting of properties
ParameterExpression instanceParameter = Expression.Variable(typeof(T));
ParameterExpression sqlDataReaderParameter = Expression.Parameter(typeof(SqlDataReader));
// create and assign new T to variable: var instance = new T();
BinaryExpression createInstance = Expression.Assign(instanceParameter, Expression.New(typeof(T)));
statements.Add(createInstance);
foreach (var property in typeof(T).GetProperties())
{
// instance.MyProperty
MemberExpression getProperty = Expression.Property(instanceParameter, property);
// row[property] -- NOTE: this assumes column names are the same as PropertyInfo names on T
IndexExpression readValue = Expression.MakeIndex(sqlDataReaderParameter, indexerProperty, new[] { Expression.Constant(property.Name) });
// instance.MyProperty = row[property]
BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(readValue, property.PropertyType));
statements.Add(assignProperty);
}
var returnStatement = instanceParameter;
statements.Add(returnStatement);
var body = Expression.Block(instanceParameter.Type, new[] { instanceParameter }, statements.ToArray());
var lambda = Expression.Lambda<Func<SqlDataReader, T>>(body, sqlDataReaderParameter);
// cache me!
return lambda.Compile();
}
Update:
I have now given up and decided it is not worth it. From the comments below, I got as far as:
if (readValue.Type != property.PropertyType)
{
BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(Expression.Call(property.PropertyType, "Parse", null, new Expression[] { Expression.ConvertChecked(readValue, typeof(string)) }), property.PropertyType));
statements.Add(assignProperty);
}
else
{
// instance.MyProperty = row[property]
BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(readValue, property.PropertyType));
statements.Add(assignProperty);
}
I don't think I was too far off, feel free to finish it and post the answer if you figure it out :)
You could try to fix it by "convert checked" before assigning i.e. using Expression.ConvertChecked on the value instead of Expression.Convert .
Couldn't try it right now but this should take care of the case you describe...
EDIT - as per comment this could be a boxing issue:
In this case you could try using Expression.TypeAs or Expression.Unbox for the conversion or use Expression.Call for calling a method to do the conversion... an example for using Call can be found at http://msdn.microsoft.com/en-us/library/bb349020.aspx
What you're trying to build is actually much more complicated if you want to support 100% of the primitives in .NET and SQL.
If you don't care about some of the edge cases (nullable types, enums, byte arrays, etc), two tips to get you 90% there:
Don't use the indexer on IDataRecord, it returns an object and the boxing/unboxing will kill performance. Instead, notice that IDataRecord has Get[typeName] methods on it. These exist for all .NET primitive types (note: it's GetFloat, not GetSingle, huge annoyance).
You can use IDataRecord.GetFieldType to figure out which Get method you need to call for a given column. Once you have that, you can use Expression.Convert to coerce the DB column type to the target property's type (if they're different). This will fail for some of the edge cases I listed above, for those you need custom logic.
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?
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...
I'm writing a file generic block for my application and started with using Lambda expressions for managing my rule sets for block generation to avoid the pitfalls of magic strings, configuration hell etc.
Inside my mapping class I have lines similar to:
Map(x => x.Name).Length(20).PadLeft(true).PaddingChar("#");
This works fine and isn't where my question dwells, where I setup saving my information about the expression is in the Map method:
public override IPropertyMap Map(Expression<Func<T, object>> expression)
{
var propertyMap = new FixedLengthPropertyMap
{
//Length = 20,
//PaddingCharacter = " ",
PadLeft = false,
PropertyInfo = ReflectionHelper.GetProperty(expression)
};
_properties.Add(propertyMap);
return propertyMap;
}
_properties is just a List<IPropertyMap> that stores my info where my question from what is the best way to have a real object's data be read from the properties currently I came up with something similar to this:
var map = new AgentMap();
var agent = new Agent {Name = "Bob"};
string output = map.Write(agent);
public override string Write<T>(T agent)
{
var initial = _properties[0];
return initial.PropertyInfo.GetValue(agent, null) as string;
}
Is there a better way than using the GetValue method since earlier on I'm using an expression tree?
I don't see why you really need to use expression trees at all. Just make the Map method take a Func<T, object> and store that:
public override IPropertyMap Map(Func<T, string> fetcher)
{
var propertyMap = new FixedLengthPropertyMap
{
//Length = 20,
//PaddingCharacter = " ",
PadLeft = false,
Delegate = fetcher // Delegate is of type Delegate
};
_properties.Add(propertyMap);
return propertyMap;
}
Then:
public override string Write<T>(T agent)
{
var initial = _properties[0];
Func<T, string> fetcher = (Func<T, string>) initial.Delegate;
return fetcher(agent);
}
Is there any reason you particularly wanted to know the property and use an expression tree?
In part, it depends on what your scenario is. The "simple" answer is to just compile the expression and invoke it, but that has a potential performance impact if you are doing it in a tight loop (passing a delegate would be a lot quicker).
I'm not sure whether if would apply in this particular case (because of the agent), but to avoid doing too much expression compilation, you can look for simple scenarios and read the value directly from the expression tree; a little bit of PropertyInfo/FieldInfo is going to be quicker than compiling it...
For more, look at TryEvaluate here, and how it is used with Compile as a backup strategy (although you have the advantage of a known delegate type).
HI!
Here is my case: I have some value type which is wrapped into another type with appropriate implicit converters. If I cast wrapped type to an object and then try to get original value I can do that in two-step cast only.
If simplified my code is as follows:
public enum MyEnum : int
{
First,
Second
}
public class Test<T>
{
public Test(T val)
{
Value = val;
}
private T Value { get; set; }
public static implicit operator T(Test<T> m)
{
return m.Value;
}
public static implicit operator Test<T>(T m)
{
var res = new Test<T>(m);
return res;
}
}
static void Main()
{
object res = new Test<MyEnum>(MyEnum.First);
Console.WriteLine((MyEnum)(Test<MyEnum>)res);
Console.WriteLine((MyEnum)res);
}
First "Console.WriteLine" works OK. Second one fails.
Is there any way I can modify this behavior and get it working without double casting?
UPDATE 1
I must use object to value cast (in real application I have to cast ComboBox.SelectedItem property and I do not want to add extra property to ComboBox, because I'll have to change my UI interaction code everywhere).
UPDATE 2
Implicit conversions to and from System.Object are not allowed.
UPDATE 3
Updated my sample code to reflect the whole problem.
Don't use object that way. Write your first line like this instead:
Test res = new Test(1);
If you must have it in an object first, remember that all the compiler knows about it at this point is that it's an object, and nothing more. You, as the programmer, have additional information about what you expect this object to be, but for the compiler to take advantage of that information you have to put it into your code somewhere.
Update:
I'm glad I was able to find this again, because this almost-very-timely article by Eric Lippert, who works on the C# language design, went up this morning and explains the problem in depth:
http://blogs.msdn.com/ericlippert/archive/2009/03/19/representation-and-identity.aspx
If you want to simplify casting and not care performance effect, then create extension method.
public static T To<T>(this object obj) {
Type type = obj.GetType();
MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static);
MethodInfo method = methods.FirstOrDefault(mi => (mi.Name == "op_Implicit" || mi.Name == "op_Explicit") && mi.ReturnType == typeof(T));
if (method == null)
throw new ArgumentException();
return (T)method.Invoke(null, new[] { obj });
}
Usage
Console.WriteLine(res.To<MyEnum>());
Instead of adding implicit operators, consider implementing IConvertible. You only need to implement the ToInt32 method, the others are meaningless and you can throw the InvalidCastException in the other methods.
After that, you can use Convert.ToInt32() method to convert your object in one step.
or even
var res = new Test(1);
Your local variable res is always of type object; so the line that isn't working is trying to convert an object, that isn't an int, to an int, which can't be done. Same as this fails:
object d = 5.5d;
Console.WriteLine((int)d);
EDIT:
Perhaps a pattern that might help is something like this:
if (res.GetType() == typeof(Test))
{
Console.WriteLine((int)(Test)res);
}
else
{
Console.WriteLine((int)res);
}
It's a very localized solution to your problem, but perhaps it will work for you.
While the error is due to res being of type object, I would make the Test->int operator explicit...