How to rewrite Entity Framework query to SQL Server query - sql

I have a table called passenger policy that looks like this:
public Guid HotelId { get; set; }
public int FromAge { get; set; }
public int ToAge { get; set; }
public PassengerType PassengerType { get; set; }
and it has 3 rows for each HotelId key.
I have another table called search that looks like this
public class Search : BaseEntity
{
public DateTime Date { get; set; }
public Guid CountryId { get; set; }
public Guid ProvinceId { get; set; }
public Guid CityId { get; set; }
public Guid HotelId { get; set; }
public Guid VendorHotelRoomId { get; set; }
public int StandardCapacity { get; set; }
public int ExtraCapacity { get; set; }
public int MaxInfantAge { get; set; }
public int MaxChild1Age { get; set; }
public int MaxChild2Age { get; set; }
public double BasePrice { get; set; }
public double ExtraAdultPrice { get; set; }
public double ExtraInfantPrice { get; set; }
public double ExtraChild1Price { get; set; }
public double ExtraChild2Price { get; set; }
}
I want to write a query in T-SQL (SQL Server) to get hotels based on date field, standard capacity and extra capacity.
The extra capacity has 3 possible values:
infant
child 1
child 2
(fetched from passenger type table)
I write it like this in EF Core
var searchOnAllVendors hotelContext.Search
.Where(c => c.Date >= fromDate
&& c.Date <= toDate
&& c.CityId == cityId
&& c.ExtraCapacity >= adultCount)
.AsEnumerable();
foreach (var item in searchOnAllVendors)
{
foreach (var ag in request.Passengers.ChildrensAges)
{
if (ag <= item.MaxInfantAge && ag < item.MaxChild1Age && ag < item.MaxChild2Age)
infant++;
if (ag > item.MaxInfantAge && ag <= item.MaxChild1Age)
child1Count++;
if (ag > item.MaxChild1Age && ag <= item.MaxChild2Age)
child2Count++;
if (ag > item.MaxChild1Age && ag <= item.MaxChild2Age)
extraAdult++;
}
if (item.MaxInfantAge >= infant && item.MaxChild1Age >= child1Count && item.MaxChild2Age >= child2Count)
{
var adulPrice = extraAdult * item.ExtraAdultPrice;
var infantPrice = infant * item.ExtraInfantPrice;
var child1Price = child1Count * item.ExtraChild1Price;
var child2Price = child1Count * item.ExtraChild2Price;
var finalPrice = adulPrice + infantPrice + child1Price + child2Price + item.BasePrice;
searches.Add(new Search_Response
{
CityId = item.CityId,
CountryId = item.CountryId,
HotelId = item.HotelId,
ProvinceId = item.ProvinceId,
VendorHotelRoomId = item.VendorHotelRoomId,
Price = finalPrice
});
}
}

