Comparing SQL Date Time in Entity Framework by date parts - c#

I have this query that was actually a view but I wanted to control the WHERE clause so I decided to tackle it by LINQ and EF6:
SELECT NULLIF(SUM([a].[REQUESTED_QTY]), 0) AS [Transaction],
NULLIF(SUM([a].[ITEM_TOTAL]), 0) AS [Income]
FROM [dbo].[BILL_INFO_DETAIL] AS [a]
INNER JOIN [dbo].[SERVICE_INFO] AS [b]
ON [a].[SERVICE_CODE] = [b].[SERVICE_CODE]
WHERE ([a].[INPUT_STATUS] = '1')
AND ([b].[SERVICE_CODE] IN ( 1610, 1611, 1612 ))
AND ([a].[PAY_MODE_ID] IN ( 1, 2 ))
AND (CONVERT(VARCHAR(2), a.STAMP_DATE, 101) IN ( '10', '11', '12' ))
AND (CONVERT(VARCHAR(4), a.STAMP_DATE, 102) IN ( '2017' ))
AND ([b].[FEE] > 1)
After a while of trial and error, I got this conversion:
public async Task<ViewGuessOnline> GetGuessOnline(int[] serviceCodes, byte[] payModes, string[] months, string year)
{
try
{
using (var context = new FinanceConnection())
{
var resultTemp = from a in
(from a in context.BILL_INFO_DETAILS
where
a.INPUT_STATUS == true &&
serviceCodes.Contains(a.SERVICE_INFO.SERVICE_CODE) &&
payModes.Contains(a.PAY_MODE_ID) &&
months.Contains(SqlFunctions.StringConvert(a.STAMP_DATE)) &&
(new[] {"2017"}).Contains(SqlFunctions.StringConvert(a.STAMP_DATE)) &&
a.SERVICE_INFO.FEE > 1
select new
{
a.REQUESTED_QTY,
a.ITEM_TOTAL,
Dummy = "x"
})
group a by new {a.Dummy}
into g
select new
{
Transaction = g.Sum(p => p.REQUESTED_QTY) == 0 ? (int?) null : (int) g.Sum(p => p.REQUESTED_QTY),
Income = g.Sum(p => p.ITEM_TOTAL) == 0 ? (decimal?) null : (decimal) g.Sum(p => p.ITEM_TOTAL)
};
// result = (await result.OrderByDescending(o => o.CustomerCode).ToListAsync()).AsQueryable();
}
Logger.Info($"{LayerName} -> {callerInfo.MethodName} -> Returning");
return result;
}
catch (Exception exp)
{
Logger.Error($"{LayerName} -> {callerInfo.MethodName} -> Exception [{exp.Message}]", exp);
throw;
}
}
I'm having an issue with the date part. In original SQL, the comparison of a Quarter 4 of 2107 is very easy. But I cannot find a proper inline linq conversion that translates to proper SQL.
Also, I had to use a dummy grouping even thoug there is not groups in the original SQL.

A bit more wall-baning and I was able to make this work:
from a in
(from a in db.BILL_INFO_DETAIL
where
a.INPUT_STATUS == true &&
(new int[] {1610, 1611, 1612 }).Contains(a.SERVICE_INFO.SERVICE_CODE) &&
(new int[] {1, 2 }).Contains(a.PAY_MODE_ID) &&
a.STAMP_DATE != null &&
(new int[] {10, 11, 12 }).Contains(a.STAMP_DATE.Value.Month) &&
a.STAMP_DATE.Value.Year == 2017 &&
a.SERVICE_INFO.FEE > 1
select new
{
a.REQUESTED_QTY,
a.ITEM_TOTAL,
Dummy = "x"
})
group a by new {a.Dummy}
into g
select new
{
Transaction = g.Sum(p => p.REQUESTED_QTY) == 0 ? (int?) null : (int) g.Sum(p => p.REQUESTED_QTY),
Income = g.Sum(p => p.ITEM_TOTAL) == 0 ? (decimal?) null : (decimal) g.Sum(p => p.ITEM_TOTAL)
}
I changed the method's parameter datatypes accordingly and this works now.

