??(null-coalescing operator) between EF6 Queries - c#

I am developing an MVC 5 app using EF 6. I want to query my database and store a value in a variable and if there is null as per the the given condition then another query should be executed to return a value. In my method I have as follows: D1 is coming as a parameter and it has the current date.
int otherYear = D1.Year + 1;
lastNo = (db.ABC.ToList().LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear).S1) ?? (db.ABC.ToList().LastOrDefault(x => x.D1.Value.Month > 6 && x.D1.Value.Year == D1.Year).S1);
Now the first query before ?? operator executes but when there is null against the condition specified the system throws an error of object reference and does not execute the second query after ?? operator. How can I solve it?
I know that it can be solved by making an if-else condition and within that I should first check that if there is any data using .Any() function. But in that case I have to query my database for minimum of 2 times. Once in .Any() to check the availability of data and second to fetch that data. But I have a hefty database and I don't want to make extra queries.
Regards

You should not call db.ABC.ToList() before applying LastOrDefault() because it will load the whole database to memory and do the processing from there.
You're getting null reference exception because db.ABC.LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear) is null so that you cannot get S1 property.
For your question, I think you can use this code:
int otherYear = D1.Year + 1;
lastNo = (db.ABC.LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear)?.S1) ?? (db.ABC.LastOrDefault(x => x.D1.Value.Month > 6 && x.D1.Value.Year == D1.Year)?.S1);

To avoid the double query issue, store the result of the query in a list:
int otherYear = D1.Year + 1;
List<T> myABC = db.ABC.ToList();
lastNo = (myABC.LastOrDefault(x => x.D1.Value.Month <= 6 && x.D1.Value.Year == otherYear).S1) ?? (myABC.LastOrDefault(x => x.D1.Value.Month > 6 && x.D1.Value.Year == D1.Year).S1);

Related

EF Core declaring multiple variables on complex Where Clause

