I am building a service that retrieves Enum-like instances from various repositories. The repositories store the data differently, but all can be retrieved into an EnumEntry { int Id; string Name; } class.
My idea is to use a base class that takes Func delegates as selectors to identify the Id and Name fields on each underlying repository record. e.g.
// constructor:
BaseEnumService<T>(Table<T> table,
Expression<Func<T, int>> idSelector,
Expression<Func<T, string>> nameSelector) {... }
// usage
var caseCategoryService = new BaseEnumService(CaseCategoriesTable, x => x.id, x => x.category)
So far so good.
To retrieve an EnumEntry, given that idSelector and nameSelector are Expressions of pure functions, I thought I could use a lambda expression like this and the compiler would figure it all out:
table.Select(x => new EnumEntry() { Id = idSelector(x), Name = nameSelector(x) }).ToList();
...but the database IQueryable implementation complains as it does not like 'external' functions.
Combining everything with expression trees works ok, e.g.
var record = Expression.Parameter(typeof(T));
Expression<Func<int, string, EnumEntry>> createEnumExpression = (id, name) => new EnumEntry() { Id = id, Name = name };
var selectExpression = Expression.Invoke(createEnumExpression,
Expression.Invoke(idSelector, record),
Expression.Invoke(nameSelector, record));
return table.Select(Expression.Lambda<Func<T, EnumEntry>>(selectExpression, record));
.. but this seems rather verbose and ugly for something so simple.
Is there no way to combine Expression<>s using lambda syntax in the way I originally tried?
Related
Before try to use a variable (this has sql server do the calculation):
IQueryable<MyEntity> query = _dbSet; // IQueryable<TEntity>
var results = query.Select(m => new MyViewModel
{
MyCalculation = m.Column1 * m.Column2
}).ToList();
What I want to do (dynamically create part of my select statement from a Func variable or some other kind of variable to allow this):
IQueryable<MyEntity> query = _dbSet; // IQueryable<TEntity>
Func<MyEntity, decimal> funcVariableAttempt = m => m.Column1 * m.Column2;
var results = query.Select(m => new MyViewModel
{
MyCalculation = funcVariableAttempt.Invoke(m) // My foolish attempt does not work.
}).ToList();
The error I get when I try what I want (aka my foolish attempt):
LINQ to Entities does not recognize the method 'System.Decimal Invoke(MyProject.Repository.Models.MyEntity)' method, and this method cannot be translated into a store expression.
How do I define and utilize a variable (maybe a Func variable) to define part of a Select statement?
It's a completely valid question that I stumbeled across earlier and found a solution that works well for me.
The problem with your Func<MyEntity, decimal> is that it is a delegate and that the O/R mapper has to have access to the internal expression (the multiplication of two properties in your case). But this information is compiled into the delegate and hidden forever.
If you however start off with a Expression<Func<MyEntity, decimal>> customCalculation, things look more promising as you have the internal logic as an expression tree.
Problem with this is: you cannot invoke an expression. customCalculation(m) doesn't compile.
The compiler would let you write
m => new MyViewModel { MyCalculation = customCalculation.Compile()(m) }
, but this wouldn't be understood by most O/R mappers.
But you see that it at least somehow contains the customCalculation lambda expression and also how it relates to its surrounding expressions.
Getting from here to the expression tree as in your original working version involves some expression manipulation:
We have to replace customCalculation.Compile()(m) with the body of the lambda that is to be Compile()d, but with the lambda's parameter(s) replaced with the respective expression(s) of the delegate invocation. So if customCalculation were x => x.Column1 * x.Column2, customCalculation.Compile()(m) would have to be replaced with m.Column1 * m.Column2
Doing so is not trivial, since the lambda itself has to be dug out of a field inside an instance of a compiler generated closure class.
I've posted my implementation of this expression manipulator in another similar question . Hope that helps.
With that, you should be able to:
var customCalculation = (Expression<Func<MyEntity, decimal>>)(x => x.Column1 * x.Column2);
var selector = Express.Prepare((Expression<Func<MyEntity, MyViewModel>>)(m => new MyViewModel { MyCalculation = customCalculation.Compile()(m) }));
var result = query.Select(selector).ToList();
As you already know, your funcVariableAttempt makes no sense to your database, so you have to call your method in the linq-to-object context. i.e. for instance first fetch the data as an Enumerable, then call your method:
var results = query.Select(m => new {
Column1= col1,
Column2= col2
}).AsEnumerable()
.Select(m => new MyViewModel
{
MyCalculation = Foo(m.Column1, m.Column2)
});
*Note: Code is not tested.
You should call ToList() first and perform the Select() on the result in memory.
var results = query.ToList()
.Select(m => new MyViewModel {
MyCalculation = Foo(m.Column1, m.Column2)
});
You're trying to perform the Select as part of the query. You should just use a regular function for the mapping calculation. Lambda functions are useful with LINQ but in this case they're not needed.
I'm using generics because I need a lot of reusability with different types of data. my main problem is querying data. I'm looking for a way to query something like this:
public void test<T>(int id, T type) where T : class
{
using (var ctx = myDbContext())
{
var myTbl = ctx.Set<T>();
//this line gets the primary key of the table
string key = myTbl.GetPrimaryKey(ctx);
//this is the query I want:
var myResult = myTbl.FirstOrDefault(x => x.key == id);
//let's say if key = "UserId", then (x => x.UserId == id) or something that translates to this.
}
}
also I have implemented following method:
public object GetPropertyValue(object src, string propertyName)
that I can use to get value of a specific property.
but my problem is that I can't use it inside the .FirstOrDefault() call because of the LINQ to query issues with methods.
I currently use this code instead:
var myResult = myTbl.ToList().FirstOrDefault(x => (int)GetPropertyValue(x, key) == id);
which is fine with a few number of rows in database, but when data grows in future it will have a lot of performance impact.
P.S: I'm using EF power tools for reverse engineering code first
Umm, your code sample is completely unclear. Please, atleast provide correct variable names, because now I don't even know where you use 'key' variable.
Also, if you want to store and query objects and it's properties of various inheritance and nestings, consider to use NoSQL databases instead of relation based SQL engines.
First -> Dont use myTbl.ToList().FirstOrDefault(x => (int)GetPropertyValue(x, key) == id) because it generates a select that brings all rows from that table, then filter by id in memory. You should translate your filter to an Expression> that will generare a select filtered by the Id column.
Build your linq expression like this:
var x = Expression.Parameter(typeof(T), "x");
string keyPropName = type.GetPrimaryKey(ctx);
var equalExp = Expression.Equal(
Expression.Property(x, keyPropName),
Expression.Constant(id)
);
var lambda = Expression.Lambda<Func<T, bool>>(equalExp, x); //x => x.Id == idValue
var myResult = myTbl.FirstOrDefault(lambda);
I used your GetPropertyValue and GetPrimaryKey methods.
I have been referring this post to group by using expression tree. Here is my code:
String[] fields = { "DepartmentID", "SkillID" };
var groupLambda = GroupByExpression<Person>(fields);
var query = dbContext.People.GroupBy(groupLambda.Compile());
var queryResult = query.ToList();
Here is the method GroupByExpression which uses solution given in aforesaid post (Thanks Daniel!):
public static Expression<Func<TItem, object>> GroupByExpression<TItem>(string[] propertyNames)
{
var properties = propertyNames.Select(name => typeof(TItem).GetProperty(name)).ToArray();
var propertyTypes = properties.Select(p => p.PropertyType).ToArray();
var tupleTypeDefinition = typeof(Tuple).Assembly.GetType("System.Tuple`" + properties.Length);
var tupleType = tupleTypeDefinition.MakeGenericType(propertyTypes);
var constructor = tupleType.GetConstructor(propertyTypes);
var param = Expression.Parameter(typeof(TItem), "x");
var body = Expression.New(constructor, properties.Select(p => Expression.Property(param, p)));
var expr = Expression.Lambda<Func<TItem, object>>(body, param);
return expr;
}
I want to be able to identify fields in the group by keys with strong names in select part like query.Select(x => new { x.Key.DepartmentID, x.Key.SkillID });
How do I do this?
Now... I won't give you the solution to the question you asked, but I'll try to help you :-)
If you want to do dynamic queries, you should probably use DynamicLinq
With DynamicLinq you can do things like:
IQueryable query = context.YourTable;
var groups = query.GroupBy("new (Field1, Field2)");
I'm rereading your question...
I want to be able to identify fields in the group by keys with strong names in select part like query.Select(x => new { x.Key.DepartmentID, x.Key.SkillID });
You can't. GroupBy in general will return a IGrouping<TKey, TSource>. TKey is dynamic (because you build it based on strings), so you can't "extract" it and pass it to the compiler, so you can't do the select with strong names.
There is a single exception: if you know the types and numbers of the GroupBy TKey then something can be done. So, you gave us:
String[] fields = { "DepartmentID", "SkillID" };
If you always have two int then you can cast your query with:
.Cast<IGrouping<Tuple<int, int>, Person>>()
.Select(x => new { x.Key.DepartmentID, x.Key.SkillID });
Note that, as I've written in a comment, your GroupBy will be executed client-side, and everything after the GroupBy will be executed client-side (where client-side == where your program is vs sql-side == where your sql server is)!
DynamicLinq will solve the problem of executing the query sql-side instead of client-side, but won't solve the problem of strong vs weak naming (after a DynamicLinq you can: A) use .Cast<>() method or B) return a dynamic object/IEnumerable<dynamic>)
The syntax you're using new { x.Key.DepartmentID, x.Key.SkillID } constructs an anonymous class at compile time. If you want to create an anonymous class at runtime, see here. However, that won't allow you to "identify fields in the group by keys with strong names". If you want to construct an anonymous class at runtime, but be able to use those names at compile time, I'm afraid that's impossible.
I have a property on a class
Expression<Func<Product, int>> predicate;
that will be assigned to different expressions throughout the application.
In a method called GetProducts I would like to retrieve Products from the DB using Entity Framework, using this predicate. I will then have a variable called myInt that I would like to use as the int parameter, that will then be assigned a value.
So I tried
dbContext.Products.Where(p => predicate(p, myInt))
but I got Non-invocable member 'predicate' cannot be used like a method. error.
It looks like I need to do some expression tree manipulation, to create a new expression tree, with myInt baked in it. How can I do this?
Thank you for your help.
You defined predicate as a Type but you are using it as a method.
You can define a predicate in the following way:
private bool predicate(Product product, int myInt)
{
//put your logic here
return true;
}
You can also use lambda expressions:
product => product.SomeValue > 5
Edit:
Expression<Func<Product, int>> predicate = (product,val) => product.SomeValue > val;
var filtered = dbContext.Products.Where(predicate);
Avoid using types when naming a parameters (i.e don't name an integer MyInt)
OK, got it.
ParameterExpression prm = Expression.Parameter(typeof(Product));
InvocationExpression inv = Expression.Invoke(predicate, prm, Expression.Constant(myInt));
var lambda = (Expression<Func<Product,bool>>) Expression.Lambda(inv, prm);
dbContext.Products.Where(lambda);
The key is Expression.Invoke that can invoke the predicate, using supplied parameters.
EDIT - After trying it, this only works with linq2sql - with Entity Framework it can't translate InvocationExpression to SQL. Instead, I used LinqKit, as follows:
dbContext.Products.AsExpandable().Where(p => predicate.Invoke(p, myInt))
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);