Linq queries on dB and using custom Comparers - linq

Whats the use of using custom comparers in Linq queries? Are they beneficial or are they just an overload on the server.
So i am talking about queries like
IEnumerable<Class> GetMatch(Class comparerObject)
{
return Session.Linq<Class>().Where(x=>new StringComparer<Class>().Equals(x,comparerObject))
}
and this is how my stringcomparer class looks like
public class StringComparer<T> : IEqualityComparer<T>
where T : class, IStringIdentifiable
{
public bool Equals(T x, T y)
{
if (x == null && y == null)
return true;
if (x == null || y == null)
return false;
return x.Id.Equals(y.Id, StringComparison.OrdinalIgnoreCase);
}
So I was wondering how is this query run against the db? I think linq handles this internally where in it sends a request to the db only after all the cases in the comparere are run.
Edit:
Well if you are finding it hard to believe that the above will not work then take a simple example like
return Session.Linq<Class>().Where(x=>x.Id.Equals(comparerObject,StringComparison.InvariantCultureIgnoreCase))
Then what do you think is the expected behavior?
Thanks.

For LINQ to SQL, I'd expect that to fail at execution time - the query translator won't know what to do with your StringComparer<T> class.
In the case you've given, you should just use:
string idToMatch = comparerObject.Id;
return Session.Linq<Class>().Where(x => x.Id == idToMatch);
More complicated cases may or may not be feasible depending on exactly what you want to achieve.

Related

Can linq predicates be combined here?

I am using EF4.3 with a unit-of-work + repository pattern.
I have a method which act as a base of some other methods, here's how the code looks.
This is my 'base' method:
public static IQueryable<Deal> FindActive()
{
var r = new ReadRepo<Deal>(Local.Items.Uow.Context);
return r.Find(d =>
d.ActiveFrom <= DateTime.Now &&
(d.ActiveUntilComputed == null || d.ActiveUntilComputed > DateTime.Now) &&
d.Published);
}
Here is one of the methods that calls the base method:
public static IQueryable<Deal> FindActiveByStore(int storeId)
{
Guard.Default(storeId, "storeId");
return FindActive().Where(d => d.StoreId == storeId);
}
As you can see in FindActiveByStore, I first call FindActive, which then chains Find(). FindActive is followed by a Where() to add a secondary predicate (excuse the terminology).
I wondered if it was possible to pass a predicate to FindActive instead of using a Where(), and in fact whether it would make a difference in terms of performance.
Like this:
FindActive(d => d.StoreId == storeId)
FindActive already passes a predicate to Find() so it would need to combine both.
I'm guessing that the answers I get back will be along the lines 'its not worth it' in terms of effort or performance but I thought i'd ask the experts anyway.
You can use this code (You reduce number lines of code)
public IQueryable<Deal> FindActiveByStore(Expression<Func<Deal,bool>> predicate)
{
var r = new ReadRepo<Deal>(Local.Items.Uow.Context);
return r.Find(d => d.ActiveFrom <= DateTime.Now
&& (d.ActiveUntilComputed == null || d.ActiveUntilComputed > DateTime.Now)
&& d.Published)
.Where(predicate);
}

Is there a way to improve this LINQ?

Code :
IList<Evento> Eventi = new List<Evento>() { };
Eventi = (from Evento ae in new Eventi()
select ae).ToList();
if (strNome != "")
{
Eventi = Eventi.Where(e => e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "").ToList();
}
if (strComune != "")
{
Eventi = Eventi.Where(e => e.Comune != null && e.IDComune == strComune).ToList();
}
if (strMesi != "")
{
Eventi = Eventi.Where(e => MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString())).ToList();
}
I know all query are merged, during running time of code, in only 1 LINQ statement. But, as you can see, I convert the List -> ToList() many times. This, I think, here, is the only part when I waste time, right? How can I avoid this and improve performance?
Why so many lists/ToLists? What's wrong with IEnumerable/IQueryable?
var eventi = (from Evento ae in new Eventi()
select ae);
if (strNome != "")
{
eventi = eventi.Where(e => e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "");
}
if (strComune != "")
{
eventi = eventi.Where(e => e.Comune != null && e.IDComune == strComune);
}
if (strMesi != "")
{
eventi = eventi.Where(e => MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString()));
}
// if you do need a list, then do so right at the end
var results = eventi.ToList();
EDIT: To clarify some principles for Caesay
Caesay, thank you for taking the time to test the implementation to confirm the deferred loading works as intended; much kudos!
I wanted to explain why I disagree with your comment about the above approach being optimized at run-time whilst yours being optimized at compile time.
The above approach is, for lack of a better description, the intended approach. This is because the assignments to eventi are correctly appending expressions to the source of the IEnumerable/IQueryable.
Your approach is only supported by certain providers, such as Linq to Entities, which expect a Func(Of T, TResult) to be passed to their Select, Where, etc Extensions. Many providers, such as Entity Framework and Linq to Sql provider, provide IQueryable, which implements IEnumerable. The difference here, however, is that IQueryable's Select, Where, etc, expect you to pass an Expression(Of Func(Of T, TResult)).
In those cases, your code will not behaveas expected (or at least as I would expect), because Expression does not support multi-line lambda, where as the compiler will correctly interpret my statements and compile them into Expression>.
As a simple example:
public void Test<T1, T2>(System.Linq.Expressions.Expression<Func<T1, T2>> arg)
{
throw new NotImplementedException();
}
public void Test()
{
Test((string x) => x.ToLower());
Test((string x) =>
{
return x.ToLower();
});
}
In the above example, the first expression is absolutely fine. The second, which is based loosely on your example, will fail with the exception:
A lambda expression with a statement body cannot be converted to an expression tree
The compiler may recognise your statements as a Func which it knows is supported on IEnumerable. The result would be that the query to the database would not include any of your Where expressions, returning the whole data source. Once the data source was in-memory, it would then apply your IEnumerable Where clauses. Personally, I much prefer passing these kind of things to the database so that I'm not wasting bandwidth on returning much more data than I need, and I can utilise my Data Sources ability to filter data which is likely (and vastly in Sql Server's case) better than doing so in-memory.
I hope that makes sense and is of some use to you?
You can use fluent syntax to start with (to avoid List).
Alternatively you can combine the condition in one query.
Eventi =(new Eventi()).Where(e => e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != "" && e.Comune != null && e.IDComune == strComune &&MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString())).ToList();
I have assumed Eventi implements IEnumerable < Evento > as you have used similarly in query syntax
Given that you test for strNome not being empty, the second half of your first Where clause will never be called.
So e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != ""
can be written e.Titolo.ToLower().Contains(strNome.ToLower())
You could also calculate strNome.ToLower() outside the lambda to ensure it is calculated just once.
In addition you can streamline the Where clauses and remove the ToList() as others have suggested.
Another alternative you should be aware of is LinqKit which allows you to combine lambda expressions more easily which although not needed in this case since Where ... Where is good enough for an 'And', you might one day need an 'Or' and then you need a different solution.
Or, better yet, use the method explained here to create your own And and Or methods that perform Expression 'magic' to give you a single expression that you can hand off to Linq-to-Sql or any other Linq provider.
Combine the whole thing into 1 linq query like this:
var eventi = from Evento e in new Eventi() select e;
eventi = eventi.Where(e =>
{
if (strNome != "")
{
if(!(e.Titolo.ToLower().Contains(strNome.ToLower()) && e.Titolo != ""))
return false;
}
if (strComune != "")
{
if(!(e.Comune != null && e.IDComune == strComune))
return false;
}
if (strMesi != "")
{
if(!(MesiSelezionati.Contains(DateTime.Parse(e.DataEvento).Month.ToString())))
return false;
}
return true;
});
var results = eventi.ToList();
This is logically equivalent to your code, but should be much faster. Although I was not able to test it, because I can't compile it.

