I am building a section of an application that revolves around pulling information about transactions out of the database. Due to the nature of the data, there are many columns in the table that I want to filter on. I have a filter selection box with 15 fields that I want to be able to build up a where clause for the LINQ statement. The interesting part comes when I want certain fields to be null. For example I want to be able to filter on any or all of:
Transaction Type
Response Code
Transaction Amount
Many more
I can build up a predicate that looks like
Func<Transaction, bool> pred = t => t.ResponseCode == ResponseCode && t.TransactionType == TransactionType && t.TransactionAmount > 100.00;
But in order to be able to choose which fields to include in the predicate I am concatenating the predicates together:
Func<Transaction, bool> pred = t => true;
if(ResponseCode != null)
pred.AndAlso(t => t.ResponseCode == ResponseCode);
// Rinse and repeat
And then passing that predicate to the where clause of the LINQ statement.
This works exactly the way I want it, but is rather complicated. Are there any other ways of doing this?
UPDATE:
Thanks Justice for the comments. I'm not using LINQ to SQL, I'm using LINQ on a collection of objects from a repository. How would you programatically build an Expression filter?
In dynamic SQL... Since you only have one WHERE clause - you must concatenate predicates with AND.
In linq query construction... you get as many WHERE clauses as you want. Linq will AND them together for you when it translates the query.
Example:
IQueryable<Transaction> query = db.Transactions;
if (filterByTransactionType)
{
query = query.Where(t => t.TransactionType == theTransactionType);
}
if (filterByResponseCode)
{
query = query.Where(t => t.ResponseCode == theResponseCode);
}
if (filterByAmount)
{
query = query.Where(t => t.TransactionAmount > theAmount);
}
Another Example:
List<Expression<Func<Transaction, bool>>> filters = GetFilterExpressions();
IQueryable<Transaction> query = db.Transactions;
filters.ForEach(f => query = query.Where(f));
First, you would need to use Expression<Func<Transaction, bool>> for LINQ-to-SQL (that's what you're trying to use, and it's not the same thing as LINQ).
Second, you can programmatically build up an Expression<Func<Transaction, bool>> using the System.Linq.Expression namespace.
You will not be able to use LINQ per se to query the database using programmatically built-up expressions. Instead of using the query operators, you will need to use the query extension methods: for example, instead of from p in db.People where p.Age > 50 select p.Name you will need to use db.People.Where(p => p.Age > 50). You can use this style to add filters: db.People.Where(myFilter), where myFilter = new Expression<Func<Person, bool>>(p => p.Age > 50). In your case, myFilter would be your programmatically built-up filter, not one created using lambda-expression syntax.
Related
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 a simple scenario.I want to list out all the employees except the logged in user.
Similar SQL Condition is
select * from employee where id not in(_loggedUserId)
How can I acheive the above using LINQ.I have tried the following query but not getting the desired list
int _loggedUserId = Convert.ToInt32(Session["LoggedUserId"]);
List<int> _empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id)
.Except(_loggedUserId)
.ToList();
Except expects argument of type IEnumerable<T>, not T, so it should be something like
_empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id)
.Except(new[] {_loggedUserId})
.ToList();
Also note, this is really redundant in the case when exclusion list contains only one item and can be replaces with something like .Where(x => x != _loggedUserId)
Why not use a very simple Where condition?
_empIds = _cmn.GetEmployeeCenterWise(_loggedUserId).Where(e=>e.Id != _loggedUserId).ToList();
The title of your question is how to perform a not in query against a database using LINQ. However, as others have pointed out your specific problem is better solved by a using users.Where(user => user.Id != loggedInUserId).
But there is still an answer on how to perform a query against a database using LINQ that results in NOT IN SQL being generated:
var userIdsToFilter = new[] { ... };
var filteredUsers = users.Where(user => !userIdsToFilter.Contains(user.Id));
That should generate the desired SQL using either Entity Framework or LINQ to SQL.
Entity Framework also allows you to use Except but then you will have to project the sequence to ID's before filtering them and if you need to original rows you need to fetch them again from the filtered sequence of ID's. So my advice is use Where with a Contains in the predicate.
Use LINQ without filtering. This will make your query execute much faster:
List<int> _empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id).ToList();
Now use List.Remove() to remove the logged-in user.
_empIds.Remove(_loggedUserId);
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.
Say I take an arbitrary LINQ to SQL query's Expression, is it possible to invoke it somehow?
MyContext ctx1 = new MyContext("...");
var q = from t in ctx1.table1 where t.id = 1 select t;
Expression qe = q.Expression;
var res = Expression.Invoke(qe);
This throws:
ArgumentException "Expression of type System.Linq.IQueryable`1[...]' cannot be invoked".
My ultimate goal is to evaluate the same query on several different data contexts.
Queries are not Expressions. A Query has an ExpressionTree.
Queries are not Methods to be invoked.
Queries may be Enumerated, yielding their results. This code will Enumerate any IQueryable:
List<object> result = query.Cast<object>().ToList();
My ultimate goal is to evaluate the
same query on several different data
contexts.
Then you should write your queries as query generators, that accept DataContext as a parameter.
Func<MyDataContext, IQueryable<Customer>> queryGen =
(dc) => dc.Customers.Where(c => c.Name == "Bob");
//now we can get some queries
IQueryable<Customer> query1 = queryGen(new MyDataContext());
IQueryable<Customer> query2 = queryGen(new MyDataContext());
If you goal is to run an Expression across different contexts, why not create just the expression like so:-
Expression<Func<MyClass, bool>> myExpression = x => x.id == 1;
Then you can do whatever you like with it, including using it in .Where() clauses.
LINQ to SQL expressions are parsed by the LINQ to SQL Provider and converted into T-SQL. My guess is that exception is being raised explicitly - that Microsoft did not intend for those expressions to be Invoked directly (you could confirm this using .NET Reflector.)
I have the need to construct a LINQ To SQL statement at runtime based on input from a user and I can't seem to figure out how to dynamically build the WHERE clause.
I have no problem with the following:
string Filters = "<value>FOO</value>";
Where("FormattedMessage.Contains(#0)",Filters)
But what I really need is to make the entire WHERE clause dynamic. This way I can add multiple conditions at runtime like this (rough idea):
foreach (Filter filter in filterlist)
{
whereclause = whereclause + "&& formattedmessage.contains(filter)";
}
I don't know what data types are being used here, but why don't you try to use general query?
var query = context.Messages
.AsQueryable();
foreach (Filter filter in filterlist)
{
query = query
.Where(m => m.Contains(filter));
}
this will concatenate all the conditions using AND (as is in your question).
You may also consider using the PredicateBuilder class. Using that will allow you to dynamically add AND/OR conditions to your tree.
Refer to http://www.albahari.com/nutshell/predicatebuilder.aspx