Expression Tree checking column value is in list of ints - c#

Just to help with the question I am trying to find a way to search a database column 'ID'for any row that has a match to a list of ints.
for example the db might have id's 1, 2, 3, 4, 5 6
and i have a list of ints { 1 , 3, 5 } and i want to find any of the ids in the list
I have so far...
//list of ints
dynamic constant = Expression.Constant(value);
List<int> ids = constant.Value;
Expression<Func<Invoice, bool>> test = (Invoice inv) => ids.Contains(inv.ID);
return Expression.Call(typeof(Enumerable), "Any", new Type[] { }, constant, test);
I then add this expression to any of the others and call
Expression.Lambda<Func<Invoice, bool>>(finalexpression, parameter);
The error I am getting is
No method 'Any' on type 'System.Linq.Enumerable' is compatible with the supplied arguments.
Any help is appreciated thanks in advance

Enumerable.Any is a generic method with one generic type argument. You should supply that argument to the Expression.Call overload used through Type[] typeArguments parameter:
return Expression.Call(
typeof(Enumerable), "Any", new Type[] { typeof(Invoice) } /* <== here */,
constant, test);
P.S. These two lines
var constant = Expression.Constant(value);
List<int> ids = constant.Value;
look suspicious (the second line does not compile at all). To make the sample working, the List<int> ids variable must be initialized somewhere else and the value variable should hold IEnumerable<Invoice> instance.

Related

cast list of anonymous type to list of object

I am trying to create a anonymous list which can hold any data type with intellisense support but without creating a class.
So I found below solution to use anonymous type.
var list = new[]
{
new { Number = 10, Name = "Smith" },
new { Number = 10, Name = "John" }
}.ToList();
foreach (var item in list)
{
Console.WriteLine(item.Name);
}
But what if I want a method which returns above anonymous type.
public List<object> GetData()
{
var list = new[]
{
new { Number = 10, Name = "Smith" },
new { Number = 10, Name = "John" }
}.ToList();
return list;
}
Compile Time Error:
Cannot implicitly convert type
'System.Collections.Generic.List<>' to 'System.Collections.Generic.List
Is it possible to cast list of anonymous type to list of object with intellisense support ?
Update:
The reason we don't want to create a type , because we just want to do some data manipulation using the anonymous type and populate some other objects that's all.
Here are the ways possible:
Return a List<object>, which means you have no intellisense on the receiving end
Return a List<dynamic>, which means you have no intellisense on the receiving end, but perhaps easier to access members you know are there than through reflection
Return a List<T> but then you will have to provide an example of how T is supposed to look and this won't be any more safe at runtime than dynamic is
Return a tuple of the new type that came with C# 7
Point 1 and 2 can be easily solved by just ensuring the list is of that type:
...
}.ToList<object>();
Point 3 is a hack but I'll post it below.
Point 4 can be solved with this syntax:
public List<(int Number, string Name)> GetData()
{
var list = new[]
{
(Number: 10, Name: "Smith"),
(Number: 10, Name: "John")
}.ToList();
return list;
}
This will give you intellisense for a while but the naming of the properties is a hack by the compiler and if you start passing these values around they will easily fall back to .Item1 and .Item2.
To cast an object to a specific type you can use a hack which only works in the same assembly that the anonymous object was created in, and that is that multiple anonymous types used around in your code, which has the same properties, in the same order, with the same property types, all end up being the same anonymous type.
You can thus cast an object to a specific anonymous type with this hackish code:
public T AnonymousCast<T>(object value, T example) => (T)value;
public IEnumerable<T> AnonymousCastAll<T>(IEnumerable<object> collection, T example) => collection.OfType<T>();
You would use it in your case like this:
var d = AnonymousCast(GetData()[0], new { Number = 0, Name = "" });
This is no more safe than using dynamic as there is no guarantee the object returned from GetData actually is of that anonymous type.
In short, use a named type.

Dynamic lambda expression for where clause