I have this LINQ Where clause that is declaring 2 variables on the SQL query
var parkingLotPrice =
_context.ParkingLotPrice
.Where(x => currentDate >= x.EffectiveDate && (currentDate <= x.ExpiryDate || x.ExpiryDate == null))
.ToQueryString();
It generates this SQL Query:
DECLARE #__currentDate_0 datetime2 = '2021-07-21T17:48:29.1106534-06:00';
DECLARE #__currentDate_1 datetime2 = '2021-07-21T17:48:29.1106534-06:00';
SELECT [p].[ParkingLotId],
[p].[PriceScheduleId],
[p].[EffectiveDate],
[p].[ExpiryDate]
FROM [ParkingLotPrice] AS [p]
WHERE (#__currentDate_0 >= [p].[EffectiveDate]) AND ((#__currentDate_1 <= [p].[ExpiryDate]) OR [p].[ExpiryDate] IS NULL)
Note: the declarations contains the same value.
The problem is the (currentDate <= x.ExpiryDate || x.ExpiryDate == null).
If I remove the null evaluation, it only declares 1 variable.
DECLARE #__currentDate_0 datetime2 = '2021-07-21T17:32:31.3980763-06:00';
SELECT [p].[ParkingLotId],
[p].[PriceScheduleId],
[p].[EffectiveDate],
[p].[ExpiryDate]
FROM [ParkingLotPrice] AS [p]
WHERE (#__currentDate_0 >= [p].[EffectiveDate]) AND (#__currentDate_0 <= [p].[ExpiryDate])
Is there a way of keeping the Where evaluation, but only declare 1 variable?
And what is wrong with it? Does it return a wrong result? I created hundreds queries with null and without and they always returned the right result.
I can see the real problem in your query. The way you compare dates. It will compare times too. In some cases it will return a wrong result. I highly recommend you to compare only dates
var parkingLotPrice = _context.ParkingLotPrice
.Where(x => EF.Functions.DateDiffDay(x.EffectiveDate,currentDate) >=0
&& (x.ExpiryDate == null || EF.Functions.DateDiffDay(currentDate,x.ExpiryDate)>=0 )).ToList();
or if you need some time try this
var parkingLotPrice = _context.ParkingLotPrice
.Where(x => EF.Functions.DateDiffMinute(x.EffectiveDate,currentDate) >=0
&& (x.ExpiryDate == null || EF.Functions.DateDiffMinute(currentDate,x.ExpiryDate)>=0 )).ToList();

Linq Where fails when trying to check a range

When trying to check if a variable is within a range the code does not work.
This is a project in Radzen, Linq is interacting with a mysql database
This does not work:
var QuantQ = dbContext.CnpjCnaes.Where(x => x.CAPITAL_SOCIAL < CapMaximo && x.CAPITAL_SOCIAL > CapMinimo && x.CNAE_FISCAL == Parametros[4] && x.COD_MUNICIPIO == CidadeQ.ToArray()[0].COD_MUNICIPIO.ToString());
But this does:
var QuantQ = dbContext.CnpjCnaes.Where(x => x.CAPITAL_SOCIAL < CapMaximo && x.CNAE_FISCAL == Parametros[4] && x.COD_MUNICIPIO == CidadeQ.ToArray()[0].COD_MUNICIPIO.ToString());
Well I expected to get a range between CapMinimo and CapMaximo, but it seems that when I try to check a range it always fails.

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

Entity Framework - Use condition statements in query

Good Day Guys,
I have three lists (ICollection of strings). My intention is to fetch results from the db based on the values stored in these lists using linq to entities. Below is my snippet
int entityCriteriaCount = entityCriteria == null ? 0 : entityCriteria.Count();
int operationCriteriaCount = operationCriteria == null ? 0 : operationCriteria.Count();
int roleCriteriaCount = roleCriteria == null ? 0 : roleCriteria.Count();
// Where entityCriteria,operationCriteria and roleCriteria are the above mentioned lists
Data Query:
var auditItems = db.AuditTrails.Where(a => (entityCriteriaCount > 0 ? reportCriteria.EntityTypes.Contains(a.EntityType) : a.EntityType.Contains(""))
&& (roleCriteriaCount > 0 ? reportCriteria.Roles.Contains(a.UserRole) : a.UserRole.Contains(""))
&& (operationCriteriaCount > 0 ? reportCriteria.Operations.Contains(a.UserAction) : a.UserAction.Contains(""))
&& EntityFunctions.TruncateTime(a.TimeStamp) >= startDate
&& EntityFunctions.TruncateTime(a.TimeStamp) <= endDate).OrderByDescending(a => a.TimeStamp).ToList();
The goal of the above query is to check if the list is empty before attempting to fetch records based on its content. The above works perfectly if all the lists have entries. It however fails if any or all of them are empty.
How can I make this work i.e Query the database using the lists if there are entries and fetch all entries for the criteria if the list is empty.
Any help will be appreciated. Thanks guys
You don't have to do the whole query in one go, break it down and only filter when you need to:
var auditItems = db.AuditTrails.Where(EntityFunctions.TruncateTime(a.TimeStamp) >= startDate && EntityFunctions.TruncateTime(a.TimeStamp) <= endDate);
if(entityCriteriaCount > 0)
auditItems = auditItems.Where(a => reportCriteria.EntityTypes.Contains(a.EntityType));
if(roleCriteriaCount > 0)
auditItems = auditItems.Where(a => reportCriteria.Roles.Contains(a.UserRole));
if(operationCriteriaCount > 0)
auditItems = auditItems.Where(a => reportCriteria.Operations.Contains(a.UserAction));
var results = auditItems.OrderByDescending(a => a.TimeStamp).ToList();

Why does this LINQ statement throw a timeout error?

I am getting this error when I execute this line, through break points I detected this error.
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
int? dressSerialNo;
var lstDress = (
from yy in currContext.OrderDressings
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select yy
).ToList();
if (lstDress.Count > 0)
{
dressSerialNo = (
from yy in lstDress
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select (int?)yy.SrNo
).Max();
dressSerialNo += dressSerialNo + 1;
}
else dressSerialNo = 1;
solution:- In my project I was mainlining transaction in two for some module old method with ado.net and for newly developed modules I was using the entity framework so it was creating problem in transaction. So it went aberrant.
You are using Linq-To-Entities. There is an issue with the connection to your database server. Common causes for this are:
The query is taking longer than the timeout specified in the context.
There are network related issues causing a delay.
You can optionally change the command timeout (see this question about how to do this).
You don't want to materialize your all your entities using .ToList().
You could write a single query that returns only what you're interested in:
// Get the entity that contains the max value, using Ordering
var myMaxIfAny = currContext.OrderDressings
.Where(yy => yy.OrderID == this.OrderID && yy.OrderItemID == this.orderItemID && yy.ProductID == this.ProductID)
.OrderByDescending(z => z.SrNo)
.FirstOrDefault();
if (myMaxIfAny != null)
{
// Then you got a value, retrieve the Max using myMaxIfAny.SrNo
// ...
}
else
{
// ...
}
I've formatted and commented your code:
int? dressSerialNo;
// Get all OrderDressings with matching OrderID, orderItemID and ProductID as a List<OrderDressing>
var lstDress = (from yy in currContext.OrderDressings
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select yy)
.ToList();
// If any were found,
if (lstDress.Count > 0)
{
// Execute the Where again (what else will the list contain?) and select all yy.SrNo
dressSerialNo = (from yy in lstDress
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select (int?)yy.SrNo)
.Max(); // And take the Max() of that
// Add dressSerialNo + 1 to dressSerialNo.
dressSerialNo += dressSerialNo + 1;
}
else dressSerialNo = 1;
Which seems it can be corrected and reduced to:
int? serialNumber = (from yy in currContext.OrderDressings
where yy.OrderID == this.OrderID
&& yy.OrderItemID == this.orderItemID
&& yy.ProductID == this.ProductID
select yy.SrNo)
.DefaultIfEmpty() // Might not be necessary
.Max();
if (!serialNumber.HasValue)
{
serialNumber = 1;
}
else
{
serialNumber++;
}
Please note this can cause concurrency issues if two people execute this at the same time.
The query against the database is taking too long. There are many reasons as to why this could be happening.
Try running the sql statement generated from the linq directly to the database to see if it takes as long.
Check and see if any of your columns have massive data. (like a string column filled with large volume of data)
Meanwhile try adding this to end of your connection string
Connection Timeout=30000;
I have found the issue it was creating time out problem,
in my application transaction was maintained in 2 style,
one with old ado.net style, and another with EF style,
so it created a chaos. I am to make it uniform all to change in entity-framework.

Categories