Using String.compare inside a lambda expression - linq

I have the following model method:
public IQueryable<CustomerVLAN> CustomerVLANS(int customerid)
{
string customername = entities.AccountDefinitions.SingleOrDefault(a=>a.ORG_ID == customerid).ORG_NAME;// .tolist since i will be querying different context
return tms.CustomerVLANS.Where(String.Compare(a=>a.CustomerName.ToString(), customername.ToString(), StringComparison.OrdinalIgnoreCase));
}
but I am unable to use the String.Compare inside my where clause ,,,

Provided CustomerName and customername are strings, you can try
return tms.CustomerVLANS.Where(a=>a.CustomerName.ToUpper() == customername.ToUpper());

The Where extension filters an IEnumerable using a supplied predicate function: Func<TSource, bool>, in your case Func<CustomerVLAN, bool>.
Your line "String.Compare(a=>a.CustomerName.ToString(), customername.ToString(), StringComparison.OrdinalIgnoreCase)" returns an integer, rather than a Func<CustomerVLAN, bool> delegate (I'm also not sure if there's an overload of string Compare which expects the values you've provided).
If you wanted to use that particular function, you'd have to do something like this (using a Lambda expression):
CustomerVLANS.Where(x => (String.Compare(x.CustomerName, customername, StringComparison.OrdinalIgnoreCase) == 0));
Unless there's a particular reason for it, you may well be better of using String.Equals, which returns a boolean:
CustomerVLANS.Where(x => (String.Equals(x.CustomerName, customername)));
You may need your ToString(), depending on what type CustomerName is.

Related

Create case sensitive LINQ Expression with Contains method (Case insensitive collation database)

I have a database created with a SQL_Latin1_General_CP1_CI_AS collation (which is case insensitive).
I'm creating LINQ Expressions and trying to make "Contains" method which should compare strings in a case sensitive way.
I know that I can force collation if I use something like:
CHARINDEX(N'foo' COLLATE SQL_Latin1_General_CP1_CS_AS, 'something Foo') > 0
but since I'm building expressions using LINQ to create a query, my code looks like:
using System.Linq.Expressions;
private Expression Contains(Type type, string value, Expression propertyExpression)
{
var searchValue = Expression.Constant(value, typeof(string));
var method = propertyExpression.Type.GetMethod("Contains", new[] { type });
var result = Expression.Call(propertyExpression, method, searchValue);
...
}
So, if I'm trying to use this Contains method for word 'foo', rows with 'Foo' will also be returned (which I don't want).
Is there a way that I can expand this Expression so that I can specify Case Sensitive comparison?

How can I use func in a C# EF dbquery statement for sorting?

I have to do multi-part sorts and want to do it dynamically.
I found this question but do not know how to use func in a dbquery statement.
No generic method 'ThenBy' on type 'System.Linq.Queryable'
If I could get the code in the thread to work it would be nirvana.
All the examples I have seen use then within a where statement, but I need to use the function to do sorting.
I have written extensions using IQueryable, including ones for orderby and orderbydescending. The problem is thenby and thenbydescending use iorderedqueryable.
The error I get when using ThenByProperty is
Object of type 'System.Data.Entity.Infrastructure.DbQuery1[ORMModel.v_Brand]' cannot be converted to type 'System.Linq.IOrderedEnumerable1[ORMModel.v_Brand]'.
Do not get such an error when I use a comparable OrderByProperty extension.
what a mess, obviously I do not post often here. Anyway I am stumped and clueless so any tips are very appreciated.
Tried to post code but kept getting format errors so gave up. But help me anyways :)
If you use method syntax, you'll see func quite often, for instance in Where, GroupBy, Join, etc
Every method with some input parameters and one return value can be translated to a Func<...> as follows
MyReturnType DoSomething(ParameterType1 p1, ParameterType2, p2) {...}
Func<ParameterType1, ParameterType2, MyReturnType> myFunc = (x, y) => DoSomething(x, y);
The part Func<ParameterType1, ParameterType2, MyReturnType> means: a function with two input parameters and one return value. The input parameters are of type ParameterType1 and ParameterType2, in this order. The return value is of MyReturnType.
You instantiate an object of Func<ParameterType1, ParameterType2, MyReturnType> using a lambda expression. Before the => you type a declaration for the input parameters, after the => you call the function with these input parameters. If you have more than one input parameter you make them comma separated surrounded by brackets.
For a Where you need a Func<TSource, bool>. So a function that has as input one source element, and as result a bool:
Where(x => x.Name == "John Doe")
For a GroupJoin you need a resultSelector of type Func<TOuter,System.Collections.Generic.IEnumerable<TInner>,TResult> resultSelector
So this is a function with as input one element of the outer sequence, and a sequence of elements of the inner sequence. For example, to query Teachers with their Students:
var result = Teachers.GroupJoin(Students,
teacher => teacher.Id, // from every Teacher take the Id,
student => student.TeacherId, // from every Student take the TeacherId,
(teacher, students) => new
{
Id = teacher.Id,
Name = teacher.Name,
Students = students.Select(student => new
{
Id = student.Id,
Name = student.Name,
})
.ToList(),
});
Here you see several Funcs. TOuter is Teacher, TInner is Student, TKey is int
OuterKeySelector: Func<TOuter, TKey>: teacher => teacher.Id
InnerKeySelector: Func<TInner, TKey>: student => student.TeacherId
ResultSelector: Func<Touter, IEnumerable<TInner>, TResult>
The resultSelector is a function that takes one TOuter (a Teacher), and a sequence of TInner (all students of this Teacher) and creates one object using the input parameters
(teacher, students) => new {... use teacher and students }
When creating the lambda expression it is often helpful if you use plurals to refer to collections (teachers, students) and singulars if you refer one element of a collection (student).
Use the => to start defining the func. You can use input parameters that were defined before the => to define the result after the =>

