Compiled Query in LINQ error? - c#

I am getting the error below and I am not sure the syntax is correct for multiple criteria in the LINQ query.
My code thus far,
static class MyQuery
{
private static Func<DatabaseDataContext, IQueryable<Staff_Time_TBL>>
queryFor =
CompiledQuery.Compile((DatabaseDataContext db, DateTime dDate) =>
db.Staff_Time_TBLs.Where(a => a.Date_Data == dDate &&
a.Time_Data_1 == null && a.Time_Data_2 == null).FirstOrDefault());
}
DatabaseDataContext is the name of the LINQ to SQL class.
Staff_Time_TBL is the table to get data from.
This is the error,
Error CS0029 Cannot implicitly convert type 'System.Func
(Example3.DatabaseDataContext, System.DateTime,
Example3.Staff_Time_TBL)' to 'System.Func
(Example3.DatabaseDataContext,
System.Linq.IQueryable(Example3.Staff_Time_TBL))'
I would post all my attempts at getting this right ,but would clutter the post.
Used this as research to get to this point.
How to: Store and Reuse Queries
Compiled Queries (LINQ to Entities)
How to improve your LINQ query performance by 5 X times?
Compiled Queries in LINQ

You've defined your field as:
Func<DatabaseDataContext, IQueryable<Staff_Time_TBL>>
Which has the equivelant method signature:
public IQueryable<Staff_Time_TBL> SomeMethod(DatabaseDataContext db)
{
...
}
But look at what you're passing it:
(DatabaseDataContext db, DateTime dDate) =>
db.Staff_Time_TBLs.Where(a => a.Date_Data == dDate &&
a.Time_Data_1 == null && a.Time_Data_2 == null)
.FirstOrDefault()
With the equivelant method:
public Staff_Time_TBL SomeMethod(DatabaseDataContext db, DateTime dDate)
{
return
db.Staff_Time_TBLs
.Where(a => a.Date_Data == dDate && a.Time_Data_1 == null && a.Time_Data_2 == null)
.FirstOrDefault();
}
This has two arguments, not one. You're also executing .FirstOrDefault(), which means you're returning Staff_Time_TBL - not IQueryable<Staff_Time_TBL>
Without knowing exactly what you're trying to do, it's likely you should re-write the field as:
private static Func<DatabaseDataContext, DateTime, Staff_Time_TBL>
queryFor =
CompiledQuery.Compile((DatabaseDataContext db, DateTime dDate) =>
db.Staff_Time_TBLs.Where(a => a.Date_Data == dDate &&
a.Time_Data_1 == null && a.Time_Data_2 == null).FirstOrDefault());

Related

"The specified type member 'Date' is not supported in LINQ to Entities" [duplicate]

This question already has answers here:
The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties
(11 answers)
Closed 3 years ago.
I started an asp.net mvc project and I wanna filter requesting data from database with dynamic linq/lambda. Some part of my code is as below:
IQueryable<VehicleMilage> query = null;
if (Fr_DeviceId == null && Fr_ContractId == null && From_Date == null && To_Date == null && From_QueryDate == null && To_QueryDate == null)
{
query = (db.VehicleMilage).OrderBy(x => x.Id).Skip(skip).Take(rows);
}
else
{
query = (db.VehicleMilage);
if (Fr_DeviceId != null)
{
int a = int.Parse(Fr_DeviceId);
query = query.Where(x => x.Fr_DeviceId == a);
}
if (Fr_ContractId != null)
{
int b = int.Parse(Fr_ContractId);
query = query.Where(x => x.Fr_ContractId == b);
}
if (From_Date != null)
{
query = query.Where(x => x.From_Date.Date >= FromDate_ConvertedToDateTime1.Date);
}
if (To_Date != null)
{
query = query.Where(x => x.To_Date.Date <= ToDate_ConvertedToDateTime1.Date);
}
if (From_QueryDate != null)
{
query = query.Where(x => x.CreateDate.Date >= FromQueryDate_ConvertedToDateTime1.Date);
}
if (To_QueryDate != null)
{
query = query.Where(x => x.CreateDate.Date <= ToQueryDate_ConvertedToDateTime1.Date);
}
query = query.OrderBy(x => x.Id).Skip(skip).Take(rows);
}
My data in database is at least 2000000 records and I must define the variable query as IQueryable.
In block of else I must filter the query with every parameter that is not null but I encounter the error
The specified type member 'Date' is not supported in LINQ to Entities.
If I define the variable of query as List, then I could put ToList() before Where In every if in block of else that is related to datetime as below but in this project because of heavy data I cannot do this:
query = query.ToList().Where(x => x.From_Date.Date >= FromDate_ConvertedToDateTime1.Date);
query = query.Where(x => x.From_Date.Date >= FromDate_ConvertedToDateTime1.Date);
could be replaced with:
var fromDate = FromDate_ConvertedToDateTime1.Date;
query = query.Where(x => x.From_Date >= fromDate);
It has the same logical meaning, will generate efficient SQL and (most importantly) it won't experience the error you are seeing.
For less than or equal to, it is slightly more complicated - but not markedly so.
query = query.Where(x => x.To_Date.Date <= ToDate_ConvertedToDateTime1.Date);
could be replaced with:
var dayAfterToDate = ToDate_ConvertedToDateTime1.Date.AddDays(1)
query = query.Where(x => x.To_Date < dateAfterToDate);
That logic might seem odd at first glance - but if it is before the date after the ToDate that is logically equivalent to being on or before the ToDate (ignoring the time component).
Repeat the same process for the other queries - they will follow the exact same pattern.

