Comparing date only on DateTime properties in EF4. - linq

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

Related

Using String.compare inside a lambda expression

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.

How to convert a string into a datetime in Linq to Entities query?

My Linq to entities query is written as below.
The datatype of DATECOLUMN1 in my ORACLE database is of string.
Datetime FilterStartDate = DateTime.Now;
var query = from c in db.TABLE1
join l in db.TABLE2 on c.FK equals l.PK
where (FilterStartDate >= DateTime.ParseExact(l.DATECOLUMN1, "dd/MM/yyyy", CultureInfo.InvariantCulture) : false) == true
select c;
Writing above query gives me an error of not supported. How can I convert DATECOLUMN1 into a datetime to compare it.
P.S. I do not have control over database schema, so changing datatype of column in Oracle database is not a feasible solution for me.
In you Model, add the following property to your partial class TABLE2:
public DateTime DATECOLUMN1_NEW
{
get
{
return DateTime.ParseExact(DATECOLUMN1, "dd/MM/yyyy", CultureInfo.InvariantCulture);
}
set { }
}
Then, in you LINQ query, use DATECOLUMN1_NEW (it's already in DateTime format) in place of DATECOLUMN1.
Erm.. I think the problem you are having is that you are putting ": false" in there.
It looks like you are trying to use a condtional operator (?:) but you forgot the "?".
I don't think you actually need this as you are just trying to determine if the date is greater or not. Also if ParseExact fails it will throw an exception (not what you want) so you should use TryParse instead and handle the true/false returned and the out value to determine whether or not the date is (a) Actually a date (b) less then FilterStartDate.
You can use two alternatives:
Use the function described in the answer here: How to I use TryParse in a linq query of xml data?
Use the following fluent syntax version which I think is more readable.
var query = db.Table1.Join(db.Table2, x => x.FK, y => y.PK, (x, y) => x).Where(x =>
{
DateTime Result;
DateTime.TryParse(x.Date, out Result);
return DateTime.TryParse(x.Date, out Result) && FilterStartDate >= Result;
});

How to compare only dates in linq?

In linq how can i diffentiate the date, like in sql we use
datediff(mm,getdate(),getdate())=0
now how can i write in linq, can any body help me thank you.
I think this is what you want:
var query = from entity in entities
where entity.SomeDateTime.Date == anotherDateTime.Date
select entity;
This only compares dates and not times.
You can use this code for comparing two dates using linq in C#
DateTime d1 = myDate1;
DateTime d2 = myDate2;
TimeSpan t1 = d2.Subtract(d1);
totalDays = t1.Days;
There's nothing special in Linq about date and time handling, so we should look at the plain .Net facilities.
A DateTime in C# is a date and time.
To get just the date component in C#, try this:
var now = DateTime.UtcNow; // or DateTime.Now for local time/day
DateTime today = now.Date;
This will give you the date and time at midnight of the same day.
If you want to get the difference in days between two DateTime objects:
public TimeSpan DiffDates(DateTime d1, DateTime d2)
{
return d1.Date - d2.Date;
}
// ...
if(DiffDates(dateTime1, dateTime2) == TimeSpan.Zero)
{
// ...
}
Alternately you could check directly if the dates are equal:
if(dateTime1.Date == dateTime2.Date)
{
// ...
}
If you skip the .Date property, then you will get incorrect results.

Linq to EF Expression Tree / Predicate int.Parse workaround

I have a linq Entity called Enquiry, which has a property: string DateSubmitted.
I'm writing an app where I need to return IQueryable for Enquiry that have a DateSubmitted within a particular date range.
Ideally I'd like to write something like
IQueryable<Enquiry> query = Context.EnquirySet.AsQueryable<Enquiry>();
int dateStart = int.Parse("20090729");
int dateEnd = int.Parse("20090930");
query = (from e in query
where(enq => int.Parse(enq.DateSubmitted) < dateEnd)
where(enq => int.Parse(enq.DateSubmitted) > dateStart)
select e);
Obviously Linq to EF doesn't recognise int.Parse, so I think I can achieve what I want with an Expression method that returns a predicate???
I've been playing around with PredicateBuilder and looking all over but I've successfully fried my brains trying to work this out. Sure I could add another property to my Entity and convert it there but I'd really like to understand this. Can anyone explain or give an example/link that doesn't fry my brains?
Thanks in advance
Mark
If you know your date strings are valid, and they're really in that order (which is a natural sort order) you might be able to get away with string comparisons:
IQueryable<Enquiry> query = Context.EnquirySet.AsQueryable<Enquiry>();
string dateStart ="20090729";
string dateEnd = "20090930";
query = (from e in query
where(enq => enq.DateSubmitted.CompareTo(dateEnd)) < 0)
where(enq => enq.DateSubmitted.CompareTo(dateStart)) > 0)
select e);