I have a dynamic list of objects on which I am using lambda expression where clause to filter items. For example, just consider that it have 3 properties, foo, bar and baz
class item // let this be current class in dynamic item list
{
bool foo;
string bar;
string baz;
}
Now if I want to filter item list where foo is false I can use following expression
var filtered = itemList.Where("!foo");
I can even filter the list by strings value as
var filtered = itemList.Where("bar==\"value\""); \\all items with bar = value
What I want to actually check is if item in list have a specific string value not null of white space. I tried following code
var filtered = itemList.Where("!String.IsNullOrWhiteSpace(baz)");
It threw an error
Expression of type 'System.Func`2[DynamicType,System.Object]' cannot
be used for parameter of type 'System.String' of method 'Boolean
IsNullOrWhiteSpace(System.String)'
Though I succeeded to get result by following query
var filtered = itemList.Where("baz!=null && baz!=\"\"");
I wanted to confirm if there is a way I can use String.IsNullOrWhiteSpace() in this query.
You can replace "!String.IsNullOrWhiteSpace(baz)" with "!(baz == null || baz.Trim() == string.Empty)" and it should work.
Have a look at System.Linq.Dynamic, there is a great example here.
You will alse need to make sure the List<T> is not List<object>, otherwise System.Linq.Dynamic will not be able to find the properties.
Here is a snippet for your example:
void Main()
{
var itemList = new List<dynamic>{ new {foo = true, bar = "a", baz = "b" }, new {foo = true, bar = (string)null, baz = "d" } };
var filtered = itemList.ToAnonymousList().Where("bar != null and bar !=\"\"");
filtered.Dump();
}
public static class EnumerableEx {
public static IList ToAnonymousList(this IEnumerable enumerable)
{
var enumerator = enumerable.GetEnumerator();
if (!enumerator.MoveNext())
throw new Exception("?? No elements??");
var value = enumerator.Current;
var returnList = (IList) typeof (List<>)
.MakeGenericType(value.GetType())
.GetConstructor(Type.EmptyTypes)
.Invoke(null);
returnList.Add(value);
while (enumerator.MoveNext())
returnList.Add(enumerator.Current);
return returnList;
}
}
I have no problem using your expression - it works fine.
I have used this with objects and entities against an EF storage.
Expression of type 'System.Func`2[DynamicType,System.Object]' cannot
be used for parameter of type 'System.String' of method 'Boolean
IsNullOrWhiteSpace(System.String)'
So looking at the error, it is stating (moving the order around):
The method IsNullOrWhiteSpace, that returns a Boolean, expects a parameter of type System.String.
But what was received was Expression of type System.Func``2[DynamicType,System.Object]'
It appears that you may have referenced an object rather than a string value for your comparison. However, without sample code for the objects you are using, and whether you are using objects, entities, or LinQ to SQL (which I haven't tried) we can only guess at what you have supplied for the expression.
Finally, what is a
'dynamic item list'?
Are you using Dynamic, or is it a normal List<Item>?

SelectMany cannot be inferred from the usage [duplicate]

This question already has an answer here:
SelectMany() Cannot Infer Type Argument -- Why Not?
(1 answer)
Closed 7 years ago.
I get the following error when I try to compile my code:
The type arguments for method
'System.Linq.Enumerable.SelectMany(System.Collections.Generic.IEnumerable,
System.Func>)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
List<RowEntry> entries = ...
List<RowArgument> arguments = ...
var argumentsVal = entries.SelectMany((RowEntry entry) =>
(IEnumerable<RowArgumentVal>)arguments.SelectMany((RowArgument arg) =>
new RowArgumentVal()
{
Action = "X"
, EntryID = entry.ID
, ArgID = arg.ID
, Val_BIT = true
, Val_DATE = new DateTime(2014, 01, 04)
, Val_DATETIME = new DateTime(2014, 01, 04)
, Val_DECIMAL = 4.6M
, Val_INT = 88
, Val_TIME = new TimeSpan(6, 0, 0)
}
).Cast<RowArgumentVal>()).Cast<RowArgumentVal>().ToList();
I don't get how I can "type" this even further...
The problem is that the inner SelectMany isn't applicable there, and you probably meant Select.
var argumentsVal = entries.SelectMany(entry =>
arguments.Select(arg => new RowArgumentVal())).ToList();
Each entry will be mapped into an IEnumerable<RowArgumentVal> according to the arguments.
Imagine the outer SelectMany was a simple Select, and it would generate List<IEnumerable<RowArgumentVal>>. But because it is SelectMany, it will "flatten" the result into a simple List<RowArgumentVal>.
The SelectMany method expects a mapping to IEnumerable<T> - not T. Your original code would be valid if RowArgumentVal just happened to implement the IEnumerable<T> interface, which I suppose isn't the case.
That seems to be a cartesian product from both lists since there is no relation between them. You may want to join them but it's not exactly clear how.
Here is a more readable and compiling way to produce a cartesian product:
var query = from entry in entries
from argument in arguments
select new RowArgumentVal
{
Action = "X", EntryID = entry.ID, ArgID = argument.ID, // ...
};
List<RowArgumentVal> argumentsVal = query.ToList();
If you want a cartesian product try doing the following
var argumentsVal = from e in entries
from a in arguments
select new RowArgumentVal(...)
As an aside, the "further typing" you could have done would be to give the type arguments to the generic method calls. In particular, if you had changed the second SelectMany to SelectMany<RowArgument, RowArgumentVal> you would have got the errors
Cannot implicitly convert type RowArgumentVal to System.Collections.Generic.IEnumerable<RowArgumentVal>. An explicit conversion exists (are you missing a cast?)
Cannot convert lambda expression to delegate type System.Func<RowArgument,int,System.Collections.Generic.IEnumerable<RowArgumentVal>> because some of the return types in the block are not implicitly convertible to the delegate return type
which would maybe have led you to the other answers here - that you were trying to call a method that expected a sequence, but you were giving it a single object.
(Or in trying to decide which type arguments to add, you would have realised what was wrong sooner.)