Reversing IQueryable based on passed property for sorting logic

I am implementing sort based on parameter passed to ascending or descending OrderBy method
else if (showGrid.Sortdir == "DESC")
{
alerts = DB.Incidents.OfType<Alert>().Where(
a =>
a.IncidentStatusID == (int)AlertStatusType.New ||
a.IncidentStatusID == (int)AlertStatusType.Assigned ||
a.IncidentStatusID == (int)AlertStatusType.Watching)
.OrderByDescending(a => showGrid.Sort);
}
else
{
alerts = DB.Incidents.OfType<Alert>().Where(
a =>
a.IncidentStatusID == (int)AlertStatusType.New ||
a.IncidentStatusID == (int)AlertStatusType.Assigned ||
a.IncidentStatusID == (int)AlertStatusType.Watching)
.OrderBy(a => showGrid.Sort);
}
In case of ascending order sorting it works fine but for descending order sorting doesn't work. I debugged the code and I found that list is not revered its same as ascending order. Please help me
Ok. I've written a small test. It is funny, but your code can actually compile and work, but very differently from what you expect :)
Obviously showGrid is not of type Alert, it is an instance of some other class, that incidentally have the same propery as Alert, called Sort.
First I was confused, because expected this code to fail to compile.
// The signature of OrderBy
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
// In your case it will result in
public static IOrderedQueryable<Alert> OrderBy<Alert, string>(this IQueryable<Alert> source, Expression<Func<Alert, string>> keySelector)
//when you call it like you do
DB.Incidents.OfType<Alert>().OrderByDescending(a => showGrid.Sort);
// You supply a property from object of type different from your entity.
// This is incorrect usage, the only object you can use here is the
// "a" argument. Like this:
DB.Incidents.OfType<Alert>().OrderByDescending(a => a.Sort);
// Because anything else does not make any sense to entity provider.
So your order by simply does not work.
As far as I understood, what you want is to perform sorting based on selection in UI. This is not easily achieved in strongly-typed LINQ. Because as I showed above, you send a property, not a value to the OrderBy. It does not care about the value inside the prop. So there are several solutions to the problem:
Write a big switch, that will check every possible Sort value, and will append appropriate 'OrderBy(a => a.YouPropToSort)' to the query. This is straitforward, and you should begin with this. Of course this is a static way, and will require to change code everytime you want new columns to be added for sorting.
Create argument for your OrderBy using 'LINQ Expression Trees'. For you case it should not be very hard to do. Look for the term, you will find a lot of examples.
Try to use Dynamic LINQ. I did not not use it myself, just looked at the docs. This seems to be an extension to the normal LINQ which allows you to write parts of queries as strings, to overcome limitations like the current one with dynamic sorting.
Here's my solution to sorting based on user selections:
Create your base query
var query = DB.Incidents.OfType<Alert>.Where(
a =>
a.IncidentStatusID == (int)AlertStatusType.New ||
a.IncidentStatusID == (int)AlertStatusType.Assigned ||
a.IncidentStatusID == (int)AlertStatusType.Watching);
and then apply your sort using a case statement
bool desc = showGrid.SortDir = "DESC";
switch(showGrid.Sort)
{
case "col1":
query = desc ? query.OrderByDescending( a => a.Col1 ) : query.OrderBy( a => a.Col1 );
break;
case "col2":
query = desc ? query.OrderByDescending( a => a.Col2 ) : query.OrderBy( a => a.Col2 );
break;
...
}
var results = query.ToList();

