How to do optional parameters in LINQ? - c#

Here is what I'm trying to do:
(Dc.DET_Cases.Where(c => c.ProductID == pl.ProductID
&& oldTOR == false ? c.OldTOR == oldTOR :
&& (productLineName.ToInt() == 0 || productLineName.ToInt() == c.ProductLineID)
&& (productCategory.ToInt() == 0 || productCategory.ToInt() == c.ProductCategoryID)
&& (issueType.ToInt() == 0 || issueType.ToInt() == c.IssueTypeID)
&& (issue.ToInt() == 0 || issue.ToInt() == c.IssueID)
)
.FirstOrDefault() != null)
This is the line I'm trying to do.
oldTOR == false ? c.OldTOR == oldTOR :
inside a where LINQ statement. If the value is false then compare the value. If not, then ignore it.

The easiest way to do this is to just set the other option to be true.
Ie: !oldTOR ? c.OldTOR == oldTOR : true
This means that if oldTor is false, then we want to compare OldTor's, otherwise, keep evaluating the rest of the expression.
As an aside, I would split each part of your massive .Where() boolean comparison into individual .Where() statements. This will improve readability and comprehension of your Linq.
Dc.DET_Cases
.Where(c => c.ProductID == pl.ProductID)
.Where(c => !oldTOR ? c.OldTOR == oldTOR : true)
.Where(c => productLineName.ToInt() == 0 || productLineName.ToInt() == c.ProductLineID)
.Where(c => productCategory.ToInt() == 0 || productCategory.ToInt() == c.ProductCategoryID)
.Where(c => issueType.ToInt() == 0 || issueType.ToInt() == c.IssueTypeID)
.Where(c => issue.ToInt() == 0 || issue.ToInt() == c.IssueID)
.FirstOrDefault() != null;
This makes it clear that if oldTor is false then you want to compare, otherwise, pass that statement (true).

I think you can use the LINQ Dynamic Query Library.
And then create a query as you like.

The fastest way to do this is by evaluation paths:
oldTOR && (c.OldTor == oldTOR)
Why is this the fastest? Answer: if oldTOR is false then the whole and statement is false, and there is no need to compare the rest of the statement.

Related

Linq optional parameters query