How to pass runtime argument variable in Expression.Call? [duplicate]

This question already has answers here:
Calling (params object[]) with Expression[]
(2 answers)
Closed 9 years ago.
I'm missing something trivial here. Say I have a method like this:
abstract class C
{
public static void M(Type t, params int[] i)
{
}
}
I'm learning expression trees and I need to build a delegate that calls this method with some predefined arguments. The problem is that I don't know to choose the correct overload and pass arguments of Expression.Call.
I want to achieve this:
//I have other overloads for M, hence I need to specify the type of arugments
var methodInfo = typeof(C).GetMethod("M", new Type[] { typeof(Type), typeof(int[]) });
//this is the first argument to method M, not sure if I have chosen the right expression
var typeArgumentExp = Expression.Parameter(someType);
var intArrayArgumentExp = Enumerable.Repeat(Expression.Constant(0), 3);
var combinedArgumentsExp = new Expression[] { typeArgumentExp }.Concat(intArrayArgumentExp);
var call = Expression.Call(methodInfo, combinedArgumentsExp);
At the Expression.Call line I get:
An unhandled exception of type 'System.ArgumentException' occurred in
System.Core.dll
Additional information: Incorrect number of arguments supplied for
call to method 'Void M(System.Type, Int32[])'
Where have I gone wrong?
The params keyword does not do anything at runtime. When you call C.M(t, 1, 2, 3), the compiler transforms this to C.M(t, new int[] { 1, 2, 3 }). In this case, you are performing parts of the job of the compiler, and this is one transformation that becomes your responsibility. You should explicitly create the array, and call C.M with exactly two arguments.
I solved it like this (shown here Calling (params object[]) with Expression[]):
//I have other overloads for M, hence I need to specify the type of arugments
var methodInfo = typeof(C).GetMethod("M", new Type[] { typeof(Type), typeof(int[]) });
//I fixed this issue where the first argument should be typeof(Type)
var typeArgumentExp = Expression.Parameter(typeof(Type));
var intArrayArgumentExp = Expression.NewArrayInit(typeof(int), Enumerable.Repeat(Expression.Constant(0), 3));
var combinedArgumentsExp = new Expression[] { typeArgumentExp }.Concat(intArrayArgumentExp);
var call = Expression.Call(methodInfo, combinedArgumentsExp);
Expression.NewArrayInit does the trick. Thanks hvd for his direction.

Dynamic LINQ, Select function, works on Enumerable, but not Queryable

I have been fiddling with dynamic LINQ for some time now, but I have yet to learn its secrets.
I have an expression that I want to parse that looks like this:
"document.LineItems.Select(i => i.Credit).Sum();"
During parsing of this I reach a point where I need to call a Select function on LineItems Collection. I am using factory method of Expression.Call:
Expression.Call(
typeof(Queryable),
"Select",
new Type[] { typeof(LineItem), typeof(decimal?) },
expr,
Expression.Lambda(expression, new ParameterExpression[] { Expression.Parameter(typeof(LineItem) }));
At this moment
expr: document.LineItems [ICollection<LineItem>]
expression: LineItem.Credit [decimal?]
none of which is materialized yet. I am just building Expression Tree at the moment.
Now, the problem:
This Expresssion.Call throws and Exception: "No generic method 'Select' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments";
I resolve it easily by looking for 'Select' in 'System.Linq.Enumerable' instead of 'Queryable' by changing first argument of Expression.Call.
But, that's not quite that I want. I don't want all LineItems hauled in only to calculate Sum(), which would obviously be case with Enumerable. I want Queryable to work.
Also, for the last part of parsing - Sum(), I also need to go with Enumerable Sum(), because Queryable Sum() throws same Exception.
I have checked MSDN, both signatures of 'Select' function are identical, so i really cannot see why would one work and other not.
Any help or pointers would be aprreciated.
Regards,
The problem is that LineItems is an ICollection<LineItem>, while the first parameter to Queryable.Select requires an IQueryable<LineItem>. ICollection<T> only implements IEnumerable<T> which is why it works for Enumerable.Select.
You will need to change the type of expr to be IQueryable<LineItem>.
You should be able to do this with the Queryable.AsQueryable method. The following function creates an expression to sum the credit property values for a given Document instance:
public static Expression<Func<decimal?>> CreateSumLineItemsExpr(Document document)
{
var docExpr = Expression.Constant(document);
var itemsExpr = Expression.Property(docExpr, "LineItems");
Expression<Func<LineItem, decimal?>> selector = i => i.Credit;
var queryableExpr = Expression.Call(typeof(Queryable), "AsQueryable", new[] { typeof(LineItem) }, itemsExpr);
var selectExpr = Expression.Call(typeof(Queryable), "Select", new[] { typeof(LineItem), typeof(decimal?) }, queryableExpr, selector);
var sumExpr = Expression.Call(typeof(Queryable), "Sum", null, selectExpr);
return Expression.Lambda<Func<decimal?>>(sumExpr);
}

Categories