after couple days and try a few things i find a way to have best performance in T-SQL
...
1. Get count of extra passernger types in scalar-valued functions like this :
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[GetChildRule](#ages as nvarchar(max),#min as int,#max as int)
RETURNS int
AS
BEGIN
declare #count int
select #count=count(*) from STRING_SPLIT(#ages,',')
where value > #min and value <= #max
RETURN #count
END
...
and use it in stored procedure like a field as below :
select * , GetChildRule('1,2,3',mymin,mymax) from Search where date between date1 and date2
and call it in EF CORE:
Context.Set<YourModelWithAllOfFiledYouReturnInSP>()
.FromSqlRaw($"EXEC [dbo].[StaySearch] #extraAges = N'{ages}',
#checkInDate = N'{fromDate}', #checkOutDate = N'{toDate}',
#destinationId = '{destinationId}',
#countrySearch = '{countrysearch}',#adultCount={adultCount}");

Related

how to use in clause in Linq Query and pass it dynamically from code

I am converting my project to EF Core in my old project I have a query running.
IDictionary<int, IGrouping<int, UserPurchaseItemAddonWithAmount>> addons =
context.Fetch<UserPurchaseItemAddonWithAmount>($"Select UPIA.*, EA.Amount From UserPurchaseItemAddons UPIA Inner Join ExtraAddons EA on UPIA.AddonID = EA.AddonID Where UPIA.UserPurchaseItemID in ({string.Join(',', userPurchaseItems.Select(S => S.UserPurchaseItemID))})")
.GroupBy(G => G.UserPurchaseItemID).ToDictionary(D => D.Key);
I need to convert this query in to Linq query what I am doing is below
IDictionary<int, IGrouping<int, UserPurchaseItemAddonWithAmount>> addons =
(from f in context.UserPurchaseItemAddons
join s in context.ExtraAddons
on f.AddonId equals s.AddonId
select new
{
Amount = s.Amount,
UserPurchaseItemAddonID = f.UserPurchaseItemAddonId,
UserPurchaseItemID = f.UserPurchaseItemId,
BranchItemVariantID = f.BranchItemVariantId,
AddonID = f.AddonId,
UserID = f.UserId,
IsDeleted = f.IsDeleted,
ModifiedOn = f.ModifiedOn,
ModifiedBy = f.ModifiedBy,
Reason = f.Reason,
}).GroupBy(G => G.UserPurchaseItemID).ToDictionary(D => D.Key);
This query is causing a compiler error related to casting to IGrouping<int, UserPurchaseItemAddonWithAmount> to an anonymous type. The other thing is that how can I apply in clause in where condition in above query, just like the first query .
class
public class UserPurchaseItemAddonWithAmount
{
public decimal Amount { get; set; }
public int UserPurchaseItemAddonID { get; set; }
public int UserPurchaseItemID { get; set; }
public int BranchItemVariantID { get; set; }
public int AddonID { get; set; }
public int UserID { get; set; }
public bool IsDeleted { get; set; }
public DateTime? ModifiedOn { get; set; }
public int? ModifiedBy { get; set; }
public string? Reason { get; set; }
}
Try the following query. Main mistake that you have returned anonymous class.
var purchaseItemIds = userPurchaseItems.Select(S => S.UserPurchaseItemID);
IDictionary<int, IGrouping<int, UserPurchaseItemAddonWithAmount>> addons =
(from f in context.UserPurchaseItemAddons
join s in context.ExtraAddons on f.AddonId equals s.AddonId
where purchaseItemIds.Contains(f.UserPurchaseItemID)
select new UserPurchaseItemAddonWithAmount
{
Amount = s.Amount,
UserPurchaseItemAddonID = f.UserPurchaseItemAddonId,
UserPurchaseItemID = f.UserPurchaseItemId,
BranchItemVariantID = f.BranchItemVariantId,
AddonID = f.AddonId,
UserID = f.UserId,
IsDeleted = f.IsDeleted,
ModifiedOn = f.ModifiedOn,
ModifiedBy = f.ModifiedBy,
Reason = f.Reason,
})
.AsEnumerable()
.GroupBy(G => G.UserPurchaseItemID)
.ToDictionary(D => D.Key);

Conversion of this sql query to LINQ query

I want to convert this sql query into a linq query.
SELECT
CreationUtcTime,
Speed,
convert((CreationUtcTime - LAG(CreationUtcTime) OVER (ORDER BY CreationUtcTime)), char) AS diff
FROM assetstatusrecords
WHERE
Speed <> 0.00 and
CreationUtcTime <= '2022-03-28' and
CreationUtcTime >= '2022-02-21' and
AssetId = '7556d50c-95e5-4cd5-a31d-b4d6c6ab1fb1'
ORDER BY CreationUtcTime
Model Class for LINQ
class AssetStatusRecord : Entity
{
protected AssetStatusRecord()
{
}
public AssetStatusRecord(CoordinatesValue coordinates, double speed,
LengthValue distanceTravelled, Guid sensorId, Guid? assetId,
int? heading, Guid readingId, DateTime? sensorDateTime)
{
Coordinates = coordinates;
Speed = speed;
DistanceTravelled = distanceTravelled;
SensorId = sensorId;
AssetId = assetId;
Heading = heading;
ReadingId = readingId;
SensorDateTime = sensorDateTime;
}
public CoordinatesValue Coordinates { get; private set; }
public double Speed { get; private set; }
public LengthValue DistanceTravelled { get; private set; }
public Guid SensorId { get; private set; }
public Guid? AssetId { get; private set; }
public int? Heading { get; private set; }
public Guid ReadingId { get; private set; }
public DateTime? SensorDateTime { get; private set; }
}
And the Entity Class are as follows : -
public class Entity : IEntity
{
public Entity();
public Guid Id { get; protected set; }
public long SequentialId { get; protected set; }
public DateTime CreationUtcTime { get; protected set; }
public DateTime CreationLocalTime { get; protected set; }
}
And the Interface IEntity :-
public interface IEntity
{
Guid Id { get; }
long SequentialId { get; }
DateTime CreationUtcTime { get; }
}
This model class can be used to execute linq query which I am using in below query in comments
If you are using EF Core, you can execute such query via linq2db.EntityFrameworkCore extension. Note that I'm one of the creators.
With this extension you can use LAG in LINQ query:
var query =
from s in context.AssetStatusRecord.ToLinqToDB() // switch LINQ Provider
where s.Speed != 0
&& s.CreationUtcTime <= endTime
&& s.CreationUtcTime >= startTime
&& s.AssetId == assetId
orderby s.CreationUtcTime
select new
{
s.CreationUtcTime,
s.Speed,
diff = s.CreationUtcTime -
Sql.Ext.Lag(s.CreationUtcTime)
.Over()
.OrderBy(s => s.CreationUtcTime)
.ToValue()
};
var result = query.ToList();
If for any two records A and B such that A.SequentialId < B.SequentialId the condition A.CreationUtcTime <= B.CreationUtcTime is met, then without LAG function you can do something like this:
DateTime dateFrom = DateTime.Parse("2022-02-21");
DateTime dateTo = DateTime.Parse("2022-03-28");
string assetId = "7556d50c-95e5-4cd5-a31d-b4d6c6ab1fb1";
var records =
from rec in context.AssetStatusRecords
where
rec.CreationUtcTime >= dateFrom &&
rec.CreationUtcTime <= dateTo &&
rec.Speed != 0 &&
rec.AssetId == assetId
select rec;
var query =
from rec1 in records
from rec2 in records.Where(r => rec1.SequentialId > r.SequentialId).DefaultIfEmpty()
group new { rec1, rec2 } by new { rec1.SequentialId, rec1.CreationUtcTime, rec1.Speed } into g
orderby g.Key.SequentialId
select new
{
g.Key.CreationUtcTime,
g.Key.Speed,
Diff = EF.Functions.DateDiffDay(g.Max(p => p.rec2.CreationUtcTime), g.Key.CreationUtcTime)
};
var results = query.ToList();
Note: code above works with Pomelo.EntityFrameworkCore.MySql provider.

Converting SQL query result to Linq result with an new field that is not in the Model

I have an SQL query,
SELECT
c.AssignedTo ,c.CampaignID, c.Name, c.Venue,c.StartedOn,
c.CompletedOn, c.IsOpen, COUNT(C.Name) AS Leads
FROM
Campaign AS c
RIGHT JOIN
Leads AS l ON l.CampaignID = c.CampaignID
GROUP BY
c.AssignedTo, c.CampaignID, c.Name,c.Venue,c.StartedOn, c.CompletedOn, c.IsOpen
ORDER BY
c.AssignedTo
These are my two tables in that query, CAMPAIGNS:
[Table("Campaigns")]
public class Campaigns
{
[Key]
public int CampaignID { get; set; }
[MaxLength(30)]
public string Name { get; set; }
[MaxLength(200)]
public string Venue { get; set; }
[ForeignKey("UsersReference")]
public int AssignedTo { get; set; }
public DateTime StartedOn { get; set; }
public DateTime? CompletedOn { get; set; }
public bool IsOpen { get; set; }
// To Reference Users table using FK
public Users UsersReference { get; set; }
}
and Leads:
[Table("Leads")]
public class Leads
{
[Key]
public int LeadID { get; set; }
[ForeignKey("CampaignsReference")]
public int CampaignID { get; set; }
[MaxLength(30)]
public string ConsumerName { get; set; }
[MaxLength(30)]
public string EmailAddress { get; set; }
[MaxLength(10)]
public string PhoneNo { get; set; }
public string PreferredMoC { get; set; }
public DateTime DateApproached { get; set; }
[ForeignKey("ProductsReference")]
public int ProductID { get; set; }
public string Status { get; set; }
// To access Campaign and Product table through FK
public Campaigns CampaignsReference { get; set; }
public Products ProductsReference { get; set; }
}
My query returns a result likes this in SQL:
The Leads column is just the COUNT() of the columns matching with my CampaignID. I've used context.Campaigns.FromSqlRaw() but since my Campaigns doesn't have another column specifically for the count.
Is there a way to display my new field Count() using EF Core and Linq?
Use LINQ Query for such task.
var query =
from l in ctx.Leads
let c = l.CampaignsReference
group c by new { c.AssignedTo, c.CampaignID, c.Name, c.Venue, c.StartedOn, c.CompletedOn, c.IsOpen } into g
select new
{
g.Key.AssignedTo,
g.Key.CampaignID,
g.Key.Name,
g.Key.Venue,
g.Key.StartedOn,
g.Key.CompletedOn,
g.Key.IsOpen,
Leads = g.Sum(x => x.Name != null ? 1 : 0)
};
var query = query.OrderBy(x => x.AssignedTo);

How to convert my SQL query to a LINQ statement

I am not familiar with linq complex queries, how can I write the following?
Three tables: Library,Batch,Plan
Library: BatchId ProcessingQuantity
Batch: BatchId
Plan: PlanNo Name Quantity ExecState Date
SELECT b.ProductionPlanBatchId,
a.ProductionPlanNo,
a.ProductConfigName,
sum(c.ProcessingQuantity) AS 'ProcessingQuantity',
sum(a.Quantity) AS 'Quantity',
a.ExecState,
round(CONVERT(float,sum(c.ProcessingQuantity))/CONVERT(float,sum(a.Quantity)), 2) AS 'Percent',
a.ProcessingCompletionDate
FROM ProductionPlan a,
ProductionPlan_Batch b,
ProductionLibrary c
WHERE a.ProductionPlanId = b.ProductionPlanId
AND b.ProductionPlanBatchId = c.ProductionPlanBatchId
AND a.ExecState <> 'Deleted'
GROUP BY b.ProductionPlanBatchId,
a.ProductionPlanNo,
a.ProductConfigName,
a.ProcessingCompletionDate,
a.ExecState
HAVING round(Convert(float,sum(c.ProcessingQuantity))/Convert(float,sum(a.Quantity)), 2) < 1
ORDER BY b.ProductionPlanBatchId DESC
According to your description, I suggest you could try to use below linq.
Class:
[Table("ProductionLibrary")]
public partial class ProductionLibrary
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ProductionPlanBatchId { get; set; }
public int? ProcessingQuantity { get; set; }
}
[Table("ProductionPlan")]
public partial class ProductionPlan
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ProductionPlanNo { get; set; }
[StringLength(10)]
public string ProductConfigName { get; set; }
public int? Quantity { get; set; }
[StringLength(10)]
public string ExecState { get; set; }
[StringLength(10)]
public string ProcessingCompletionDate { get; set; }
public int? ProductionPlanId { get; set; }
}
public partial class ProductionPlan_Batch
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ProductionPlanBatchId { get; set; }
public int? ProductionPlanId { get; set; }
}
Linq:
var re = from a in dbcontext.ProductionPlans
from b in dbcontext.ProductionPlan_Batch
from c in dbcontext.ProductionLibraries
where a.ProductionPlanId == b.ProductionPlanId
&& b.ProductionPlanBatchId == c.ProductionPlanBatchId
&& a.ExecState != "Deleted"
select new
{
a.ExecState,
a.ProcessingCompletionDate,
a.ProductConfigName,
a.ProductionPlanId,
a.ProductionPlanNo,
a.Quantity,
b.ProductionPlanBatchId,
bProductionPlanId = b.ProductionPlanId,
c.ProcessingQuantity,
cProductionPlanId = c.ProductionPlanBatchId
}
into p
group p by new {
p.ProductionPlanId,
p.ProductionPlanNo ,
p.ProductConfigName,
p.ProcessingCompletionDate,
p.ExecState,
p.ProductionPlanBatchId
} into grpre
where Math.Round(((decimal)grpre.Sum(x => x.ProcessingQuantity))/((decimal)grpre.Sum(x => x.Quantity)), 2) <1
orderby grpre.Key.ProductionPlanBatchId descending
select new {
grpre.Key.ProductionPlanBatchId,
grpre.Key.ProductionPlanNo,
grpre.Key.ProductConfigName,
ProcessingQuantity = grpre.Sum(x =>x.ProcessingQuantity) ,
Quantity = grpre.Sum(x => x.Quantity),
grpre.Key.ExecState,
Percent = Math.Round(((decimal)grpre.Sum(x => x.ProcessingQuantity)) / ((decimal)grpre.Sum(x => x.Quantity)), 2),
grpre.Key.ProcessingCompletionDate
};

