Generic method that accepts generic list of Expression<Func> - c#

I have declared multiple of these variables, but how can I then put them into a generic list?
Expression<Func<poco, string>> fieldToUpdate1 = x => x.Name;
Expression<Func<poco, bool>> fieldToUpdate2 = x => x.Id;
Currently I can only specify one type for the generic list.
So I can either get a List<string> or List<bool>. But not both. I want to be able to have a generic list that accepts both so I can pass that list as a parameter.
Use case:
The use case I am trying to do is create a generic wrapper for the Mongo method updateOne. With the below signature. I want to create a generic wrapper that will accept two parameters. I can use these parameters to call the actual mongo implementation. Something like this:
GenericWrapper(Expression<Func<TDocument, bool>> filter, List<(Expression<Func<TDocument, TField>> expression, TField actual value)>)
The problem is that TField can only be one type. So I can only do this:
Expression<Func<Student, string>> fieldToUpdate1 = x => x.name;
Expression<Func<Student, int>> fieldToUpdate2 = x => x.testScore;
var expressions = new List<(Expression<Func<Student, int>> expression, int value)>();
var item1 = (expression: fieldToUpdate2, value: 4);
var item2 = (expression: fieldToUpdate1, value: "test");
expressions.Add(item1);
//I can't add item2 since its of a different type. I can only pass a list of the same type. And my generic wrapper function will only accept a list of one type
http://api.mongodb.com/csharp/current/html/M_MongoDB_Driver_IMongoCollectionExtensions_UpdateOne__1.htm
public static UpdateResult UpdateOne<TDocument>(
this IMongoCollection<TDocument> collection,
Expression<Func<TDocument, bool>> filter,
UpdateDefinition<TDocument> update,
UpdateOptions options = null,
CancellationToken cancellationToken = null
)
Any ideas on how to make this generic wrapper?

Since Expression<T> inherits from Expression you can put it into a List<Expression>.
List<Expression> expressions = new List<Expression>();
expressions.Add(fieldToUpdate1);
expressions.Add(fieldToUpdate2);

You can use object as the return value:
Expression<Func<poco, object>> fieldToUpdate1 = x => x.Name;
Expression<Func<poco, object>> fieldToUpdate2 = x => x.Id;
List<Expression<Func<poco, object>>> testList = new List<Expression<Func<poco, object>>>();
testList.Add(fieldToUpdate1);
testList.Add(fieldToUpdate2);
Anyway, the general design seems a bit strange, since at the end, you have to cast at least the results.

Related

C# Is there a way to resolve lambda expression`s delegate type in code

My goal is to support sorting in an application and expose it via REST API that would accept the parameter as a string.
Current implementation is along the lines of this:
GetUsers (Expression<Func<User, int>> sortExpression) {
// Retrieve users ...
return users.orderBy(sortExpression);
}
Usage example:
var users = GetUsers(u => u.Id);
the Expression<Func<User, int>> sortExpression is widely used in our repository and changing it would be difficult.
What I'd like to do is to be able to swap the u => u.Id with something that is generated during run-time.
Something that I have so far is:
// sortBy is retrieved from the calling method.
var type = typeof(User).GetProperties().FirstOrDefault(x => x.Name == sortBy).GetType();
var sortExpression = Expression.Property(Expression.Parameter(typeof(User)), sortBy);
var parameter = Expression.Parameter(typeof(User));
var expressionBody = Expression.Lambda(typeof(Func<User, int>), sortExpression, parameter);
var users = GetUsers(expressionBody)
I can see at run-time that this does create an expression that fits my needs, but the error is Argument 5: cannot convert from 'LambdaExpression' to 'Expression<System.Func<User, int>>' even though the body of the expression is supposed to be set by typeof(Func<User, int>)
I've figured out what I've been doing wrong.
First: Create the expression body using generic method
// Generic Method, return type Expression<Func<User, int>>
Expression.Lambda<Func<User, int>>(sortExpression, parameter);
Instead of passing the typeof(Func<User, int>) parameter.
// Non-generic. Return type LambdaExpression
Expression.Lambda(typeof(Func<User, int>), sortExpression, parameter);
Second:
I wasn't binding the parameter properly, which made it so that the expression was accessing property of a discarded parameter that wasn't provided to the expression.
// I'm creating an expression to access the property of a newly created parameter.
var sortExpression = Expression.Property(Expression.Parameter(typeof(User)), sortBy);
var parameter = Expression.Parameter(typeof(User));
var expressionBody = Expression.Lambda<Func<User, int>>(sortExpression, parameter);
//Above causes an unbinded variable exception since there are two parameters, one of which is not passed/bound.
//Should be:
var parameter = Expression.Parameter(typeof(User));
var sortExpression = Expression.Property(parameter, sortBy);