Is it possible to write join with a boolean evaluation?

I want to write
CompareInfo myCompIntl = CompareInfo.GetCompareInfo( "es-ES" );
var SharedYomi = from ObjA in ClassListA
join ObjB in ClassListB
where CompareInfo.Compare(ObjA.Name, ObjB.Name) == 0
select new {stringA = stringA, string = string};
Linq forces me to write join with equals. I can not pass in a Boolean evaluation.
How can I do that?
You cannot write that using the LINQ lambda query syntax. The join keyword requires you to specify exactly two properties that are compared using the equals keyword, because this maps to the first overload of Join that uses the default comparer to compare keys.
However, there is an overload of Join that accepts an IEqualityComparer that will probably work for you, you just need to use method query syntax.
Since you sound like you're not familiar with the method syntax, here's a good starting article from MSDN:
http://msdn.microsoft.com/en-us/library/vstudio/bb397947.aspx
But, basically, the syntax you think of as "LINQ" is just one way to refer to the LINQ extensions, and is really just syntactic sugar around the IEnumerable extension methods that implement LINQ. So, for example, the query:
from x in y where x.IsActive orderby x.Name select x
it basically identical to
y.Where(x => x.IsActive).OrderBy(x => x.Name).Select(x => x);
For the most part, each query clause maps to a particular overload of a particular IEnumerable method, but those methods have a number of other overloads that take different numbers and types of parameters.
The Join methods are a bit complex, because they take two sequences as input and let you combine individual elements of them using expressions, but the idea is exactly the same. A typical join would look like this:
from x in y
join a in b on x.Id equals a.ParentId
select new { x.Id, x.Name, a.Date }
becomes
y.Join(
b,
x => x.Id,
a => a.ParentId,
(x, a) => new { x.Id, x.Name, a.Date });
This will join a.ParentId and x.Id using the default comparison for their data type (int, string, whatever). The compiler directly translates the query syntax into method syntax, so the two behave exactly the same. (Nitpicking my own answer: Technically, the methods are on the Enumerable class, so you are really calling Enumerable.Join. But as they were implemented as extension methods, you can call them either way and the compiler will figure it out.)
In your case, what you need is to pass in a different comparison method, so you can call string.Compare with the explicit encoding. The other overload of Join lets you supply an implementation of IEqualityComparer<T> to use instead of the default. This will require you to implement IEqualityComparer<string> in a separate class, since there's no easy way to create an anonymous interface implementation (perhaps the only feature I miss from Java). For your example, you want something like this:
public class ComparerWithEncoding : IEqualityComparer<string>
{
private CompareInfo compareInfo
public ComparerWithEncoding ( string encoding )
{
this.compareInfo = CompareInfo.GetCompareInfo(encoding);
}
public bool Equals ( string a, string b )
{
return CompareInfo.Compare(a, b) == 0
}
public int GetHashCode(string a)
{
return a.GetHashCode();
}
}
classListA.Join(
ClassListB,
ObjA => ObjA.Name,
ObjB => ObjB.Name,
(ObjA, ObjB) => new { stringA = ObjA.Foo, stringB = ObjB.Bar },
new ComparerWithEncoding("es-ES"));