linq styling, chaining where clause vs and operator

Is there a (logical/performance) difference to writing:
ATable.Where(x=> condition1 && condition2 && condition3)
or
ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)
I've been using the former but realised that with the latter, I can read and copy parts of a query out to use somewhere else easier.
Any thoughts?
Short answer
You should do what you feel is more readable and maintainable in your application as both will evaluate to the same collection.
Long answer quite long
Linq To Objects
ATable.Where(x=> condition1 && condition2 && condition3)
For this example Since there is only one predicate statement the compiler will only needs to generate one delegate and one compiler generated method.
From reflector
if (CS$<>9__CachedAnonymousMethodDelegate4 == null)
{
CS$<>9__CachedAnonymousMethodDelegate4 = new Func<ATable, bool>(null, (IntPtr) <Main>b__0);
}
Enumerable.Where<ATable>(tables, CS$<>9__CachedAnonymousMethodDelegate4).ToList<ATable>();
The compiler generated method:
[CompilerGenerated]
private static bool <Main>b__0(ATable m)
{
return ((m.Prop1 && m.Prop2) && m.Prop3);
}
As you can see there is only one call into Enumerable.Where<T> with the delegate as expected since there was only one Where extension method.
ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3) now for this example a lot more code is generated.
if (CS$<>9__CachedAnonymousMethodDelegate5 == null)
{
CS$<>9__CachedAnonymousMethodDelegate5 = new Func<ATable, bool>(null, (IntPtr) <Main>b__1);
}
if (CS$<>9__CachedAnonymousMethodDelegate6 == null)
{
CS$<>9__CachedAnonymousMethodDelegate6 = new Func<ATable, bool>(null, (IntPtr) <Main>b__2);
}
if (CS$<>9__CachedAnonymousMethodDelegate7 == null)
{
CS$<>9__CachedAnonymousMethodDelegate7 = new Func<ATable, bool>(null, (IntPtr) <Main>b__3);
}
Enumerable.Where<ATable>(Enumerable.Where<ATable>(Enumerable.Where<ATable>(tables, CS$<>9__CachedAnonymousMethodDelegate5), CS$<>9__CachedAnonymousMethodDelegate6), CS$<>9__CachedAnonymousMethodDelegate7).ToList<ATable>();
Since we have three chained Extension methods we also get three Func<T>s and also three compiler generated methods.
[CompilerGenerated]
private static bool <Main>b__1(ATable m)
{
return m.Prop1;
}
[CompilerGenerated]
private static bool <Main>b__2(ATable m)
{
return m.Prop2;
}
[CompilerGenerated]
private static bool <Main>b__3(ATable m)
{
return m.Prop3;
}
Now this looks like this should be slower since heck there is a ton more code. However since all execution is deferred until GetEnumerator() is called I doubt any noticeable difference will present itself.
Some Gotchas that could effect performance
Any call to GetEnumerator in the chain will cause a the collection to be iterated. ATable.Where().ToList().Where().ToList() will result in an iteration of the collection with the first predicate when the ToList is called and then another iteration with the second ToList. Try to keep the GetEnumerator called to the very last moment to reduce the number of times the collection is iterated.
Linq To Entities
Since we are using IQueryable<T> now our compiler generated code is a bit different as we are using Expresssion<Func<T, bool>> instead of our normal Func<T, bool>
Example in all in one.
var allInOneWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == "The Matrix" && m.Id == 10 && m.GenreType_Value == 3);
This generates one heck of a statement.
IQueryable<MovieSet> allInOneWhere = Queryable.Where<MovieSet>(entityFrameworkEntities.MovieSets, Expression.Lambda<Func<MovieSet, bool>>(Expression.AndAlso(Expression.AndAlso(Expression.Equal(Expression.Property(CS$0$0000 = Expression.Parameter(typeof(MovieSet), "m"), (MethodInfo) methodof(MovieSet.get_Name)), ..tons more stuff...ParameterExpression[] { CS$0$0000 }));
The most notable is that we end up with one Expression tree that is parsed down to Expression.AndAlso pieces. And also like expected we only have one call to Queryable.Where
var chainedWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == "The Matrix").Where(m => m.Id == 10).Where(m => m.GenreType_Value == 3);
I wont even bother pasting in the compiler code for this, way to long. But in short we end up with Three calls to Queryable.Where(Queryable.Where(Queryable.Where())) and three expressions. This again is expected as we have three chained Where clauses.
Generated Sql
Like IEnumerable<T> IQueryable<T> also does not execute until the enumerator is called. Because of this we can be happy to know that both produce the same exact sql statement:
SELECT
[Extent1].[AtStore_Id] AS [AtStore_Id],
[Extent1].[GenreType_Value] AS [GenreType_Value],
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name]
FROM [dbo].[MovieSet] AS [Extent1]
WHERE (N'The Matrix' = [Extent1].[Name]) AND (10 = [Extent1].[Id]) AND (3 = [Extent1].[GenreType_Value])
Some Gotchas that could effect performance
Any call to GetEnumerator in the chain will cause a call out to sql, e.g. ATable.Where().ToList().Where() will actually query sql for all records matching the first predicate and then filter the list with linq to objects with the second predicate.
Since you mention extracting the predicates to use else where, make sure they are in the form of Expression<Func<T, bool>> and not simply Func<T, bool>. The first can be parsed to an expression tree and converted into valid sql, the second will trigger ALL OBJECTS returned and the Func<T, bool> will execute on that collection.
I hope this was a bit helpful to answer your question.

Unpassable Where Clauses LINQ-to-SQL

As I'm struggling to learn LINQ I’ve managed to generate a SQL statement with "AND (0 = 1)" as part of the where clause. I'm just wondering if this result is common in poorly written queries and is a known issues to try and avoid or if I am doing something totally backwards to end up with this.
Update
public static IEnumerable<ticket> GetTickets(stDataContext db,string subgroup, bool? active)
{
var results = from p in db.tickets
where
( active == null || p.active == active )
/*(active == null ? true :
((bool)active ? p.active : !p.active))*/ &&
p.sub_unit == db.sub_units.Where(c=>subgroup.Contains(c.sub_unit_name))
select p;
return results;
}
If I ignore the active part and just run
public static IEnumerable<ticket> GetTickets1(stDataContext db,string subgroup, bool? active)
{
return db.tickets.Where(c => c.sub_unit.sub_unit_name == subgroup);
}
It returns the groups of tickets I want ignoring the active part.
I'd pull the processing out of the ternary operators.
where ( active == null || p.active == active )
EDIT
The rest of the where clause looks funky too... why is it not just doing
&& p.sub_unit.sub_unit_name == subgroup
or
&& subgroup.Contains(p.sub_unit.sub_unit_name)
?
That is some pretty heavy abuse of the ternary operator.
This expression:
(active == null ? true :
((bool)active ? p.active : !p.active))
Is equivalent to the following logic:
bool result;
if (active == null)
{
result = true;
}
else
{
if ((bool)active)
{
result = p.active;
}
else
{
result = !p.active;
}
}
result &= ...
Think carefully about what this is doing:
If active is null, you're fine, it skips to the next condition.
If active is true, result is true at the end of the conditional.
If active is false, result is false at the end of the conditional.
In the last case, the query can never return any rows!
#Tanzelax has already supplied a simple rewrite. The main idea is that you want to compare p.active to active, not actually evaluate the condition as p.active.
This is probably caused by a null value in one you the columns you have declared as non-nullable. LINQ2SQL makes all columns non-nullable by default. Go back to the designer and change the fields to allow values to be null. Unfortunately this feature is By Design (see connect.microsoft.com link below.)
(linq) incorrect sql generated for row.column == localVar when localVar is null (should be "is null" check)

Resources