LINQ for LIKE queries of array elements

Let's say I have an array, and I want to do a LINQ query against a varchar that returns any records that have an element of the array anywhere in the varchar.
Something like this would be sweet.
string[] industries = { "airline", "railroad" }
var query = from c in contacts where c.industry.LikeAnyElement(industries) select c
Any ideas?
This is actually an example I use in my "Express Yourself" presentation, for something that is hard to do in regular LINQ; As far as I know, the easiest way to do this is by writing the predicate manually. I use the example below (note it would work equally for StartsWith etc):
using (var ctx = new NorthwindDataContext())
{
ctx.Log = Console.Out;
var data = ctx.Customers.WhereTrueForAny(
s => cust => cust.CompanyName.Contains(s),
"a", "de", "s").ToArray();
}
// ...
public static class QueryableExt
{
public static IQueryable<TSource> WhereTrueForAny<TSource, TValue>(
this IQueryable<TSource> source,
Func<TValue, Expression<Func<TSource, bool>>> selector,
params TValue[] values)
{
return source.Where(BuildTrueForAny(selector, values));
}
public static Expression<Func<TSource, bool>> BuildTrueForAny<TSource, TValue>(
Func<TValue, Expression<Func<TSource, bool>>> selector,
params TValue[] values)
{
if (selector == null) throw new ArgumentNullException("selector");
if (values == null) throw new ArgumentNullException("values");
if (values.Length == 0) return x => true;
if (values.Length == 1) return selector(values[0]);
var param = Expression.Parameter(typeof(TSource), "x");
Expression body = Expression.Invoke(selector(values[0]), param);
for (int i = 1; i < values.Length; i++)
{
body = Expression.OrElse(body,
Expression.Invoke(selector(values[i]), param));
}
return Expression.Lambda<Func<TSource, bool>>(body, param);
}
}
from c in contracts
where industries.Any(i => i == c.industry)
select c;
something like that. use the any method on the collection.
IEnumerable.Contains() translates to SQL IN as in:
WHERE 'american airlines' IN ('airline', 'railroad') -- FALSE
String.Contains() which translates to SQL LIKE %...% as in:
WHERE 'american airlines' LIKE '%airline%' -- TRUE
If you want the contacts where the contact's industry is LIKE (contains) any of the given industries, you want to combine both Any() and String.Contains() into something like this:
string[] industries = { "airline", "railroad" };
var query = from c in contacts
where industries.Any(i => c.Industry.Contains(i))
select c;
However, combining both Any() and String.Contains() like this is NOT supported in LINQ to SQL. If the set of given industries is small, you can try something like:
where c.Industry.Contains("airline") ||
c.Industry.Contains("railroad") || ...
Or (although normally not recommended) if the set of contacts is small enough, you could bring them all from the DB and apply the filter with LINQ to Objects by using contacts.AsEnumerable() or contacts.ToList() as the source of the query above:
var query = from c in contacts.AsEnumerable()
where industries.Any(i => c.Industry.Contains(i))
select c;
it will work if you build up the query as follows:
var query = from c in contacts.AsEnumerable()
select c;
query = query.Where(c=> (c.Industry.Contains("airline")) || (c.Industry.Contains("railroad")));
you just need to programmatically generate the string above if the parameters airline and railroad are user inputs. This was in fact a little more complicated than I was expecting. See article - http://www.albahari.com/nutshell/predicatebuilder.aspx
Unfortunately, LIKE is not supported in LINQ to SQL as per here:
http://msdn.microsoft.com/en-us/library/bb882677.aspx
To get around this, you will have to write a stored procedure which will accept the parameters you want to use in the like statement(s) and then call that from LINQ to SQL.
It should be noted that a few of the answers suggest using Contains. This won't work because it looks to see that the entire string matches the array element. What is being looked for is for the array element to be contained in the field itself, something like:
industry LIKE '%<element>%'
As Clark has mentioned in a comment, you could use a call to IndexOf on each element (which should translate to a SQL call):
string[] industries = { "airline", "railroad" }
var query =
from c in contacts
where
c.industry.IndexOf(industries[0]) != -1 ||
c.industry.IndexOf(industries[1]) != -1
If you know the length of the array and the number of elements, then you could hard-code this. If you don't, then you will have to create the Expression instance based on the array and the field you are looking at.

Resources