LINQ Query Syntax (Count Operation) - c#

I currently have a LINQ statement which works fine with Method Syntax. Am curious to see how would the equivalent query syntax look like. Tried doing multiple iterations but couldn't succeed with query syntax
Currently I have 2 strings - one being a sentence and other being an alphabet. I convert both of them into character for comparison - so I can find the number of occurrences of the character in the full string.
string sentenceToScan = "I Love StackOverflow!!!!";
string characterToScanFor = "e";
var stringToCheckAsCharacterArray = sentenceToScan.ToCharArray();
var characterToCheckFor = Char.Parse(characterToScanFor);
int numberOfOccurenes = stringToCheckAsCharacterArray.Count(n =>
n == characterToCheckFor);
Answer: 2

Count doesn't have a query expression syntax, it can only be appended at the end of a query expression like:
var count = (from t in stringToCheckAsCharacterArray
where t == characterToCheckFor).Count();
Or
var count = (from t in stringToCheckAsCharacterArray
where t == characterToCheckFor
select t).Count();
Personally I like Method expression, also LINQ query expression compiles in Method expressions. Your own code for Count with predicate is more readable IMO.

My LINQ is rusty, but something like:
stringToCheckAsCharacterArray.Where(n => n == characterToCheckFor).Count();

Related

Why do I get a different output from analogous Linq queries?

I am trying the following code in LinqPad 5 (specifically 5.26.01)
IEnumerable<string> breeds = new List<string>{
"Fantail",
"Lahore",
"Bokhara Trumpeter",
"Rhine Ringbeater",
"Birmingham Roller",
"Pomeranian Pouter",
"Racing Homer",
"Archangel"};
IEnumerable<string> GetAllBreedsContainingLetter_Fluent(IEnumerable<string> breedlist, string letter)
{
return breedlist
.Where(breedname => breedname.Contains(letter.ToUpperInvariant()) || breedname.Contains(letter.ToLowerInvariant()))
.OrderByDescending(breedname => breedname)
.Select(breedname => breedname);
}
IEnumerable<string> GetAllBreedsContainingLetter_Query(IEnumerable<string> breedlist, string letter)
{
return breedlist = from b in breedlist
where (b.Contains(letter.ToUpperInvariant()) || b.Contains(letter.ToLowerInvariant()))
orderby b descending
select b;
}
var breedsFluent = GetAllBreedsContainingLetter_Fluent(breeds, "R");
breedsFluent.Dump();
var breedsQuery = GetAllBreedsContainingLetter_Query(breeds, "R");
breedsQuery.Dump();
I think the two functions should be analogous but I noticed something odd about the output in Linqpad. The first .Dump() is identified as an IEnumerable<String>; the second .Dump() identifies as a IOrderedEnumerable<String>.
Is this something about the queries I'm running or is it an artifact of Linqpad? I haven't found anything from Googling.
In query syntax the transformation is such that when you have a trivial projection (projecting an item to itself) it only generates a Select call when that trivial projection is the only operation in the query. Since your query contains other operations, the Select is elided entirely, for performance reasons.
A proper translation of that query syntax query into method syntax would skip the Select. The way to replicate the behavior using query syntax would require something like a second query, to do the trivial projection.

Use SQL LIKE operator in C# LINQ

I am building up a query in C#. For integer and string fields, case is quite simple. For date fields, I am using following query:
list.Where("myDateColumn >= DateTime(2017,1,20)");
How I can perform following SQL LIKE query in LINQ?
select * from table where myTextColumn LIKE '%abc%';
There are lots of possibilities for Like in Linq:
For LIKE '%abc%';
list.Where(x => x.myTextColumn.Contains('abc'));
For LIKE 'abc%';
list.Where(x => x.myTextColumn.StartWith('abc'));
For LIKE '%abc';
list.Where(x => x.myTextColumn.EndsWith('abc'));
Updates : If you need to add Date comparison as well means you can do like the following:
DateTime date2Compare = new DateTime(2017, 1, 20);
list.Where(x => myDateColumn >= date2Compare && x.myTextColumn.Contains('abc'));
Placement of the Wildcard '%' in a LIKE clause makes a difference, and C# has the methods to back this up. Below is what the placements of the Wildcard means.
LIKE '%abc'
Meaning: Find any word ending with 'abc'.
C# equivalent: EndsWith
LIKE 'abc%'
Meaning: Find any word starting with 'abc', and you don't care about the text after.
C# equivalent: StartWith
LIKE '%abc%'
Meaning: Find any word that contains 'abc', and you don't care where in the word it appears.
C# equivalent: Contains
You can use Contains with myTextColumn field
var date = new DateTime(2017,1,20);
list.Where(x => x.myDateColumn >= date && x.myTextColumn.Contains('abc'));
While other answers (including the accepted one) are 80% correct, there are some caveats.
T-SQL allows the Like statement to have wildcards not just at the start and end of a string, but also in the middle. And % is not the only special char in the syntax, see docs for details.
.NET Core has EF.Functions.Like() method that is literally translated into LIKE statement. See the below example.
//assuming there is a db context with a table of Foos
await using var dbContext = new SqlContext();
//assuming table of Foos has Ipsum, Iptum, Ipuum
var result = dbContext.Foos.Where(x
=> EF.Functions.Like(x.Bar, "Ip_um"));
Caveats:
This only works with a server-side evaluation. An attempt to use this feature in client-side evaluation will throw NotSupportedException.
There is a noticeable difference in SQL translation between EF.Functions.Like and Contains or StartsWith, which may impact performance.
If you are certain you are using server-side evaluation and performance is important, use EF.Functions.Like.
Let's try solving the problem in general case. Suppose we're given something like
select ...
where ... MyField like '%abc%'
we can try convert like expression into corresponding regular one:
Like | Description |Regular
-------------------------------------------------
_ | any character (one and only one) | .
% | any characters (zero or more) | .*
Implementation
// If you want to implement both "*" and "?"
private static String LikeToRegular(String value) {
return "^" + Regex.Escape(value).Replace("_", ".").Replace("%", ".*") + "$";
}
usage:
var result list
.Where(x => Regex.IsMatch(x.myTextColumn, LikeToRegular("%abc%")));
you may want to convert the data into string before matching:
var result list
.Where(x => Regex.IsMatch(x.myDate.ToString(), LikeToRegular("%abc%")));