Converting string to nullable DayOfWeek field using Linq cause an error

In my sql database WorkDay field is in string format and in model it is nullable DayOfWeek, i.e public DayOfWeek? WorkDay { get; set; }. While Converting database WorkDay field into model WorkDay field it will generate an error like:
Could not translate expression 'Table(StaffSchedule)' into SQL and
could not treat it as a local expression.
I have also tried to create three different linq statements which are as below.
1) Retrieve Data from StaffSchedule table.
2) Apply select operation on it.
3) Apply AddRange operation on selected data.
results.AddRange(dataContext.StaffSchedules
.Where(x => !x.Excluded)
.Where(x => x.DistrictID == districtId && x.District.Active && (x.Position == positionTeacher || x.Position == positionDirector || x.Position == positionAssistant || x.Position == positionAssistantDirector))
.Select(x => new Core.StaffSchedule()
{
ID = x.ID,
Staff = x.Staff.SelectSummary(),
Position = (StaffPosition)Enum.Parse(typeof(StaffPosition), x.Position, true),
Class = refs.Class,
District = x.District.SelectSummary(),
Time = null,
Reoccurring = false,
Inherited = true,
ReoccourringStart = x.ReoccourringStart,
ReoccourringEnd = x.ReoccourringEnd,
WorkDay = x.WorkDay == null ? (DayOfWeek?)null : (DayOfWeek)Enum.Parse(typeof(DayOfWeek), x.WorkDay, true)
}));
This is the conversion code for string to nullable DayOfWeek field. Which cause an error in my case.
WorkDay = x.WorkDay == null ? (DayOfWeek?)null : (DayOfWeek)Enum.Parse(typeof(DayOfWeek), x.WorkDay, true)
I have already gone through below link.
How to solve issue "Could not translate expression ...into SQL and could not treat it as a local expression."
Try to convert dataContext.StaffSchedules to IEnumerable by calling ToList()
method before making the query like this
results.AddRange(dataContext.StaffSchedules.ToList()
.Where(x => !x.Excluded)....the rest of you query
Search for difference between IEnumerable and IQueryable for more detailed explain
You can't translate any C# code to SQL so x.WorkDay == null ? (DayOfWeek?)null : (DayOfWeek)Enum.Parse(typeof(DayOfWeek), x.WorkDay, true) won't work in Linq to Entities.
Try to select your data after the query execution by writing AsEnumerable() before Select. Don't do it at the beginning of the query because you will fetch all the data from a db table.
results.AddRange(dataContext.StaffSchedules
//everything (well almost) from this point is going to be translated into SQL
.Where(x => !x.Excluded)
.AsEnumerable() //everything from here is going to be executed after the query ends so it can be any C# code
.Select(x => new Core.StaffSchedule()
{
//now this should work
WorkDay = x.WorkDay == null ? (DayOfWeek?)null : (DayOfWeek)Enum.Parse(typeof(DayOfWeek), x.WorkDay, true)
});

Nullable object must have a value in Linq where clause

Why would a nullable int give this error using linq?
public void test(int? variableId)
{
var date = _dbContext.Set<MyEvent>()
.Where(x => x.Calendar.id == (variableId.HasValue ? variableId : x.Calendar.id))
.ToList();
}
variableId.HasValue is false
variableId is null
You should write it as (variableId.HasValue ? variableId.Value : x.Calendar.id)
Assuming variableId is null, then you have a very funny expression .Where(x => x.Calendar.id == x.Calendar.id) that means - all records. The problem with your code is that having your original where expression most likely cause client side filtering but not sql side filtering. It is better to rewrite it like:
var date = variableId.HasValue
?_dbContext.Set<MyEvent>().Where(x => x.Calendar.id == variableId.Value))
:_dbContext.Set<MyEvent>();

Proper way to use LINQ for this type of query?

I was originally using a foreach loop and then for each element in the loop, I perform a LINQ query like so:
foreach (MyObject identifier in identifiers.Where(i => i.IsMarkedForDeletion == false))
{
if (this.MyEntities.Identifiers.Where(pi => identifier.Field1 == pi.Field1 && identifier.Field2 == pi.Field2 && identifier.Field3 == pi.Field3).Any())
{
return false;
}
}
return true;
Then I modified it like so:
if (identifiers.Any(i => !i.IsMarkedForDeletion && this.MyEntities.Identifiers.Where(pi => i.Field1 == pi.Field1 && i.Field2 == pi.Field2 && i.Field3 == pi.Field3).Any()))
{
return false;
}
return true;
My question is this still the wrong way to use LINQ? Basically, I want to eliminate the need for the foreach loop (which seems like I should be able to get rid of it) and also make the DB query faster by not performing separate DB queries for each element of a list. Instead, I want to perform one query for all elements. Thanks!
You can change your code in this way, and it will be converted to SQL statement as expected.
To prevent runtime errors during transformation, it will be better to save DBSet to the IQueryable variable; identifiers should be IQueryable too, so you should change your code into something like this (to be honest, Resharper converted your foreach in this short labda):
IQueryable<MyObject2> identifiers = MyEntities.Identifiers.Where(i => i.IsMarkedForDeletion == false);
IQueryable<MyObject2> ids = MyEntities.Identifiers.AsQueryable();
return identifiers.All(identifier => !ids.Any(pi => identifier.Field1 == pi.Field1 && identifier.Field2 == pi.Field2 && identifier.Field3 == pi.Field3));
If identifiers is in memory collection you can change code in this way (hope that fields are string):
IQueryable<MyObject2> ids = MyEntities.Identifiers.AsQueryable();
string[] values = identifiers.Where(i => i.IsMarkedForDeletion == false).Select(i => String.Concat(i.Field1, i.Field2, i.Field3)).ToArray();
return !ids.Any(i => values.Contains(i.Field1 + i.Field2 + i.Field3));
Unfortunately your modified version will be executed exactly the same way (i.e. multiple database queries) as in the original foreach approach because EF does not support database query with joins to in memory collection (except for primitive and enumeration type collections), so if you try the most logical way
bool result = this.MyEntities.Identifiers.Any(pi => identifiers.Any(i =>
!i.IsMarkedForDeletion &&
i.Field1 == pi.Field1 && i.Field2 == pi.Field2 && i.Field3 == pi.Field3));
you'll get
NotSupportedException: Unable to create a constant value of type 'YourType'. Only primitive types or enumeration types are supported in this context.
The only way to let EF execute a single database query is to manually build a LINQ query with Concat per each item from in memory collection, like this
IQueryable<Identifier> query = null;
foreach (var item in identifiers.Where(i => !i.IsMarkedForDeletion))
{
var i = item;
var subquery = this.MyEntities.Identifiers.Where(pi =>
pi.Field1 == i.Field1 && pi.Field2 == i.Field2 && pi.Field3 == i.Field3);
query = query != null ? query.Concat(subquery) : subquery;
}
bool result = query != null && query.Any();
See Logging and Intercepting Database Operations of how to monitor the EF actions.
I would use it as follows:
if (identifiers.Where(i => !i.IsMarkedForDeletion &&
this.MyEntities.Identifiers.Field1 == i.Field1 &&
this.MyEntities.Identifiers.Field2 == i.Field2 &&
this.MyEntities.Identifiers.Field3 == i.Field3).Any()))
{
return false;
}
return true;
I hope this helps. Even though it is more to type out, it is more understandable and readable then using multiple 'where' statements.

