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
Related
I have this already
if (options.English != null) query = query
.Where(w => w.English.Contains(options.English));
What I would like to do is to extend this (maybe with another if clause) to make it so that if:
a user enters ^abc then my query would check if the word starts with "abc".
a user abc then it would check if the column contains "abc"
I am using a SQL Server back-end database. Could anyone give me a suggestion as to how I could implement this functionality.
Assuming that you're using Entity Framework, you can use the StartsWith() and EndsWith() methods, to achieve the same results as Contains() except only at the beginning or the end of a string. It will generate the code for you.
Then simply create conditional statements in your code, in order to determine which one of the methods you should use.
Some word of advice:
There might be a bug with EF Core, in which it turns StartsWith("string") into LIKE "string%" which might yield incorrect results with strings, containing wildcard characters such as "_".
So I'd advise you to use plain SQL with EF Core, and given that you're using SQL Server as a DBMS, query like that:
if (searchText.StartsWith("^"))
{
var result = query.FromSql($"SELECT something FROM table WHERE PATINDEX({searchText.Substring(1)}, something) = 1");
}
else
{
var result = query.FromSql($"SELECT * FROM table WHERE PATINDEX({searchText.Substring(1)}, something ) <> 0");
}
With PATINDEX() you will get correct results even if your pattern string contains wildcard characters - escaping potential bugs with relying on StartsWith() and EndsWith() to generate proper SQL code.
But that's only for EF Core, EF 6 works like a charm the way other people answered :)
You can put that choice in a conditional statement:
IQueryable<Whatever> query = ...;
if (searchText.StartsWith("^"))
{
query = query.Where(w => w.English.StartsWith(searchText.Substring(1)));
}
else
{
query = query.Where(w => w.English.Contains(searchText));
}
You can also do the same comparison inline, but that'll generate very ugly SQL, if it even works:
query = query.Where(w =>
searchText.StartsWith("^")
? w.English.StartsWith(searchText.Substring(1))
: w.English.Contains(searchText));
Do note that you generally don't want to search text using SQL, as that results in a pretty poor user experience. Take a look at full-text indexing.
if (options.English != null)
{
bool englishStartsWith = options.English.StartsWith("^");
if(englishStartsWith)
{
query = query.Where(w => w.English.StartsWith(options.English.Substring(1)));
}
else
{
query = query.Where(w => w.English.Contains(options.English));
}
}
I have a table which has hundreds of columns (like FirstName and LastName).
All this data is displayed as a GridView with listing. I've made use of the SortCommand event which has the e.SortExpression, but I can't fathom how to use it in an OrderBy function since after inputting a lambda expression. IntelliSense shows me all the hundreds of columns instead of the one I need - which I only have at run-time.
How do I use the Func<TSource, TKey> selector the OrderBy functions expects from me to let it know that the string e.SortExpression is the column that needs to be sorted?
Example of what I essentially mean:
private void dataGrid_SortCommand(object source, DataGridSortCommandEventArgs e)
{
var users = new UsersEntities().UsersTable.OrderBy(x => "x."+e.SortExpression);
FillDataGrid(users);
}
Update:
I've come to understand that the way in which I'm looking to accomplish this is using Expression trees, and I'm hoping someone can provide the exact solution.
Update 2:
I've found the solution here. Turns out my question was a duplicate and to add to that, badly worded. Thanks to everyone.
And I've also just noticed that ken's offered pretty damning evidence that I should use dynamic linq. I'll consider doing so in the future, but I couldn't help but notice how fun it is to experiment with Expression trees rather than rely on libraries to do them for me. At this stage, as I'm still new to Expression trees this is a perfect opportunity to utilize them.
You can use dynamic linq That would allow you to do an OrderBy based on a string.
Edit
See my comment. Using dynamic linq, your solution could be as easy as:
var users = new UsersEntities().UsersTable.OrderBy(e.SortExpression);
You can dynamically create LINQ expressions easily as long as only logical ANDs are involved in where clauses. Simply repeat the Where clause!
// Filter
var query = new UsersEntities().UsersTable.Select(x => x);
if (!String.IsNullOrEmpty(nameFilter) {
query = query.Where(x => x.Name == nameFilter);
}
if (!String.IsNullOrEmpty(zipFilter) {
query = query.Where(x => x.Zip == zipFilter);
}
// Sorting
switch (sortField)
{
case "Name":
query = query.OrderBy(x => x.Name);
break;
case "Zip":
query = query.OrderBy(x => x.Zip);
break;
}
You could also create an expression tree dynamically, but this is not very obvious.
I'm using a LINQ to Entities, and I have a couple of queries for which I want to be able to specify the Select clause at runtime.
I figured I'd have to do it by building an Expression and adding it to the IQueryable, but I'm not sure how to do this. Can anybody give me a hint?
I am not sure you could do what you want with expressions. The select clause specifies the type of the object in the IQueryable collection, that has to be defined at compile time. There is something called Dynamic Linq that can do what you want.
Something like this:
IQueryable<cerberus_Ticket> matches = db.cerberus_Tickets;
if (this.AgentIdField.Text.Trim().Length > 0)
{
matches = matches.Where(a => a.AgentId == criteria.AgentId);
}
if (this.TicketIdField.Text.Trim().Length > 0)
{
matches = matches.Where(a => a.TicketId.Contains(criteria.TicketId));
}
var output = matches.ToList();
I'm building an ASP.NET MVC site that uses LINQ to SQL.
In my search method that has some required and some optional parameters, I want to build a LINQ query while testing for the existence of those optional parameters.
Here's what I'm currently thinking:
using(var db = new DBDataContext())
{
IQueryable<Listing> query = null;
//Handle required parameter
query = db.Listings.Where(l => l.Lat >= form.bounds.extent1.latitude && l.Lat <= form.bounds.extent2.latitude);
//Handle optional parameter
if (numStars != null)
query = query.Where(l => l.Stars == (int)numStars);
//Other parameters...
//Execute query (does this happen here?)
var result = query.ToList();
//Process query...
Will this implementation "bundle" the where clauses and then execute the bundled query? If not, how should I implement this feature?
Also, is there anything else that I can improve?
Thanks in advance.
Yes, the query will only be executed once ToList() is called. If you follow this pattern and are using anonymous types, be aware of OrderBy() returning IOrderedQueryable instead of IQueryable.
Be aware that you are able to just iterate over the IQueryable, you don't have to call ToList to access the data.
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.