Search records with code pattern in column value c#

I have one code of employee which is A-B-C-D- . Now I want to search all records which are starting with A- till it rich A-B-C-D-. I have tried below code:
var result = db.Employee.Where(x=> x.EmployeeCode.StartsWith("A-B-C-D-"));
Above code gives me only one record. But I want all records which starts with A- then A-B- then A-B-C- and then equals to A-B-C-D-.
Any hint or idea is appreciated.
Have you tried this?
var result = db.Employee
.Where(x=> x.EmployeeCode.StartsWith("A-")
|| x.EmployeeCode.StartsWith("A-B-")
|| x.EmployeeCode.StartsWith("A-B-C-")
|| x.EmployeeCode.StartsWith("A-B-C-D-");
As you say in the comment that it must be dynamic, then do something like this:
string code = "A-B-C-D-";
var predicates = new List<Expression<Func<Customer,bool>>>();
for (int i = 0; i < code.Length; i++)
{
if (code[i] == '-')
{
var prefix = code.Substring(0, i + 1);
predicates.Add(x => x.EmployeeCode.StartsWith(prefix));
}
}
var oredPredicates = ...; // Keep reading!
...
var result = db.Employee.Where(oredPredicate);
Now, you have a lis of predicates, and have to combine them with || (or). To do so it's a bit messy, but there are solutions, for example like in this SO Q&A's:
Combining two expressions (Expression>)
Combine two Linq lambda expressions
How can I combine two lambda expressions without using Invoke method?
C# how to combine two expressions into a new one?
Once you have all the predicates combined, use it as parameter for your .Where() function.
Unfortunately the most complicated part of combining the expressions is unavoidable, and it's the toughest part to solve this problem, but it works. Please, read the 3 Q&A's, to get insight in what you're doing and get the code that best suits you (beware that using the simple Expression.OrAlso would not directly work, because the x param in each lambda is different for each created expression)

Nesting PredicateBuilder predicates : 'The parameter 'f' was not bound in the specified LINQ to Entities query expression'

I am building predicates using LinqKit's PrediateBuilder class to dynamically setup filters and I want to combine a nested one to another.
I have read this (http://www.albahari.com/nutshell/predicatebuilder.aspx) :
Here is my code :
// The main predicate.
var mainPredicate = PredicateBuilder.True<Document>();
// ... some other conditions to the main predicate here ...
// The inner predicate (combined conditions using OR).
var innerPredicate = PredicateBuilder.False<Document>();
foreach (var period in periods)
{
var p = period;
innerPredicate =
innerPredicate.Or(
d =>
(d.Date >= p.DateFrom && d.Date <= p.DateTo));
}
mainPredicate = mainPredicate.And(innerPredicate);
documents = this.ObjectSet.AsExpandable().Where(mainPredicate).ToList();
I am combining my two predicates just like it is explained in the documentation. However, I get this exception :
The parameter 'f' was not bound in the specified LINQ to Entities
query expression
I first thought that the inner predicate has to be expanded before combining it with the main predicate, so I changed my combining code to add a call to to the inner predicate's Expand method like this :
mainPredicate = mainPredicate.And(innerPredicate.Expand());
But I get the exact same exception.
The only difference in my code versus the documentation is that I dynamically build my nested predicate using a foreach loop. I just don't know how it can negatively affect the resulting expression.
What is wrong with my code ?
How can I actually debug this ?
Where the f parameter comes from ? How is it generated ? Why is it problematic in my case ?
Is there some kind of expression tree visualizer of some kind that could help me actually see what is wrong with the resulting expression ? Because the expression's body is hard to read.
Finally, I have found a way to avoid combining multiple predicates to the main expression tree.
Given that each predicate represents a different filter and I want the final, combined filter to be a series of must-be-respected conditions, we can say that each of the predicates has to return true for the final predicate to return true.
For that to work, the predicates has to be combined with AND. So, the resulting SQL query must look like this :
predicate1 AND predicate2 AND predicate3 ...
A better way to combine these predicates with AND is to chain Where query operators to the final query, like this :
var documents = this.ObjectSet.AsExpandable()
.Where(mainPredicate)
.Where(otherPredicate)
.Where(yetAnotherPredicate)
.ToList();
The resulting SQL query will combine each of these predicates with AND. That is just what I wanted to do.
It is easier than hacking out an expression tree by myself.

How to reuse where clauses in Linq To Sql queries

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.

Categories

Resources