Converting string to nullable DayOfWeek field using Linq cause an error - c#

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

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.

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

Compiled Query in LINQ error?

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

Can't compare dates using SQLite with EF6

I'm trying to compare dates using Linq to Entities on a SQLite database. The following code works, but I need to trim off the time portion to get the correct results.
return (from c in Context.Car
join d in Context.Driver on c.CarID equals d.DriverID
join r in Context.Rides on c.CarID equals r.RideID into rideJoin
from rides in rideJoin.DefaultIfEmpty()
where c.IsActive && d.IsActive
group rides by new { c.CarID, d.FullName, d.HireDate, d.FirstPayRiseDate } into grp
select new MyCustomClass
{
CarID = grp.Key.CarID,
Driver = grp.Key.FullName,
NumberOfRides = grp.Count(x => x != null && x.RideDate >= grp.Key.HireDate && x.RideDate <= grp.Key.FirstPayRiseDate)
}).OrderBy(x => x.Driver ).ToList();
I've tried using System.Data.Entity.DBFunctions like so and I get this error:
NumberOfRides = grp.Count(x => x != null && DbFunctions.TruncateTime(x.RideDate) >= grp.Key.HireDate && DbFunctions.TruncateTime(x.RideDate) <= grp.Key.FirstPayRiseDate)
SQL logic error or missing database no such function: TruncateTime
I also get the same error with DBFunctions.DiffDays()
I've also tried casting to Date like so and get this error:
NumberOfRides = grp.Count(x => x != null && x.RideDate.Date >= grp.Key.HireDate && x.RideDate.Date <= grp.Key.FirstPayRiseDate)
'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported
What gives? How am I supposed to do Date functions in Linq to Entities with SQLite??
I need to trim off the time portion to get the correct results
No you don't. If you want to include the rows from startDate through endDate inclusive then just use
... && x.RideDate >= startDate && x.RideDate < endDate.AddDays(1)
(Note that the second comparison is now "strictly less than".)
How are you storing dates on the database ? as unix time integrs ?
in that acse you can amend your connection string to include this following config setting and it will make it easy to read the datetime value via EF.
datetimeformat=UnixEpoch;datetimekind=Utc
So something like :
data source="|DataDirectory|\data.sqlite";datetimeformat=UnixEpoch;datetimekind=Utc
Ref: https://stackoverflow.com/a/24323591/3660930

Avoiding null exception in EF that is thrown by your query

I have a query like this :
result =
firstIdeaRepository.FindBy(
i => i.FirstIdeaState == FirstIdeaState && i.Date >= start && i.Date <= end)
.AsEnumerable()
.Select(j => new RptListOfCompanyBasedOnFirstIdeaState()
{
Name =
companyRepository.FindBy(i => i.UserId == j.UserId)
.FirstOrDefault()
DateOfMeeting =
calenderRepository.ConvertToPersianToShow(
meetingReposiotry.FindBy(s => s.FirstIdeaId == j.Id)
.FirstOrDefault()
.Date),
DateOfExit =
calenderRepository.ConvertToPersianToShow(j.DateOfExit.Value),
ReasonOfExit = j.ReasonOfExit,
}).ToList();
return result;
As you can see i use FirstOrDefault() and j.DateOfExit.Value and sometimes my Date doesn't have any values or sometime my other variables are null too because i use firstordefaut() like
companyRepository.FindBy(i => i.UserId == j.UserId).FirstOrDefault().
So my query throws a null exception and the result can't be created ,how can i handle this exception and for example if the .NET detects the null value ignores it by default or uses a default values for that ?
Best regards.
I would make the following changes:
result =
firstIdeaRepository.FindBy(
i => i.FirstIdeaState == FirstIdeaState && i.Date >= start && i.Date <= end)
.AsEnumerable()
.Select(j => new RptListOfCompanyBasedOnFirstIdeaState()
{
Name =
companyRepository.FindBy(i => i.UserId == j.UserId)
.FirstOrDefault()
DateOfMeeting =
callenderRepository.ConvertToPersianToShow(
meetingReposiotry.FindBy(s => s.FirstIdeaId == j.Id)
// project a new sequence first, before calling `FirstOrDefault`:
.Select(s => s.Date)
.FirstOrDefault(),
DateOfExit =
j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value) :
null,
ReasonOfExit = j.ReasonOfExit,
}).ToList();
When you use FirstOrDefault, there's a possibility that you'll get null back (in the case of reference types), and so you need to plan for that in your code.
For example, when assigning DateOfMeeting, you could project the results (using .Select) before using .FirstOrDefault, so that you're not ever accessing the Date property on what could be a null value.
As for DateOfExit, I've used the conditional operator to determine whether to call the calendarRepository's method at all. This assumes that DateOfExit is nullable.
Unrelated: "Calendar" is spelled with one "l" and not two.
Since you're using a nullable date, you can try filtering by values that have date, something like:
.FindBy(s => s.FirstIdeaId == j.Id && s.Date.HasValue)
This will ensure that you don't get any records with null date.
As I mentioned in comments, other cases need to be handled on case-by-case basis. Judging by the code you've shown, maybe you can handle Name as:
Name = companyRepository.FindBy(i => i.UserId == j.UserId).FirstOrDefault() ?? "anonymous";
and so on.
Another example:
If you do want to get the record even if DateOfMeeting is null, then add a check for HasValue in subsequent part or default it to some date:
DateOfExit = j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
: (DateTime)null, // you need to make `DateOfExit` nullable and then handle that downstream
// or (default with current date)
DateOfExit = j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
: callenderRepository.ConvertToPersianToShow(DateTime.Now),
// or (default with empty date)
DateOfExit = j.DateOfExit.HasValue ?
callenderRepository.ConvertToPersianToShow(j.DateOfExit.Value)
: callenderRepository.ConvertToPersianToShow(new DateTime()),
Moral of the story: figure out what the default value should be in case of null and then substitute that accordingly in the query when calling FirstOrDefault().
The broadest solution would be to use the idea of an null object with the DefaultIfEmpty<T>(T DefaultValue) method in your query. An example would be:
var defaultMeeting = new Meeting() { Date = new DateTime() };
var dateOfMeeting = meetingRepository.FindBy(s => s.FirstIdeaId == j.Id)
.DefaultIfEmpty(defaultMeeting)
.FirstOrDefault()
.Date;

Categories