Dynamically construct Select clause of a Linq query - c#

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();

Related

Split and Contain inside clause where in complex linq query

I have this linq query, that is doing a select with loads of joins, the query has to return this, and I need to check a "splitted" string for a certain other string. My query is the following:
....
where
&& Name.Split(',').Contains(container) == true
))
select new
{
...
});
But this gives me an exception, because of the split method. What can I do? I tried the Any clause, but still gives me an error...
Thanks in advance :)

LINQ WHERE with OR

I use LINQ to create my where clause like so:
var query = from x in context.Xs
select x;
if (y == ...)
{
query = query.Where(x => x.Y == 1);
}
I have bunch of these "if .... where" statements. The issue I have is that all of those wheres join where clauses using AND but I need all my where clauses to use OR. Is there an easy way to port this code into where OR code? Or even what is the easiest way to do this with OR?
Thanks.
You could do something like:
var query = from x in context.Xs
where
(x.X == 1) ||
(x.Y == 2) ||
(x.Z == "3")
select x;
PredicateBuilder is the perfect solution for your problem. It allows you to keep adding individual "AND" as well as "OR" statements together.
I would suggest using Expression Trees to build your query dynamically:
(MSDN) How to: Use Expression Trees to Build Dynamic Queries
You could also use a PredicateBuilder (which does something similar under the hood) to build the Predicate dynamically and then pass the final Predicate to the Where method.

How dynamic can I make my LINQ To SQL Statements?

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

Dynamic WHERE clause in LINQ

What is the best way to assemble a dynamic WHERE clause to a LINQ statement?
I have several dozen checkboxes on a form and am passing them back as: Dictionary<string, List<string>> (Dictionary<fieldName,List<values>>) to my LINQ query.
public IOrderedQueryable<ProductDetail> GetProductList(string productGroupName, string productTypeName, Dictionary<string,List<string>> filterDictionary)
{
var q = from c in db.ProductDetail
where c.ProductGroupName == productGroupName && c.ProductTypeName == productTypeName
// insert dynamic filter here
orderby c.ProductTypeName
select c;
return q;
}
(source: scottgu.com)
You need something like this? Use the Linq Dynamic Query Library (download includes examples).
Check out ScottGu's blog for more examples.
I have similar scenario where I need to add filters based on the user input and I chain the where clause.
Here is the sample code.
var votes = db.Votes.Where(r => r.SurveyID == surveyId);
if (fromDate != null)
{
votes = votes.Where(r => r.VoteDate.Value >= fromDate);
}
if (toDate != null)
{
votes = votes.Where(r => r.VoteDate.Value <= toDate);
}
votes = votes.Take(LimitRows).OrderByDescending(r => r.VoteDate);
You can also use the PredicateBuilder from LinqKit to chain multiple typesafe lambda expressions using Or or And.
http://www.albahari.com/nutshell/predicatebuilder.aspx
A simple Approach can be if your Columns are of Simple Type like String
public static IEnumerable<MyObject> WhereQuery(IEnumerable<MyObject> source, string columnName, string propertyValue)
{
return source.Where(m => { return m.GetType().GetProperty(columnName).GetValue(m, null).ToString().StartsWith(propertyValue); });
}
It seems much simpler and simpler to use the ternary operator to decide dynamically if a condition is included
List productList = new List();
productList =
db.ProductDetail.Where(p => p.ProductDetailID > 0 //Example prop
&& (String.IsNullOrEmpty(iproductGroupName) ? (true):(p.iproductGroupName.Equals(iproductGroupName)) ) //use ternary operator to make the condition dynamic
&& (ID == 0 ? (true) : (p.ID == IDParam))
).ToList();
I came up with a solution that even I can understand... by using the 'Contains' method you can chain as many WHERE's as you like. If the WHERE is an empty string, it's ignored (or evaluated as a select all). Here is my example of joining 2 tables in LINQ, applying multiple where clauses and populating a model class to be returned to the view. (this is a select all).
public ActionResult Index()
{
string AssetGroupCode = "";
string StatusCode = "";
string SearchString = "";
var mdl = from a in _db.Assets
join t in _db.Tags on a.ASSETID equals t.ASSETID
where a.ASSETGROUPCODE.Contains(AssetGroupCode)
&& a.STATUSCODE.Contains(StatusCode)
&& (
a.PO.Contains(SearchString)
|| a.MODEL.Contains(SearchString)
|| a.USERNAME.Contains(SearchString)
|| a.LOCATION.Contains(SearchString)
|| t.TAGNUMBER.Contains(SearchString)
|| t.SERIALNUMBER.Contains(SearchString)
)
select new AssetListView
{
AssetId = a.ASSETID,
TagId = t.TAGID,
PO = a.PO,
Model = a.MODEL,
UserName = a.USERNAME,
Location = a.LOCATION,
Tag = t.TAGNUMBER,
SerialNum = t.SERIALNUMBER
};
return View(mdl);
}
Just to share my idea for this case.
Another approach by solution is:
public IOrderedQueryable GetProductList(string productGroupName, string productTypeName, Dictionary> filterDictionary)
{
return db.ProductDetail
.where
(
p =>
(
(String.IsNullOrEmpty(productGroupName) || c.ProductGroupName.Contains(productGroupName))
&& (String.IsNullOrEmpty(productTypeName) || c.ProductTypeName.Contains(productTypeName))
// Apply similar logic to filterDictionary parameter here !!!
)
);
}
This approach is very flexible and allow with any parameter to be nullable.
You could use the Any() extension method. The following seems to work for me.
XStreamingElement root = new XStreamingElement("Results",
from el in StreamProductItem(file)
where fieldsToSearch.Any(s => el.Element(s) != null && el.Element(s).Value.Contains(searchTerm))
select fieldsToReturn.Select(r => (r == "product") ? el : el.Element(r))
);
Console.WriteLine(root.ToString());
Where 'fieldsToSearch' and 'fieldsToReturn' are both List objects.
This is the solution I came up with if anyone is interested.
https://kellyschronicles.wordpress.com/2017/12/16/dynamic-predicate-for-a-linq-query/
First we identify the single element type we need to use ( Of TRow As DataRow) and then identify the “source” we are using and tie the identifier to that source ((source As TypedTableBase(Of TRow)). Then we must specify the predicate, or the WHERE clause that is going to be passed (predicate As Func(Of TRow, Boolean)) which will either be returned as true or false. Then we identify how we want the returned information ordered (OrderByField As String). Our function will then return a EnumerableRowCollection(Of TRow), our collection of datarows that have met the conditions of our predicate(EnumerableRowCollection(Of TRow)). This is a basic example. Of course you must make sure your order field doesn’t contain nulls, or have handled that situation properly and make sure your column names (if you are using a strongly typed datasource never mind this, it will rename the columns for you) are standard.
System.Linq.Dynamic might help you build LINQ expressions at runtime.
The dynamic query library relies on a simple expression language for formulating expressions and queries in strings.
It provides you with string-based extension methods that you can pass any string expression into instead of using language operators or type-safe lambda extension methods.
It is simple and easy to use and is particularly useful in scenarios where queries are entirely dynamic, and you want to provide an end-user UI to help build them.
Source: Overview in Dynamic LINQ
The library lets you create LINQ expressions from plain strings, therefore, giving you the possibility to dynamically build a LINQ expression concatenating strings as you require.
Here's an example of what can be achieved:
var resultDynamic = context.Customers
.Where("City == #0 and Age > #1", "Paris", 50)
.ToList();

LINQ, Large filter on a query

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.

Categories

Resources