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.
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.
For example, I have this code:
IQueryable<MyModel> q = new List<MyModel>().AsQueryable(); // this is just an example, this is obviously not a list
var query = from item in q select new { item.Property };
var oneItem = query.FirstOrDefault(x => x.SomeProperty == somevalue);
var allItems = query.ToArray();
Now in a bit more complex situation, I need to get oneItem and allItems in two different methods. So to follow DRY, i'd like to move my query to a private method and then in the consuming ones just call this.GetQuery().FirstOrDefault() or .ToArray() as required.
However, when I try to have the method as IQueryable<dynamic> I get the 'An expression tree may not contain a dynamic operation' error. If I change it to IQueryable<object> then my filtering in the oneItem doesn't work.
You need to return
IQueryable<MyObject>
You can make your methods/classes dry by using genrics eg
IQuerable<T> GetQueryable()
Then the consumer can specify what T should be and your away.
You can't use dynamic with linq. See here to understand why.
For two methods to communicate they must understand the same type so you really want to project into a named type.
If you insist on using dynamic programming it can be done but you will need a lot of casting because dynamic is not a type but just a way of treating object:
IQueryable<MyModel> q = new List<MyModel>().AsQueryable(); // this is just an example, this is obviously not a list
IQueryable<object> query = from item in q select (object)new { item.Property };
var oneItem = query.FirstOrDefault(x => ((dynamic)x).SomeProperty == somevalue);
object[] allItems = query.ToArray();
I'll start with piece of code:
var objectType = typeof(Department); // Department is entity class from linqdatacontext
using (var dataContext = new DataModel.ModelDataContext())
{
var entity = Expression.Parameter(objectType, "model");
var keyValue = Expression.Property(entity, "Id");
var pkValue = Expression.Constant(reader.Value);
var cond = Expression.Equal(keyValue, pkValue);
var table = dataContext.GetTable(objectType);
... // and here i don't how to proceed
}
I am not even sure if i am building that expression correctly. However simply put, i need to call dynamically SingleOrDefault() on that table to find entity by primary key. Every example i had found is using generic variant of GetTable<>(), but i cannot use that obviously. I am probably overlooking something...
Whenever I build expression trees, I like to start off with an example of what I'm building:
() => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == 1);
From that, we can easily dissect the target expression. You are partway there; you just need to include a call to the GetTable method in the expression tree and then build an outer lambda expression to call the whole thing:
using(var dataContext = new DataModel.ModelDataContext())
{
var getTableCall = Expression.Call(
Expression.Constant(dataContext),
"GetTable",
new[] { entityType });
var entity = Expression.Parameter(entityType, "entity");
var idCheck = Expression.Equal(
Expression.Property(entity, "Id"),
Expression.Constant(reader.Value));
var idCheckLambda = Expression.Lambda(idCheck, entity);
var singleOrDefaultCall = Expression.Call(
typeof(Queryable),
"SingleOrDefault",
new[] { entityType },
getTableCall,
Expression.Quote(idCheckLambda));
var singleOrDefaultLambda = Expression.Lambda<Func<object>>(
Expression.Convert(singleOrDefaultCall, typeof(object)));
var singleOrDefaultFunction = singleOrDefaultLambda.Compile();
return singleOrDefaultFunction();
}
We have to convert the SingleOrDefault call to have a return type of object so it can serve as the body of the Func<object> function.
(Untested)
Edit: Parameterizing the data context and value
Now we are building this function:
(dataContext, value) => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == value);
You would change the constants to parameters and add those parameters to the function you compile:
var dataContextParameter = Expression.Parameter(typeof(ModelDataContext), "dataContext");
var valueParameter = Expression.Parameter(typeof(object), "value");
var getTableCall = Expression.Call(
dataContextParameter,
"GetTable",
new[] { entityType });
var entity = Expression.Parameter(entityType, "entity");
var idCheck = Expression.Equal(
Expression.Property(entity, "Id"),
valueParameter);
var idCheckLambda = Expression.Lambda(idCheck, entity);
var singleOrDefaultCall = Expression.Call(
typeof(Queryable),
"SingleOrDefault",
new[] { entityType },
getTableCall,
Expression.Quote(idCheckLambda));
var singleOrDefaultLambda =
Expression.Lambda<Func<ModelDataContext, object, object>>(
Expression.Convert(singleOrDefaultCall, typeof(object)),
dataContextParameter,
valueParameter);
var singleOrDefaultFunction = singleOrDefaultLambda.Compile();
// Usage
using(var dataContext = new DataModel.ModelDataContext())
{
return singleOrDefaultFunction(dataContext, reader.Value);
}
If you are using .NET 4, you could try casting your returned objects as dynamic, so you could then query like this.
using (var dataContext = new DataModel.ModelDataContext())
{
var entity = Expression.Parameter(objectType, "model");
var keyValue = Expression.Property(entity, "Id");
var pkValue = Expression.Constant(reader.Value);
var cond = Expression.Equal(keyValue, pkValue);
var table = dataContext.GetTable(objectType);
var result = table.Where(ent => ((dynamic)ent).SomeField == "SomeValue");
}
I'm still not entirely sure as to the whole of your problem (and I suspect the answer about dynamic is going to solve part of what will come up too). Still, just to answer:
Every example i had found is using generic variant of GetTable<>(), but i cannot use that obviously
For any T, Table<T> implements (among other interfaces) ITable<T> and ITable. The former is generically typed, the latter not.
The form GetTable<T>() returns such a Table<T>. However, the form GetTable(Type t) returns an ITable. Since ITable inherits from IQueryable you can query it. If you need to do something with that query that would normally require knowledge of the type (such as comparing on a given property) then dynamic as per the previous answer given by Steve Danner allows that to happen.
I'd do it using Reflection and the LINQ dynamic query library, personally.
You can get the list of all the keys for a table with dataContext.Mapping.GetMetaType(objectType).IdentityMembers, then access the data with something along the lines of dataContext.GetTable(objectType).Where(key.Name + "==#0", id).
Obviously, I left out a few steps in there - if you have multiple keys, you'll need to build a fuller predicate with a loop over .IdentityMembers, and if you always just have the one key, you can use .First() on it. I haven't tested it either, but it should be pretty close. It'd probably be 6-7 lines of code total - I can write it up (and test) if you need it.
Edit: The LINQ Dynamic Query Library can be downloaded from Microsoft at http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx - just include DynamicLINQ.cs in your project and you're good.
We have a list of strings and we need to filter our results by that list. Example would be find all students who have SSNs that start with 465, 496, or 497 (plus x more)
List<string> list = GetPossibleStartsWithValues();
var qry = from student in entities.Students.WhereStartsWith(x=>x.SSN, list)
select new SearchedStudent
{
Name = student.Name,
SSN = student.SSN,
...
}
The code provided here is close to what we need, but we can't figure out how to impliment the StartsWith that we need using the Expression Class.
Well, you could try this:
public static IQueryable<T> WhereStartsWith<T>(this IQueryable<T> source,
Expression<Func<T, string>> projection,
List<T> list)
{
return source.Where(x => list.Any(y => projection(x).StartsWith(y)));
}
That may well not work, but it would be worth trying before you go into anything more complicated.
EDIT: As you say, the above won't compile - you basically need to build an expression tree representing the bit within the Where clause. Oops. However, before you start doing that, it would be worth seeing whether it'll work in the end. Try this:
List<string> list = GetPossibleStartsWithValues();
var qry = from student in entities.Students
.Where(student => list.Any(y => student.SSN.StartsWith(y)))
select new SearchedStudent
{
Name = student.Name,
SSN = student.SSN,
...
}
If that doesn't work, then making a more general method won't be any use :(
How about using a compound statement such as
var qry = from student in entities.Students.Where(
s => list.Where( x => s.StartsWith(x)).Count() != 0 )