Comparing date only on DateTime properties in EF4.

I find myself using the 'pattern' below rather unsettlingly often, when I want to select entities with based only on the date part of a DateTime property. EF doesn't parse the DateTime.Date property to T-SQL, so I end up using this code:
var nextDay = raceDate.Date.AddDays(1);
return EntityContext.RacingInfoes.SingleOrDefault(ri => ri.RaceDate >= raceDate && ri.RaceDate < nextDay);
It's the most readable solution I have found so far, but I don't like repeating it everywhere. However, I can't encapsulate it in any method as that method is not recognised by the Linq to Entities parser. Is there anything I can do about this?
You can encapsulate it by writing a method like this:
Expression<Func<T, bool>> OnDate<T>(Expression<Func<T, DateTime>> selector,
DateTime date)
{
var nextDay = date.Date.AddDays(1);
// Build up an expression tree, using Expression.AndAlso etc to
// compare the result of applying the selector with both date and nextDay
}
Then you'd write:
return EntityContext.RacingInfoes.SingleOrDefault(Helper.OnDate(x => x.RaceDate),
raceDate);
(OnDate is a bad name, but you see what I mean...)
I use this (often in combination with LINQKit's Invoke functionality) (which is similar to an implementation of Jon Skeet's answer):
public static class Criteria
{
...
public static Expression<Func<DateTime, DateTime, bool>> IsOnDate =
(dateTime, onDate) =>
dateTime >= onDate.Date `&&` dateTime < onDate.AddDays(1).Date;
...
}
This way, you can combine the criteria with other conditions in a single statement
EntityContext.RacingInfoes.AsExpandable().SingleOrDefault(ri =>
Criteria.IsOnDate.Invoke(ri.RaceDate, DateTime.Today) || someOtherCriteria);

LINQ Dynamic Expression API, predicate with DBNull.Value comparison

I have an issue using the Dynamic Expression API. I cannot seem to compare a DataTable field against DBNull.Value. The API is supposed to be able to "support static field or static property access. Any public field or property can be accessed.". However given the following query:
var whatever = table1.AsEnumerable()
.Join(table2.AsEnumerable(),
(x) => x.Field<int>("Table1_ID"),
(y) => y.Field<int>("Table2_ID"),
(x, y) => new { x, y})
.AsQueryable()
.Where("x[\"NullableIntColumnName\"] == DBNull.Value");
I end up getting the error: "No property or field 'DBNull' exists in type '<>f__AnonymousType0`2'"
Anyone have ideas on how to get around this? I can't use Submission.Field("NullableIntColumnName") in the string passed to the Where method either, btw, or else I would be able to compare against null instead of DBNull.Value.
Well, I finally got it. cptScarlet almost had it.
var values = new object[] { DBNull.Value };
...
.Where("x[\"NullableIntColumnName\"] == #0", values);
or
.Where("x[\"NullableIntColumnName\"] == #0", DBNull.Value);
What happens when you replace your current .Where with something like
.Where(string.format("x[\"NullableIntColumnName\"] == {0}",DBNull.Value));
If you change x.Field<int>("Table1_ID") to x.Field<int?>("Table1_ID") then you'll get nullable integers instead of regular integers, and any DBNull values will be converted to simple C# null values. Based simply on your code snippet, I'm not even sure you'd need dynamic expressions - a simple .Where(foo => foo.x == null) ought to work.
In general, you can also try:
.Where("NullableColumnName.HasValue");
Sorry to non-answer with a USL but...
Have you looked in the source? There's not a lot of it. My guess is that DBNull is not in the list of registered root objects.
I dont have the source to hand right now, but it is also likely to tell you what any other constants one might compare against might be.
.Where(a => a.IntColName == null);
Edit:
Sorry, I did't see this dynamic requirement... Dynamic would be: (at least in Framework 4)
var intColName = "...";
.Where(string.Format("it.{0} is null", intColName));

Resources