Related

Linq Query Throws Timeout in code but Works fine on LinqPad

When I run the Linq query on LinqPad it takes only 4-5 seconds to return 5000 rows but when I run that same query in my code using Entity Framework it throws timeout.
What could be the possible issues?
Thanks in advance.
Below is the query:
var resultSale =
from product in Products
join productInfo in ProductInfoSummaries on product.ID equals productInfo.ProductID
join productDetail in ProductDetails on new { Id = product.ID, storeId = product.CreatedInStore } equals new { Id = productDetail.ProductID, storeId = productDetail.StoreID }
join productInventoryOtherStore in InventoryOtherStores on product.ID equals productInventoryOtherStore.ProductID
into productInventories
from productInventoryOtherStore in productInventories.DefaultIfEmpty()
join saleLine in SaleLines on productDetail.ID equals saleLine.ArtikkelNr
join sales in Sales on saleLine.OrderID equals sales.ID
where saleLine.ArtikkelNr != null
&& saleLine.DatoTid >= new DateTime(2018, 01, 01)
&& saleLine.DatoTid <= new DateTime(2019,11,21)
&& sales.StoreID == 14
&& (sales.OrderType == 1 || sales.OrderType == 2 || sales.OrderType == 4 || sales.OrderType == 6)
&& productDetail.SupplierProductNo != null
&& productDetail.Deleted == null
&& (productInfo.Inactive == null || productInfo.Inactive == false)
&& (product.CreatedInStore == 14 || product.CreatedInStore == 0 || product.CreatedInStore == null)
group new { saleLine.AntallEnheter, sales.OrderType } by new { product.ID, productInventoryOtherStore.Amount } into g
select new ProductSaleSummaryVM
{
ID = g.Key.ID,
Inventory = (double)g.Key.Amount,
TotalSold = g.Sum(x => x.OrderType !=4 ? x.AntallEnheter : 0) ?? 0,
TotalWastage = g.Sum(x => x.OrderType ==4 ? x.AntallEnheter : 0) ?? 0,
TotalOrderedQty = 0
};
var resultSupplierOrder =
from supplierOrderLine in SupplierOrderLines
join supplierOrder in SupplierOrders on supplierOrderLine.SupplierOrderID equals supplierOrder.ID
where supplierOrderLine.Deleted == null
&& supplierOrder.Status != 1
&& supplierOrder.StoreID == 14
group supplierOrderLine by supplierOrderLine.ProductID into g
select new ProductOrderDetailsVM
{
ID = g.Key,
TotalOrderedQty = (double)g.Sum(x => x.ConsumerQuantity - x.QuantiyReceived)
};
var r =
(from resSale in resultSale
join resSupplierOrder in resultSupplierOrder on resSale.ID equals resSupplierOrder.ID
into resSupplierOrders
from resSupplierOrder in resSupplierOrders.DefaultIfEmpty()
orderby resSale.ID
select new ProductSaleSummaryVM
{
ID = resSale.ID,
Inventory = resSale.Inventory,
TotalSold = resSale.TotalSold,
TotalWastage = resSale.TotalWastage,
TotalOrderedQty = resSupplierOrder.TotalOrderedQty ?? 0
})
.Where(x => x.Inventory + x.TotalOrderedQty < x.TotalSold);
r.Dump();
Try using entity framework within linqpad see if it gives you any clues. see this link on how to use entity framework in linqpad
https://www.linqpad.net/EntityFramework.aspx
This behaviour could be related to parameter sniffing - check this article for details

Entity Framework grouped query translated into multiple SELECTs