Linq not sure of correct syntax

My query below is not returning any results. I think its to do with the way I am trying to join the traineeEvent table can a linq query have two from commands.
I realised it is to do with the tranineeEvent Date but this of course word overwrite the records and hence return nothing
New edit 11:30 I relised now I need the cpdpoints and trainnieevent to go into the one row ie not to have two diff columns in the select new how is this possible
var q = from cpd in pamsEntities.EmployeeCPDs
from traineeEvent in pamsEntities.TrainingEventTrainees
join Employee e in pamsEntities.Employees on cpd.EmployeeID equals e.emp_no
join TrainingEventPart tEventPart in pamsEntities.TrainingEventParts on traineeEvent.TrainingEventPartId equals tEventPart.RecordId
where (cpd.EmployeeID == id) && (startDate >= cpd.StartDate && endDate <= cpd.EndDate) &&
(traineeEvent.EmployeeId == id)
&& traineeEvent.StartDate >= startDate
&& traineeEvent.EndDate <= endDate
&& (traineeEvent.TraineeStatus == 1 || traineeEvent.TraineeStatus == 2)
&& (tEventPart.CPDHours > 0 || tEventPart.CPDPoints > 0)
&& (cpd.CPDHours > 0 || cpd.CPDPoints > 0)
orderby cpd.StartDate
select new
{
surname = e.surname,
forname1 = e.forename1,
forname2 = e.forename2,
EmployeeID = cpd.EmployeeID,
StartDate = cpd.StartDate,
EndDate = cpd.EndDate,
CPDHours = cpd.CPDHours,
CPDPoints = cpd.CPDPoints,
Description = cpd.Description,
TrainingStartDate = tEventPart.StartDate,
TrainingEndDate = tEventPart.EndDate,
TrainingCPDHours = tEventPart.CPDHours,
TrainingCPDPoints = tEventPart.CPDPoints,
TrainingEventDescription = tEventPart.Description
};
Asssoicated class is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DataObjectLibrary.Reports
{
public class EmployeeCPDReportRecord
{
public EmployeeCPDReportRecord(
string employeeName,
DateTime startDate,
DateTime endDate,
decimal cpdHours,
decimal cpdPoints,
string eventDesc,
DateTime trainingstartDate,
DateTime trainingEndDate,
decimal trainingcpdHours,
decimal trainingCpdPoints,
string trainingEventDesc,
string cpdtype
)
{
EmployeeName = employeeName;
StartDate = startDate;
EndDate = endDate;
CPDHours = cpdHours;
CPDPoints = cpdPoints;
EventDescription = eventDesc;
CpdType = cpdtype;
TrainingStartDate = trainingstartDate;
TrainingEndDate = trainingEndDate;
TrainingCPDHours = trainingCpdPoints;
TrainingCPDPoints = trainingCpdPoints;
TrainingEventDescription = trainingEventDesc;
}
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public decimal CPDHours { get; set; }
public decimal CPDPoints { get; set; }
public string EventDescription { get; set; }
public string EmployeeName { get; set; }
public DateTime TrainingStartDate { get; set; }
public DateTime TrainingEndDate { get; set; }
public decimal TrainingCPDHours { get; set; }
public decimal TrainingCPDPoints { get; set; }
public string TrainingEventDescription { get; set; }
public string TrainingEmployeeName { get; set; }
public string CpdType { get; set; }
}
}