I am making a search form that queries my database to show results based on what has been filled out on the form. The only required field is the date which I have working. all the other fields are optional, if an optional field is not filled in it should not be a part of the query. This is the code I have written:
var queryable = context.TransactionJournal.Where(s => s.TransactionDateTime <= transactionDate)
.Where(s => Region == null || Region == s.AcquirerID)
.Where(s => MCC == null || MCC == s.MerchantCategoryCode)
.Where(s => MerchantID == null || MerchantID.Contains(s.MerchantID))
.Where(s => TxnCurrency == null || TxnCurrency.Contains(s.Currency))
.Where(s => TerminalID == null || TerminalID.Contains(s.TerminalID))
.Where(s => TxnAmount.ToString() == null || TxnAmount==(s.TransactionAmount))
.Where(s => BIN == null || BIN.Contains(s.Bin))
.Where(s => MsgType == null || MsgType.Contains(s.MessageType))
.Where(s => MaskedPan == null || MaskedPan.Contains(s.PANM))
.Where(s => ProcessingCode == null || ProcessingCode.Contains(s.ProcessingCode))
.Where(s => ClearPan == null || ClearPan.Contains(s.PAN))
.Where(s => ResponseCode == null || ResponseCode.Contains(s.ResponseCode))
.Where(s => AuthorizationCode == null || AuthorizationCode.Contains(s.AuthorizationCode))
.Where(s => EntryMode == null || EntryMode.Contains(s.PosEntryMode))
.AsQueryable();
Unfortunately it does not work correctly. Can someone tell me what I am missing or if there is a better way to write this?
Took advice from the comments and went through each line and found which line was evaluating false. This fixed my problem.
I think the best you can do there is check first if you should apply the condition and then filter the list.
An example using the code you provided.
var queryable = context.TransactionJournal.Where(s => s.TransactionDateTime <= transactionDate);
if (!string.IsNullOrEmpty(your_objet.Region)
{
var queryable = queryable.Where(x=>x.Region == your_objet.Region).AsQueryable();
}
if (!string.IsNullOrEmpty(your_objet.MCC)
{
var queryable = queryable.Where(x=>x.MCC == your_objet.MCC).AsQueryable();
}
The first line is the entire list, then you check all parameters that you have in the form and evaluate it, if has value the apply the filter to list.
And the end you'll get your list filtered.

alternative way to linq GroupBy to improve speed of query

i have some product category which can inherit to more grade (in my project i have max 4 grade). for example (1)Digital_Product-> (2)Mobile-> (3)Android-> (4)Sony
i want to select 2 newest product from each root category (grade 1). i used below query but it processed in 1800ms. the problem is groupBy query. when i delete Groupby, it processed in 100ms.
IEnumerable<Db.Article> _List = uow.ArticleRepository.Get().Where(x =>
x.ArticlePropertyValues.Any(c => (_Status == "All" || c.ArticleProperty.Key == _Status))
&& x.IsActive == true
&& x.IsDeleted == false
&& (_Type == -2 || x.ArticleCategory.TypeID == _Type)
).GroupBy(p => p.ArticleCategory.ParentID == -1 ?
p.ArticleCategory.ID :
(p.ArticleCategory.ArticleCategory1.ParentID == -1 ? p.ArticleCategory.ArticleCategory1.ID :
(p.ArticleCategory.ArticleCategory1.ArticleCategory1.ParentID == -1 ?
p.ArticleCategory.ArticleCategory1.ArticleCategory1.ID :
p.ArticleCategory.ArticleCategory1.ArticleCategory1.ParentID))
).SelectMany(p => p.OrderByDescending(x => x.ID).Take(2));
the ParentID isn't foreign id and no index was used in database.

Fetch Items in LINQ where child entities fulfill certain conditions

I'm trying to return a list of TrackInformationRecord, done by:
return _trackInformationRepository
.Fetch(t => t.TrackPartId == trackPart.Id && t.IsDeleted == false)
.ToList();
However, the TrackInformationRecord contains a list of SessionInformationRecord in TrackInformationRecord.Sessions and I only want to take Sessions with IsDeleted == false.
I tried the following but it did't work:
return _trackInformationRepository
.Fetch(t => t.TrackPartId == trackPart.Id && t.IsDeleted == false
&& t.Sessions.Where(s => s.IsDeleted == false))
.ToList();
Any advise would be highly appreciated.
If you want to make sure that all Session's object IsDeleted should be false, you want All
return _trackInformationRepository
.Fetch(t => t.TrackPartId == trackPart.Id && !t.IsDeleted
&& t.Sessions.All(s => !s.IsDeleted)).ToList();
But in case, you want any Session's object IsDeleted to be false, you need Any-
return _trackInformationRepository
.Fetch(t => t.TrackPartId == trackPart.Id && !t.IsDeleted
&& t.Sessions.Any(s => !s.IsDeleted)).ToList();
On a side note, instead of writing t.IsDeleted == false, you can write it like !t.IsDeleted.

Linq to Entity having more than one where conditions

How can I check for more than one condition in the where clause of LINQ to Entities?
How can I check if the value is false or null
.Where(p => (p.Disabled == false || p.Disabled = null));
You can combine conditions using the usual Boolean operators.
Your solution is missing == in the second part of the condition:
.Where(p => (p.Disabled == false || p.Disabled == null));
// Here --------------------^
You can simplify this further, because checking for a nullable bool to be false or null is equivalent to checking for it not being true:
.Where(p => p.Disabled != true);

Smarter where clause?

I've been experimenting with LINQ to SQL recently and have a quick question.
The basic premise is I have a search request which contains a Make and Model which I use to search a DB containing cars.
The expression for my Where Clause is shown below:
.Where(c => c.make == search.make && c.model == search.model)
This is fine when my search contains both a make and a model. The problem arises when it only contains a make(or vice versa) and not both search fields. I want it to return all cars of that make, it is however returning none.
Im assuming this is because it is looking for the make plus a model that is null or empty?
Is there an elegant way to get around this other than manually building up the query with a series of "if not null append to query" type steps?
Have you tried:
.Where(c => (search.make == null || c.make == search.make) && (search.model == null || c.model == search.model))
Update: There's actually a nice treatment of the general problem, and clean solutions, here:
LINQ to SQL Where Clause Optional Criteria. The consensus seems to be that an extension method is cleanest.
.Where(c => (search.make == null || c.make == search.make) &&
(search.model == null || c.model == search.model))
IMO, split it:
IQueryable<Car> query = ...
if(!string.IsNullOrEmpty(search.make))
query = query.Where(c => c.make == search.make);
if(!string.IsNullOrEmpty(search.model))
query = query.Where(c => c.model== search.model);
This produces the most appropriate TSQL, in that it won't include redundant WHERE clauses or additional parameters, allowing the RDBMS to optimise (separately) the "make", "model" and "make and model" queries.
you could write something like this:
.Where(c => c.make == search.make ?? c.make && c.model == search.model ?? c.model)
This should work.
.Where(c =>
(search.make == null || c.make == search.make) &&
(search.model == null || c.model == search.model))
.Where(c => (string.IsNullOrEmpty(c.make) || c.make == search.make) &&
(string.IsNullOrEmpty(c.model) || c.model == search.model))
.Where(c => (string.IsNullOrEmpty(search.make) || c.make == search.make) &&
(string.IsNullOrEmpty(search.model) || c.model == search.model))
That's assuming the properties are strings.
Where(c => (search.make == null || c.make == search.make) &&
(search.model == null || c.model == search.model))

Categories