LINQ to Entities does not recognize the method 'Boolean Contains[Int32]

I have the following extension methods in which I am using to do a Contains on LINQ-To-Entities:
public static class Extensions
{
public static IQueryable<TEntity> WhereIn<TEntity, TValue>
(
this ObjectQuery<TEntity> query,
Expression<Func<TEntity, TValue>> selector,
IEnumerable<TValue> collection
)
{
if (selector == null) throw new ArgumentNullException("selector");
if (collection == null) throw new ArgumentNullException("collection");
if (!collection.Any())
return query.Where(t => false);
ParameterExpression p = selector.Parameters.Single();
IEnumerable<Expression> equals = collection.Select(value =>
(Expression)Expression.Equal(selector.Body,
Expression.Constant(value, typeof(TValue))));
Expression body = equals.Aggregate((accumulate, equal) =>
Expression.Or(accumulate, equal));
return query.Where(Expression.Lambda<Func<TEntity, bool>>(body, p));
}
//Optional - to allow static collection:
public static IQueryable<TEntity> WhereIn<TEntity, TValue>
(
this ObjectQuery<TEntity> query,
Expression<Func<TEntity, TValue>> selector,
params TValue[] collection
)
{
return WhereIn(query, selector, (IEnumerable<TValue>)collection);
}
}
When I call the extenion method to check if a list of ids is in a particular table, it works and I get back the List of ids, like this:
List<int> Ids = _context.Persons
.WhereIn(x => x.PersonId, PersonIds)
.Select(x => x.HeaderId).ToList();
When I execute the next statement, it complains that LINQ-To-Entities does not recogonize Contains(int32), but I thought I am not going against the entity anymore, but a collection of ints.
predicate = predicate.And(x=> Ids.Contains(x.HeaderId));
If I have a comma separated string such as "1,2,3", then the following works:
predicate = predicate.And(x=>x.Ids.Contains(x.HeaderId));
I am trying to take the List returned and create comma separated list of strings, the problem here is that now when I do predicate = predicate.And(x=>sb.Contains(x.HeaderId.ToString());, it complains that it does not like ToString().
I also tried doing:
predicate = predicate.And(x=>Extensions.WhereIn(Ids, x.id));, but it can't resolve WhereIn. It says I must add `<>`, but I am not sure what to add here and how implement it.
Where is nothing wrong with your WhereIn, and you are correct: when you use Ids, you are not going against the entity anymore, but a collection of ints.
Problem is when you're using .And on predicate: LINQ-To-Entities tries to convert everything inside those brackets into Entities methods, and there is no corresponding Contains method.
Solution:
Instead of
predicate = predicate.And(x=> Ids.Contains(x.HeaderId));
use
predicate = predicate.And(Contains<XClassName, int>(x.HeaderId));
where Contains defined as follows:
private static Expression<Func<TElement, bool>> Contains<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, List<TValue> values)
{
if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
if (null == values) { throw new ArgumentNullException("values"); }
if (!values.Any())
return e => false;
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
return Expression.Lambda<Func<TElement, bool>>(#equals.Aggregate(Expression.Or), valueSelector.Parameters.Single());
}
and XClassName is the name of the class of your x
You cant use array like that, you need to previsit this lambda in order to expand it to primitives. Alternatively you can change underlying provider so it knows how to generate IN statement , as it doesnt by default.
Didnt find post where one guys actually implement it, will updated once I did.
Basically when you use your extension method it is like
x=>arr.Contains(x)
So if you try to execute such lambda agains your entityset etc it will throw you exception saying that parameters can only be primitives.
The reason is that underlying provider doesnt know how to convert .Contains method for array as function parameter into sql query. And in order to solve that you have two options
teach it how to use T[] as parameter and use Contains with this parameter
update your extension method in order to generate new lamda which will use 'allowed' building blocks, ie expressions using primitive types like int, string, guid etc.
Check this article
http://msdn.microsoft.com/en-us/library/bb882521(v=vs.90).aspx
Replace your:
List<int> Ids = _context.Persons
.WhereIn(x => x.PersonId, PersonIds)
.Select(x => x.HeaderId).ToList();
with
var Ids = _context.Persons
.WhereIn(x => x.PersonId, PersonIds)
.Select(x => x.HeaderId).ToList();
and then try.

Linq expression, get parameters?

I got a linq expression,
internal T Execute<T>(System.Linq.Expressions.Expression<Func<int, string, T>> expr)
{
var paramInt = ??;
var paramString = ??;
}
I call this method using this:
Expression<Func<int, string, Guid>> myExpression2 = (a,b) => Callmethod(a, b, 5);
Execute<Guid>(myExpression2);
How can i get the parameters from the expression in my execute method ?
-- To clarify --
I want to get the values from the parameters so that i do some calculations with them.
Well, you can get the parameters using the Parameters property.
var parameters = expr.Parameters;
However, each of those will be a ParameterExpression. That will give you the parameter name and the type, but it's not clear what you want to do with them.

c# List<string> to Lambda Expression with starter example: Refactor to handle the List

I have this:
List<string> fields;
fields[0] = "firstFieldName";
fields[1] = "secondFieldName";
...
fields[n] = "nthFieldName";
I want to get this:
var selector = p => new {p.firstField, p.secondField, ..., p.nthFieldName}
// selector is of type Expression<Func<Entity, object>>
GoofBallLogic had this code that was simliar, ending up with p => p.column
// Entity is an object in a diagram from Entity Framework 4
var p = Expression.Parameter(typeof(Entity, "p");
var selector = Expression.Lambda<Func<Entity, string>(
Expression.Property(p, columnToGroupBy), p );
EDIT: What I am trying to accomplish
I have a "generic" Repository:
public class Repository<E, C> : IRepository<E,C>
{
private C _dc {get;set;} // ObjectContext (Entity Framework 4)
private string _entityName {get;set;}
public string entityKeyName {get;private set;}
public List<string> entityKeys {get;private set;}
public Expression<Func<E, object>> entityKey {get;private set;}
private EntityContainer _containerName {get;set;}
public Repository(C myDC)
{ _dc = myDC; // TODO: check for null
// Name of "this" ObjectContext
_containerName = _dc.MetadataWorkspace.GetEntityContainer(
_dc.DefaultContainerName, DataSpace.CSpace);
// Name of "this" Entity
_entityName = _containerName.BaseEntitySets
.Where(p => p.ElementType.Name == typeof (E).Name)
.Select( p => p.Name).FirstOrDefault();
// String list of the keys
entityKeys = _containerName
.BaseEntitySets.First(meta => meta.ElementType.Name ==
typeof(E).Name)
.ElementType.KeyMembers.Select(k => k.Name).ToList();
// Thanks Jon Skeet for this cool comma sep list formula
entityKeyName = string.Join(",", entityKeys.ToArray() );
entityKey = Expression.Lambda<Func<E, object>> ...
What to do to set entityKey as an object that can be used in
an OrderBy statement since Linq to Entities requires
ordering a set before doing a .Skip().Take()
Edit:
Amazingly, Orderby can take this:
p => "field1,field2,field3"
Which allows my code to execute but doesn't actually order the items by the field values. It's a first step in TDD I guess: use a literal.
I found this an interesting problem and took some time to figure it out, and found a relatively easy way to do it.
Anyhow, here's an example on how to do a single field sort (i'll use your first field), if you want to sort on more fields you'll have to create expressions for them too and use .ThenBy(xxx) after the usual OrderBy(xxx).
// Create a parameter which passes the object
ParameterExpression param = Expression.Parameter(typeof(E), "a");
// Create body of lambda expression
Expression body = Expression.PropertyOrField(param, fieldname);
// Create lambda function
Expression<Func<E, string>> exp = Expression.Lambda<Func<E, string>>(body, param);
// Compile it so we can use it
Func<E, string> orderFunc = exp.Compile();
Now you can do an OrderBy(orderFunc) and it'll sort the list by the property named in fieldname. Only downside being it only works for string fields (return value of expression). Could probably work around that too though.
Fixed to work with any IComparable type:
// Create a parameter which passes the field
ParameterExpression param = Expression.Parameter(typeof(E), "a");
// Create body of lambda expression
Expression body = Expression.TypeAs(Expression.PropertyOrField(param, fieldname), typeof(IComparable));
// Create lambda function
Expression<Func<E, IComparable>> exp = Expression.Lambda<Func<E, IComparable>>(body, param);
// Compile it so we can use it
Func<E, IComparable> orderFunc = exp.Compile();
You cannot do this easily because you cannot construct a new expression for a type that doesn’t exist at runtime. (You can have anonymous types in C# because the C# compiler creates the type for you.)
If you want to do it the really hard way, you could generate a dynamic assembly and actually create the type you need. There is a short example here.
I suspect that there is an easier way, though. We would need to know what your goal is (what you need this expression tree for), which you haven’t stated.
From your edited question, it appears that you just want to be able to order by multiple keys. That is easily possible simply by using .OrderBy() followed by .ThenBy(). I’m assuming that you are using an IQueryable<E> here:
IQueryable<E> query = ...;
IOrderedQueryable<E> ordered = null;
foreach (var key in entityKeys)
{
// Code from Doggett to construct the lambda expression for one step
ParameterExpression param = Expression.Parameter(typeof(E), "a");
var body = Expression.TypeAs(
Expression.PropertyOrField(param, key),
typeof(IComparable));
var exp = Expression.Lambda<Func<E, IComparable>>(body, param);
if (ordered == null)
ordered = query.OrderBy(exp);
else
ordered = ordered.ThenBy(exp);
}
var finalQuery = (ordered ?? query).Skip(n).Take(m);

Convert IQueryable<> type object to List<T> type?

I have IQueryable<> object.
I want to Convert it into List<> with selected columns like new { ID = s.ID, Name = s.Name }.
Edited
Marc you are absolutely right!
but I have only access to FindByAll() Method (because of my architecture).
And it gives me whole object in IQueryable<>
And I have strict requirement( for creating json object for select tag) to have only list<> type with two fields.
Then just Select:
var list = source.Select(s=>new { ID = s.ID, Name = s.Name }).ToList();
(edit) Actually - the names could be inferred in this case, so you could use:
var list = source.Select(s=>new { s.ID, s.Name }).ToList();
which saves a few electrons...
Add the following:
using System.Linq
...and call ToList() on the IQueryable<>.
The List class's constructor can convert an IQueryable for you:
public static List<TResult> ToList<TResult>(this IQueryable source)
{
return new List<TResult>(source);
}
or you can just convert it without the extension method, of course:
var list = new List<T>(queryable);
System.Linq has ToList() on IQueryable<> and IEnumerable<>. It will cause a full pass through the data to put it into a list, though. You loose your deferred invoke when you do this. Not a big deal if it is the consumer of the data.
Here's a couple of extension methods I've jury-rigged together to convert IQueryables and IEnumerables from one type to another (i.e. DTO). It's mainly used to convert from a larger type (i.e. the type of the row in the database that has unneeded fields) to a smaller one.
The positive sides of this approach are:
it requires almost no code to use - a simple call to .Transform<DtoType>() is all you need
it works just like .Select(s=>new{...}) i.e. when used with IQueryable it produces the optimal SQL code, excluding Type1 fields that DtoType doesn't have.
LinqHelper.cs:
public static IQueryable<TResult> Transform<TResult>(this IQueryable source)
{
var resultType = typeof(TResult);
var resultProperties = resultType.GetProperties().Where(p => p.CanWrite);
ParameterExpression s = Expression.Parameter(source.ElementType, "s");
var memberBindings =
resultProperties.Select(p =>
Expression.Bind(typeof(TResult).GetMember(p.Name)[0], Expression.Property(s, p.Name))).OfType<MemberBinding>();
Expression memberInit = Expression.MemberInit(
Expression.New(typeof(TResult)),
memberBindings
);
var memberInitLambda = Expression.Lambda(memberInit, s);
var typeArgs = new[]
{
source.ElementType,
memberInit.Type
};
var mc = Expression.Call(typeof(Queryable), "Select", typeArgs, source.Expression, memberInitLambda);
var query = source.Provider.CreateQuery<TResult>(mc);
return query;
}
public static IEnumerable<TResult> Transform<TResult>(this IEnumerable source)
{
return source.AsQueryable().Transform<TResult>();
}

Categories

Resources