Custom function in Entity Framework query sometimes translates properly, sometimes doesn't

I have this function:
public static IQueryable<Article> WhereArticleIsLive(this IQueryable<Article> q)
{
return q.Where(x =>
x != null
&& DateTime.UtcNow >= x.PublishTime
&& x.IsPublished
&& !x.IsDeleted);
}
And it works just fine in this query:
from a in Articles.WhereArticleIsLive()
where a.Id == 5
select new { a.Title }
But it doesn't work in this only slightly more complex query:
from s in Series
from a in Articles.WhereArticleIsLive()
where s.Id == a.SeriesId
select new { s, a }
I get this error message:
NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable1[TheFraser.Data.Articles.Article] WhereArticleIsLive(System.Linq.IQueryable1[TheFraser.Data.Articles.Article])' method, and this method cannot be translated into a store expression.
Any idea why? Is there another way to consolidate query parameters like this?
Thanks in advance.
EDIT: corrections by Craig.
I'm leaving this here, because I think it's a valuable tool: Use linqkit! But not for solving this question though :-)
Instead of returning IQueryable, use Expression to factor out predicates. E.g. you could define the following static method on Article:
public static Expression<Func<Article,bool>> IsLive()
{
return x =>
x != null
&& DateTime.UtcNow >= x.PublishTime
&& x.IsPublished
&& !x.IsDeleted
}
Then, ensure to store a reference to this expression when building your query, something along the lines of (not tested):
var isLive = Article.IsLive();
from s in Series
from a in Articles.Where(isLive)
where s.Id == a.SeriesId
select new { s, a }

Categories