I have the following code:
public OTestTable GetTestCode(Func<TestTable, bool> whereClause)
{
return CoreContext.TestTables.Where(whereClause).Select(TestTableMap.DataToObject).FirstOrDefault();
}
CoreContext is my data context (which is initialized in a base class)
My TestTableMap is as follows:
public class TestTableMap
{
public static readonly Func<TestTable, OTestTable> DataToObject = mapper =>
new OTestTable
{
Code = mapper.mycode
};
}
Then in my business method i have the following:
public OTestTable GetTestCode(string code)
{
return QueryEngine.GetTestCode(id => id.mycode == code);
}
From my main program, i am calling GetTestCode with a string value.
When I watch SQL profiler, I get the following:
SELECT [t0].[mycode]
FROM [dbo].[TestTable] AS [t0]
It does not have the where clause appended to the SQL query. If i add the where clause to the LINQ as var query = from c in DataContext.TestTable where c.mycode == '' select c;
It will add the where clause.
However, when I run my code, it will return the correct record, but it seems like I am pulling back all records from the database and filtering in my code (which should not happen).
Any thoughts with what I am doing wrong?
Thanks
In order to construct SQL statements, LINQ to SQL requires an expression tree. Func<TestTable, bool> does not represent an expression tree, it is a "black box" function pointer. LINQ cannot do anything intelligent with this apart from blindly execute it on an in-memory collection.
You need to do this instead:
public OTestTable GetTestCode(Expression<Func<TestTable, bool>> whereClause) {
return CoreContext.TestTables.Where(whereClause).Select(TestTableMap.DataToObject).FirstOrDefault();
}
This code compiles using the Queryable.Where extension method, which does accept an expression tree, rather than the Enumerable.Where extension method, which only accepts a raw delegate.
Try creating your where clause as:
Expression<Func<T, bool>> whereClause
Where the T parameter is your source type Table<T> source
Also see the PredicateBuilder here: http://www.albahari.com/nutshell/predicatebuilder.aspx
It provides you convenient extension methods to predicate IQueryable<T>. like this:
var predicate = PredicateBuilder.True<Family>();
predicate = predicate.And(o => o.Birthday < new DateTime(1980, 1, 1));
.Or(o => o.Name.Contains("ke"));
var result = Source.Where(predicate).ToList();
Related
I've an extension method, which returns an IQueryable, to get company products, I just want to use it in a IQueryable as a subquery,
public static class DBEntitiesCompanyExtensions {
public static IQueryable<Product> GetCompanyProducts(this DBEntities db, int companyId)
{
return db.Products.Where(m => m.CompanyId == companyId);
}
}
And this is how I call it,
using(var db = new DBEntities()) {
var query = db.Companies.Select(m => new {
CompanyName = m.Name,
NumberOfProducts = db.GetCompanyProducts(m.CompanyId).Count()
});
}
I expected it to works beacuse my extension methods returns an IQueryable, so it could be used in a IQueryable, am I wrong?
This is what I get, Is that possible to make it work?
System.NotSupportedException: LINQ to Entities does not recognize the
method 'System.Linq.IQueryable`1[WebProject.Models.Company]
GetCompanyProducts(WebProject.Models.DBEntities, Int32)'
method, and this method cannot be translated into a store expression.
Problem is not IQueryable inside IQueryable, because you can include subqueries just not the way you did.
In your example whole Select is represented as expression tree. In that expression tree there is something like :
CALL method DBEntitiesCompanyExtensions.GetCompanyProducts
Now EF should somehow traslate this into SQL SELECT statement. It cannot do that, because it cannot "look inside" GetCompanyProducts method and see what is going on there. Nor can it execute this method and do anything with it's result. The fact it returns IQueryable does not help and is not related.
Instead of using IQueryable you should create an expression predicate and use inside the IQueryable object that is connected to the data source
the object looks like that:
Expression<Func<Person, bool>> predicate = x => x.Name == "Adi";
var data = await queryable.Where(predicate).ToListAsync();
This is not about the reuse of a result but more the statement itself.
Nor is it about an error when using var as mentioned in: LINQ to SQL: Reuse lambda expression
Out of sheer curiosity I was wondering if it is possible to reuse a single LINQ statement.
Lets say I have the following LINQ statement:
.Where(x => x.Contains(""));
Is it possible to extract the statement x => x.Contains("") and use some kind of reference to this for later usage in, lets say, another class?
So I can call it like: .Where(previouslySavedStatement);
You can store it in a variable. If you are working with IQueryable then use:
System.Linq.Expressions.Expression<Func<Foo, bool>> selector = x => x.Contains("");
If you are using IEnumerable then use:
Func<Foo, bool> selector = x => x.Contains("");
And use it in your query:
query.Where(selector);
Yes, you can write a function containing the query you want to reuse, which takes and returns an IQueryable<T>
public IQueryable<T> ContainsEmpty(IQueryable<T> query)
{
return query.Where(x => x.Contains(""));
}
Now you can reuse it:
query1 = ContainsEmpty(query1);
query2 = ContainsEmpty(another);
It depends. There's two Where methods, Enumerable.Where and Queryable.Where. If you're applying the .Where to an IEnumerable than the first one is called, if you're applying it to an IQueryable the second one is called.
Since Enumerable.Where takes in a Func, it isn't reusable. Since Queryable.Where takes in an expression, it is reusable. You can do so as follows:
var x = new List<string>().AsQueryable();
var query = x.Where (n => n.Contains("some string"));
//Extract the lambda clause
var expr = query.Expression;
var methodExpr = (MethodCallExpression)expr;
var quoteExpr = (UnaryExpression)methodExpr.Arguments[1];
var funcExpr = (Expression<Func<string, bool>>)quoteExpr.Operand;
You can then later re-apply the where expression:
var query2 = x.Where(funcExpr);
I wrote a library to address exactly this concern, it's called CLinq and you can find an implementation for the EntityFramework here: https://www.nuget.org/packages/CLinq.EntityFramework
It allows to create query snippets and use them everywhere you in a linq query. Following the example of Hamid, create the following expression:
System.Linq.Expressions.Expression<Func<Foo, bool>> selector = x => x.Contains("");
You can now use this query everywhere in your linq queries like this:
query.AsComposable().Where(o => selector.Pass(o));
Additionally to this simple example you're also able to combine your query snippets:
query.AsComposable().Where(o => selector.Pass(o) || anotherSelector.Pass(o));
or even merge them together:
query.AsComposable().Where(o => anotherSelector.Pass(selector.Pass(o)));
There's some more features, but I think it's really helpful, so check it out :)
I have users searching records of type Record. They type a search term in a textbox and then I search records by matching several fields with the search term.
My query looks like:
var results = from record in DataContext.Records
where
record.Field1.ToLower().Contains(term) ||
record.Field2.ToLower().Contains(term) ||
record.Field3.ToLower().Contains(term)
select record;
I have a number of queries that all use the same filter and thus I would like to extract the filtering so it can be reused. Something like:
var filter = new Func<Record, string, bool>(
(record, term) =>
record.Field1.ToLower().Contains(term) ||
record.Field2.ToLower().Contains(term) ||
record.Field3.ToLower().Contains(term)
);
var results = from record in DataContext.Records
where filter(record, term)
select record;
However, it does not work because:
Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL.
How can I reuse my where condition across queries?
You need to build an expression instead of a function:
Expression<Func<Record, bool>> filter =
record => record.Field1.ToLower().Contains(term); // rest omitted
The lambda expression remains the same, but you need to return it into a variable of type Expression<Func<Record, bool>> -- that will make the C# compiler compile it as an expression instead of a delegate, allowing it to be passed to LINQ to SQL.
However, you won't be able to use an expression variable with a C#-syntax where clause: you'll need to use the Where extension method:
var results = DataContext.Records.Where(filter);
Edited to add: If you want to be able to create filters on different terms, you just need a method to produce an expression from a term:
private static Expression<Func<Record, bool>> Filter(string term)
{
return r => r.Field1.ToLower().Contains(term);
}
var results = DataContext.Records.Where(Filter(term));
If you prefer to keep filter as a lambda as you have at the moment, you can do so, but the generics get a bit nested:
Func<string, Expression<Func<Record, bool>>> filter =
term => (r => r.Field1.ToLower().Contains(term));
var results = DataContext.Records.Where(filter(term));
Regardless, the important thing is that what goes in the Where clause must be an Expression<Func<Record, bool>> -- but as shown above you can make the expression depend on term by building a suitable expression on the fly. Which is exactly what LINQ to SQL would be doing if you spelled out the filter longhand in the Where clause.
Use a CompiledQuery!
var filter = CompiledQuery.Compile(
(DatabaseDataContext dc, Record record, string term) =>
record.Field1.ToLower().Contains(term) ||
record.Field2.ToLower().Contains(term) ||
record.Field3.ToLower().Contains(term)
);
var results = from record in DataContext.Records
where filter(DataContext, record, term)
select record;
For more information, see How to: Store and Reuse Queries.
In addition to the Expression<Func<Record, bool>> issue that others have pointed out, I suggest looking into PredicateBuilder. It's very good for dynamically combining lambda expressions.
I think you need to make it an Expression<Func<Record, bool>>. Otherwise it's trying to translate the actual C# method call to SQL rather than the description of it. This is not a guarantee that this version will work; I'm not sure which string functions are translatable to SQL.
I have a query that needs to be reused all over the place and I need to vary which property/column gets used for a join.
What I'd like to be able to do is something like:
query = RestrictByProp(query, x=>x.ID);
An extremely simplified RestrictByProp() could be*:
private static IQueryable<Role> RestrictByProp(IQueryable<Role> query,
Func<Role, int> selector)
{
return query.Where(x => selector(x) == 1);
}
The problem is that even this simple implementation causes a runtime exception:
Method 'System.Object DynamicInvoke(System.Object[])' has no
supported translation to SQL.
**(Here I'm just adding a simple 'where' clause - in my real code I'd be using the lambda to pick which property to use for a join).*
I find this strange because if the member access lambda is done inline it is fine:
private static IQueryable<Role> RestrictByID(IQueryable<Role> query)
{
return query.Where(x=> x.ID == 1);
}
LINQ to SQL is also happy if you pass in an Expression<Func<Role, bool>> (i.e. when the parameter is x=>x.ID == 1) but that defeats the object because I need the value of the right-hand operand to be determined within the query.
Is there a way to somehow munge the lambda expression in RestrictByProp() so that LINQ to SQL knows how to generate the SQL?
First, you need to change your method signature:
private static IQueryable<Role> RestrictByProp(IQueryable<Role> query,
Expression<Func<Role, int>> selector)
That will mean your lambda expression is converted into an expression tree instead of a delegate.
You'll then need to build an Expression<Func<Role, bool>> from the existing expression tree.
It will look something like this:
LambdaExpression lambda = (LambdaExpression) selector;
var predicate = Expression.Equal(selector, Expression.Constant(1));
var lambdaPredicate = Expression.Lambda<Func<Role, bool>>(predicate,
lambda.Parameters);
return query.Where(lambdaPredicate);
Is it possible to pass parts of a linq Query into a function?
I want create a common interface for my DAL that always uses the same query interface. For example,
List<T> Get(Join j, Where w, Select s){
return currentDataContext<T>.Join(j).Where(w).Select(s).ToList();
}
Is this sort of thing possible? I'm thinking it would be done with expression trees, but I haven't been able to find examples of it.
Well, the "join" is tricky, because it is very hard to express a join - but things like where / select / orderby are pretty easy...
Really, it is just a case of combining the various LINQ methods on IQueryable<T>, which generally accept Expression<Func<...>> for some combination. So a basic select with an optional predicate would be:
public IQueryable<T> Get<T>(
Expression<Func<T,bool>> predicate
) where T : class
{
IQueryable<T> query = (IQueryable<T>)GetTable(typeof(T));
if (predicate != null) query = query.Where(predicate);
return query;
}
I would tend to return IQueryable<T> too, since that is fully composable. If the caller wants a list, they can always use ToList() on it... or (for example):
using(var ctx = new MyDataContext(CONN))
{
ctx.Log = Console.Out;
int frCount = ctx.Get<Customer>(c => c.Country == "France").Count();
}
which (using Northwind) does the query:
SELECT COUNT(*) AS [value]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[Country] = #p0
The problem with including the "select" (projection) in the query is that you would end up with multiple generic types. Since you often want the projection the be an anonymous type, it would then be pretty impossible to specify the projection type (anonymous) and the table-type, and it would not be callable.
In reality, I wonder if there is much benefit writing such a method at all. I might just stick with a base method:
public IQueryable<T> Get<T>() where T : class
{
return (IQueryable<T>)GetTable(typeof(T));
}
And let the caller compose it in their preferred way - perhaps with query syntax:
var list = (from cust in ctx.Get<Customer>()
where cust.Country == "France"
select cust.CompanyName).Take(10).ToList();
Which uses:
SELECT TOP (10) [t0].[CompanyName]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[Country] = #p0
Alternatively, if you really do want to include the order by and projection, then an extension method is the most practical approach; then you don't need to specify the original (source) T (which is what makes it uncallable when mixed with anon-types):
public static class QueryExtension
{
public static IQueryable<TProjection>
Get<TSource, TProjection, TOrderKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> where, // optional
Expression<Func<TSource, TProjection>> select,
Expression<Func<TProjection, TOrderKey>> orderBy)
{
if (where != null) source = source.Where(where);
return source.Select(select).OrderBy(orderBy);
}
}
Then consider a DAL method such as:
public List<string> Countries()
{
return Customers.Get(
x=>x.CompanyName != "",
x=>x.Country,
x=>x).Distinct().ToList();
}
Which uses (again, with Northwind):
SELECT DISTINCT [t0].[Country]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[CompanyName] <> #p0
Check this generic class: TableView.cs.
It basically uses a Func<TEntity, bool> delegate to apply the Where predicate:
//...
public TableView(DataContext dataContext, Expression<Func<TEntity, bool>> predicate)
{
this.table = dataContext.GetTable<TEntity>();
this.baseQuery = table.Where(predicate);
this.predicate = predicate.Compile();
}
//...
You can use Dynamic Expression library available with Linq Examples , with this extension library you can pass linq clauses for where etc...
Download available from here
linq samples
You could use Dynamic LINQ and pass the parameters in as strings.
Marc Gravell♦, as usual, provided a very insightful answer, but I really think having methods that take IQueryables and add restrictions would work in most cases and they keep the code clearer and easily maintainable. For example:
//Join
public static IQueryable<IContract> AllContracts(this IQueryable<IAccount> accounts, ISession s ) {
return from a in accounts
from contract in s.Query<IContract()
where (a.Id == contract.AccountId)
select contract;
}
//Where
public static IQueryable<IContract> Active(this IQueryable<IContract> contracts) {
return from contract in contracts
where (contract.Active == true)
select contract;
}
Then you can mix and match these like this:
IQueryable<IContract> activeContracts = s.Query<IAccount>()
.Where(o => o.Name == "XXX")
.GetContracts(s)
.Active();
I'm using extension methods and NHiberante's LINQ providing Query method here, but this could easily be rewritten without static methods and with any LINQ provider.