Can someone help and tell me why this LINQ query is being translated in EF to multiple SELECTs ?
var query = db.ReportedFulfillments.GroupBy(x => x.ContractId).Select(grouping => new
{
ContractId = grouping.Key,
Total = grouping.Count(),
FU = grouping.Count(x => x.Month == 1 && x.Value == 1),
BR = grouping.Count(x => x.Month == 1 && x.Value == 2)
}
I thought that EF will output something like this:
SELECT ContractId,
Count(*) AS Total,
COUNT(CASE WHEN [Month] = 1 AND [Value] = 1 THEN Value END) AS FU,
COUNT(CASE WHEN [Month] = 1 AND [Value] = 2 THEN Value END) AS BR,
FROM ReportedFulfillments GROUP BY ContractId
I'm using EntityFramework 6.2.0
Shortly, LINQ conditional Count of the GroupBy grouping result set is not supported very well (the SQL you expect relies on SQL COUNT(expr) which excludes NULLs and has no LINQ equivalent).
But the equivalent conditional Sum is supported and translated just well, so instead of
FU = grouping.Count(x => x.Month == 1 && x.Value == 1),
BR = grouping.Count(x => x.Month == 1 && x.Value == 2)
use
FU = grouping.Sum(x => x.Month == 1 && x.Value == 1 ? 1 : 0),
BR = grouping.Sum(x => x.Month == 1 && x.Value == 2 ? 1 : 0)

Sum multiple column in LINQ

How can I reproduce this query using linq?
SELECT
SUM(SecondsSinceLastEvent) as AccessTime,
SUM (case when DownloadType = 0 then 1 end) FileDownloadCount,
SUM (case when DownloadType = 1 then 1 end) KortextDownloadCount,
SUM (case when Action = 'print' then 1 end) PrintCountFROM EReaderLogs WHERE PublishedContent_Id = XXX
In LINQ to Entities you need to first use GroupBy before using multiple aggregate functions. To sum all the elements in a column you can group by some static key so then a single group would be a whole table:
var query = context.EReaderLogs
.Where(e => e.PublishedContent_Id == someValue)
.GroupBy(a => 1, (k, g) =>
{
AccessTime = g.Sum(e => e.SecondsSinceLastEvent),
FileDownloadCount = g.Sum(e => e.DownloadType == 0 ? 1 : 0),
KortextDownloadCount = g.Sum(e => e.DownloadType == 1 ? 1 : 0),
PrintCount = g.Sum(e => e.Action == "print" ? 1 : 0)
});

How to correct my code. To simplify my answer in a single array result

Here's my code. I was wondering if how can I store the value in a single variable array format. I don't know the next step. I am newly in LINQ and C#.
var result = (from x in _context.DwPropertyMasters
where
x.ShowMapPoint == "Y"
select new
{
x.LandId,
a = x.Development == null || x.Development == "" ? x.Location : x.Development,
x.MapPointX,
x.MapPointY,
AreaSize = x.AreaSize ?? 0,
Premium = x.Premium ?? 0,
b = (x.Premium == 0 ? null : x.Premium) * 100000000 / (x.AreaSize == 0 ? null : x.AreaSize),
c = (from z in _context.DwPropertyDetails
where (z.TransactionPrice > 0 || z.TransactionPrice != null) && z.LandId == x.LandId
group z by z.LandId into g
select (g.Sum(p => p.TransactionPrice) == 0 ? null : g.Sum(p => p.TransactionPrice)) / (g.Sum(p => p.ActualSize) == 0 ? null : g.Sum(p => p.ActualSize))).FirstOrDefault(),
d = (x.AreaSize2 == 0 ? null : x.AreaSize2) == 0 ? 0 : (x.Premium == 0 ? null : x.Premium) * 100000000 / (x.AreaSize2 == 0 ? null : x.AreaSize2),
x.LandType,
e = (from y in _context.DwPropertyDetails
where (y.TransactionPrice > 0 || y.TransactionPrice != null) && y.LandId == x.LandId
select new
{
a = 1
}).Count()
}).ToArray();
return View(result);
The result I get is like this:
[
{
"LandId":1,
"a":"2GETHER",
"MapPointX":"22.37607871816074",
"MapPointY":"113.96758139133453",
"AreaSize":118046,
"Premium":5.51,
"b":4667.671924,
"c":13198,
"d":4148.815215,
"LandType":"PROPERTY",
"e":169
}
]
All I want is like this:
[1,'2GETHER',22.37607871816074,113.96758139133453,118046,5.51,4668.00000000000000000000,13198,4149.00000000000000000000,'PROPERTY',169]
Something like this?
return View(new object[] {
result.LandId,
result.a,
result.MapPointX,
result.MapPointY,
result.AreaSize,
result.Premium,
result.b,
result.c,
result.d,
result.LandType,
result.e
});
Just to expand a bit on the previous answer from devio, when you do:
.Select( new { ... } ).ToArray()
You're actually telling it to give you an array of new dynamically created objects. When the dynamic object is created, it derives property names automatically for you. It ends up looking like a dictionary of key value pairs. You could try to force it to give you an object array instead of a dynamic type. Something like:
var result = _context.DwPropertyMasters
.Where( x => x.ShowMapPoint == "Y")
.Select( x => new object[] { x.LandId, x.MapPointX, ... })
.ToArray();
The difference is that instead of asking it for an array of dynamic objects, you're asking it for an array of object arrays.

Converting Sql to Linq using group get wrong result

I have a query where I need to get the count of Commpliance and NonCompliance
Here is the SQL version I need this to convert to linq...
select ScheduleClause,
COUNT(case Compliance
when 1 then 1 end) Compliance,
Count(case Compliance
when 0 then 1 end) NonCompliance
from Compliance_RiskRegisterEntry cr
where cr.RiskRegisterTypeId = 1 and Auditor = 5508 and MONTH(AuditDate) = 10 and YEAR(AuditDate) = 2013
group by ScheduleClause
I try this linq query but I get different result
compliance
.GroupBy(x => new
{
x.ScheduleClause, x.Compliance
})
.Where(x => x.Key.Compliance == 1)
.Select(x => new RiskRegisterCompliancePerCategoryDto
{
ScheduleClause = x.Key.ScheduleClause,
Compliant = x.Key.Compliance == 1 ? 1 : 0,
NonCompliant = x.Key.Compliance == 0 ? 1 : 0,
GrandTotal = x.Count()
}).ToList();
It depends on your exact column definitions. Here I'm using
Create Table Compliance_RiskRegisterEntry (
ScheduleClause varchar(10),
AuditDate datetime not null,
RiskRegisterTypeID int not null,
Auditor Int,
Compliance bit not null
);
With this, the following Linq works:
compliance
.Where(p => p.RiskRegisterTypeID == 1 &&
p.Auditor == 5508 &&
p.AuditDate.Month == 10 &&
p.AuditDate.Year == 2013
)
.GroupBy(x => x.ScheduleClause)
.Select(x => new {
ScheduleClause = x.Key,
Compliant = x.Sum(y => y.Compliance ? 1 : 0),
NonCompliant = x.Sum(y => y.Compliance ? 0 : 1),
GrandTotal = x.Count()
});
Instead of x.Sum(...) you can use x.Count(y.Compliance), but the generated query for this looks much worse than using Sum
compliance
.Where(p=> p.RiskRegisterTypeId = 1 && p.Auditor = 5508 &&
SqlFunctions.DatePart("MM", p.AuditDate) = 10 &&
SqlFunctions.DatePart("yy", p.AuditDate) = 2013)
.GroupBy(x => x.ScheduleClause)
.Select(x => new RiskRegisterCompliancePerCategoryDto
{
ScheduleClause = x.Key.ScheduleClause,
Compliant = x.Key.Compliance == 1 ? 1 : 0,
NonCompliant = x.Key.Compliance == 0 ? 1 : 0,
GrandTotal = x.Count()
}).ToList();

Categories

Resources