Create Row_Number in linq query - c#

I have a query which I wish to replace with a Linq as query syntax. Below is my SQL query which I'm trying to create I've got it all working except the row_number:
SELECT
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ID,
r.StateProvinceRegion,
AVG(s.Longitude) AS Longitude,
AVG(s.Latitude) AS Latitude
FROM
RestaurantAddress r
INNER JOIN
Restaurant s ON s.RestaurantId = r.RestaurantId
GROUP BY
StateProvinceRegion
Query result:
ID StateRegion Longitude Latitude
-----------------------------------------------------
1 Auckland 174.759541622222 -36.8552809611111
2 Mumbai -73.9904097 40.7036292
3 New York -73.9904097 40.7036292
This is the current code I've implemented which has the same output except doesn't have the ROW_NUMBER (ID) which I'm trying to figure out how to output.
var region = from restaurantAddress in _context.RestaurantAddress
join restaurant in _context.Restaurant on restaurantAddress.RestaurantId equals restaurant.RestaurantId
group restaurant by new { restaurantAddress.StateProvinceRegion } into g
select new { g.Key.StateProvinceRegion, Latitude = g.Average(p => p.Latitude), Longitude = g.Average(p => p.Longitude) };
This is what I've tried:
int number = 0;
var region = from restaurantAddress in _context.RestaurantAddress
join restaurant in _context.Restaurant on restaurantAddress.RestaurantId equals restaurant.RestaurantId
group restaurant by new { restaurantAddress.StateProvinceRegion } into g
select new { id = number++ , g.Key.StateProvinceRegion, Latitude = g.Average(p => p.Latitude), Longitude = g.Average(p => p.Longitude) };
but number++ returns an error:
An expression tree may not contain an assignment operator

It's simple:
var region =
(
from restaurantAddress in _context.RestaurantAddress
join restaurant in _context.Restaurant
on restaurantAddress.RestaurantId equals restaurant.RestaurantId
group restaurant by new { restaurantAddress.StateProvinceRegion } into g
select new
{
g.Key.StateProvinceRegion,
Latitude = g.Average(p => p.Latitude),
Longitude = g.Average(p => p.Longitude),
}
)
.AsEnumerable()
.Select((x, id) => new
{
id,
x.StateProvinceRegion,
x.Latitude,
x.Longitude,
});

Related

How to translate SQL query with multiple grouping in EF equivalent

I have a database (PostgreSQL) where there is a main table student, additional information amount and 3 dictionaries. I make a query with grouping by three fields of dictionary IDs, output the number of objects and the amount from an additional table with a condition. And how to translate it to EF Core 6?
create table region (id serial primary key, name varchar);
create table district (id serial primary key, name varchar);
create table department (id serial primary key, name varchar);
create table student (
id serial primary key,
name varchar,
region_id bigint references region,
district_id bigint references district,
department_id bigint references department
);
create table amount (
id serial primary key,
student_id bigint references student on delete cascade,
value numeric,
year int
);
My SQL query is working well:
select
t.region_id,
region."name" region_name,
t.district_id,
district."name" district_name,
t.department_id,
department."name" department_name,
t.cnt,
t.value
from (
select
region_id,
district_id,
department_id,
count(distinct s.id) cnt,
sum(a.value) "value"
from student s
join amount a on s.id = a.student_id
where a.year = 2020
group by region_id, district_id, department_id
) t
join region on t.region_id = region.id
join district on t.district_id = district.id
join department on t.department_id = department.id
How do I get names from dictionaries when translating a query to EF?
[Table("student")]
public class Student
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string? Name { get; set; }
[Column("region_id")]
public int? RegionId { get; set; }
[Column("district_id")]
public int? DistrictId { get; set; }
[Column("department_id")]
public int? DepartmentId { get; set; }
[ForeignKey(nameof(RegionId))]
public virtual Region? Region { get; set; }
[ForeignKey(nameof(DistrictId))]
public virtual District? District { get; set; }
[ForeignKey(nameof(DepartmentId))]
public virtual Department? Department { get; set; }
public ICollection<Amount>? Amounts { get; set; }
}
EF query:
var result = await db.Student
.GroupBy(x => new { x.RegionId, x.DistrictId, x.DepartmentId })
.Select(x => new
{
x.Key.RegionId,
x.Key.DistrictId,
x.Key.DepartmentId,
Cnt = x.Count(),
Value = x.Sum(c => c.Amounts.Where(v => v.Year == 2020).Sum(v => v.Value))
})
.ToListAsync();
At the moment I have such a solution, but will such a request be optimal in the end? In addition, you need to add a null check here.
RegionName = x.First().Region.Name,
DistrictName = x.First().District.Name,
DepartmentName = x.First().Department.Name,
This can be done with the following EF Core query:
var query = from student in db.Student
join region in db.Region on student.RegionId equals region.id
join district in db.District on student.DistrictId equals district.id
join department in db.Department on student.DepartmentId equals department.id
join amount in db.Amount on student.Id equals amount.student_id
where amount.Year == 2020
group amount by new
{
student.RegionId,
RegionName = region.Name,
student.DistrictId,
DistrictName = district.Name,
student.DepartmentId,
DepartmentName = department.Name
} into g
select new
{
g.Key.RegionName,
g.Key.DistrictName,
g.Key.DepartmentName,
Cnt = g.Count(),
Value = g.Sum(a => a.Value)
};
var result = await query.ToListAsync();
It is translated into the following SQL:
SELECT r.name AS "RegionName", d.name AS "DistrictName", d0.name AS "DepartmentName",
count(*)::int AS "Cnt", COALESCE(sum(a.value), 0.0) AS "Value"
FROM student AS s
INNER JOIN region AS r ON s.region_id = r.id
INNER JOIN district AS d ON s.district_id = d.id
INNER JOIN department AS d0 ON s.department_id = d0.id
INNER JOIN amount AS a ON s.id = a.student_id
WHERE a.year = 2020
GROUP BY s.region_id, r.name, s.district_id, d.name, s.department_id, d0.name
If you need LEFT JOIN then it will be:
var query = from student in db.Student
join region in db.Region on student.RegionId equals region.id into rg
from r in rg.DefaultIfEmpty()
join district in db.District on student.DistrictId equals district.id into dg
from d in dg.DefaultIfEmpty()
join department in db.Department on student.DepartmentId equals department.id into dpg
from dp in dpg.DefaultIfEmpty()
join amount in db.Amount on student.Id equals amount.student_id
where amount.Year == 2020
group amount by new
{
student.RegionId,
RegionName = r.Name,
student.DistrictId,
DistrictName = d.Name,
student.DepartmentId,
DepartmentName = dp.Name
} into g
select new
{
g.Key.RegionName,
g.Key.DistrictName,
g.Key.DepartmentName,
Cnt = g.Count(),
Value = g.Sum(a => a.Value)
};
Try the following query:
var query =
from s in db.Student
from a in s.Amounts
where a.Year == 2020
group a by new
{
s.RegionId,
RegionName = s.Region.Name,
s.DistrictId,
DistrictName = s.District.Name,
s.DepartmentId,
DepartmentName = s.Department.Name
} into g
select new
{
x.Key.RegionId,
x.Key.DepartmentName,
x.Key.DistrictId,
x.Key.DistrictName,
x.Key.DepartmentId,
x.Key.DepartmentName,
Cnt = x.Select(v => v.StudentId).Distinct().Count(),
Value = x.Sum(v => v.Value)
};
var result = await query.ToListAsync();
Not sure that Cnt = x.Select(v => v.StudentId).Distinct().Count() will be translated, it depends on EF Core version.
UPDATE - added equivalent to the SQL query:
var groupingQuery =
from s in db.Student
from a in s.Amounts
where a.Year == 2020
group a by new
{
s.RegionId,
s.DistrictId,
s.DepartmentId,
} into g
select new
{
x.Key.RegionId,
x.Key.DistrictId,
x.Key.DepartmentId,
Cnt = x.Select(v => v.StudentId).Distinct().Count(),
Value = x.Sum(v => v.Value)
};
var query =
from g in groupingQuery
join region in db.Region on g.RegionId equals region.id
join district in db.District on g.DistrictId equals district.id
join department in db.Department on g.DepartmentId equals department.id
select new
{
g.RegionId,
RegionName = region.Name,
g.DistrictId,
DistrictName = district.Name,
g.DepartmentId,
DepartmentName = department.Name,
g.Cnt,
g.Value
};
var result = await query.ToListAsync();

Why can't I select data from my LINQ sub query join?

I am trying to get data from 2 tables using a left join to a nested query. This allows me to get data from Item table but not the cart(nested query) table:
var q = from s in db.Items
join sub in (from c in db.Carts
where c.CartID == 1
group c by c.ItemID into g
select new
{
ItemID = g.Key,
Qty = g.Select(s => s.Qty)
}) on s.ItemID equals sub.ItemID into a
select new ItemViewModel
{
CategoryID = s.CategoryID,
Description = s.Description,
Price = s.Price,
**This being the issue------>>>>>>>** //Qty = a.Select(j => j.Qty),
ItemID = s.ItemID,
ItemName = s.ItemName
};
viewModel = q.ToList();
The query i am trying to acheive is:
select Items.*, Cart.Qty
from Items
left join (select ItemID, Qty from carts where CartID = 1 ) Cart
on Items.ItemID = Cart.ItemID
You can use GroupJoin with SelectMany for LEFT JOIN SQL Query and get the desired output.
var result = db.Items.GroupJoin(db.Carts.Where(x => x.CartID == 1), item => item.ItemID, cart => cart.ItemID,
(item, cart) => new { item, cart })
.SelectMany(x => x.cart.DefaultIfEmpty(), (it, ca) =>
{
return new ItemViewModel
{
ItemName = it.item.ItemName,
Price = it.item.Price,
ItemID = it.item.ItemID,
// ... .... ....
// Fill the required columns from it.Item property..
Qty = ca != null ? ca.Qty : 0
};
}).ToList();
EDIT: The LINQ version with SelectMany.
var result = from s in db.Items
join sub in (from c in db.Carts
where c.CartID == 1
select c)
on s.ItemID equals sub.ItemID into joined
from row in joined.DefaultIfEmpty()
select new ItemViewModel
{
CategoryID = s.CategoryID,
Description = s.Description,
Price = s.Price,
Qty = row != null ? row.Qty : 0,
ItemID = s.ItemID,
ItemName = s.ItemName
};
The C# Fiddle with sample data.
If I'm understanding correctly, and assuming that ItemViewModel.Qty property is just an int, the simplest form of the query you want is:
var q = from item in items
join cart in
(from cart in carts where cart.CartID == 1 select cart)
on item.ItemID equals cart.ItemID into itemCarts
select new ItemViewModel
{
ItemID = item.ItemID,
Qty = itemCarts.Sum(cart => cart.Qty)
};
If you want to only slightly modify/fix your query:
var q = from s in db.Items
join sub in (from c in db.Carts
where c.CartID == 1
group c by c.ItemID into g
select new
{
ItemID = g.Key,
Qty = g.Sum(s => s.Qty)
// or Qty = g.Select(s => s.Qty)
// and below: Qty = a.SelectMany(x => x.Qty).Sum()
})
on s.ItemID equals sub.ItemID into a
select new ItemViewModel
{
CategoryID = s.CategoryID,
Description = s.Description,
Price = s.Price,
Qty = a.Sum(x => x.Qty),
ItemID = s.ItemID,
ItemName = s.ItemName
};

Convert SQL(Row_Number) Query into Linq in MVC?

How to convert below SQL Query to Linq expression in C#(MVC)?
with cte
as
(
select *,rank() over(partition by LineClass order by LineClassId) as rownum
from MT_LineClass where ProjectId =1
)
select * from cte where rownum=1
var res = from tbl in MT_LineClass
group tbl.lineclassid by tbl.lineclass into g
select new { LineClass = g.Key, LineClassIds = g.ToList() };
You can use other than ToList operations to get the id (eq First, Max etc)
var lst = (from t in context.MT_LineClass
where t.ProjectId == projId
group t by t.LineClass into g
select new Mt_Lineclass
{
lineId = g.Select(t => t.LineClassId).FirstOrDefault(),
Lineclass = g.Select(t => t.LineClass).FirstOrDefault()
}).ToList();

how to convert sql statement to LINQ in c#

how to covert to LINQ :
from p in Product
join c in Catalog on c.Id equals p.CatalogId
join m in Manufacturer on m.Id equals p.ManufacturerId
where p.Active == 1
select new { Name = p.Name, CatalogId = p.CatalogId,
ManufacturerId = p.ManufacturerId, CatalogName = c.Name,
ManufacturerName = m.Name };
help!
Don't try to translate the query literally. In LINQ, you don't need to join entities as long as they already have a relationship because LINQ will automatically use the relationship to join the entities:
So assuming your Product has a Catalog property which in turn has Manufacturer property, you can write your LINQ without joins like this:
from p in Product
where p.Active == 1
select new {
Name = p.Name,
CatalogId = p.CatalogId,
ManufacturerId = p.ManufacturerId,
CatalogName = p.Catalog.Name,
ManufacturerName = p.Manufacturer.Name };
The solution is to build a temp result after the first join and use it as the first sequence in the second join
var result = product.Where(p => p.Active == 1), // 1st sequence
.join(catalog, // 2nd sequence
p => p.CatalogId, // selector from 1st sequence
c => c.Id, // selector from 2nd sequence
(p, c) => // take the elements where the selector match
new {ManufacturerId = p.ManufacturerId,
Name = p.Name,
CatalogId = p.CatalogId,
CatalogName = c.Name}) // result is 1st sequence next join
.Join(Manufacturer, // 2nd sequence 2nd join
r => r.ManufacturerId, // selector result 1st join
m => m.Id, // selector 2nd sequence 2nd join
(r, m) => // two elements where the selectors match
new {Name = r.Name, // build the result object
CatalogId = r.CatalogId,
ManufacturerId = r.ManufacturerId,
CatalogName = r.CatalogName,
ManumfacturerName = r.Name});
Piece of cake ^^
Just call ToString() method of IQueryable will return SQL Representation.
var query = from p in Product
join c in Catalog on c.Id equals p.CatalogId
join m in Manufacturer on m.Id equals p.ManufacturerId
where p.Active == 1;
string sqlQuery = query.ToString(); //SQL Query Saved Here

LINQ Query for GroupBy and Max in a Single Query

I have the following LINQ query but i want to modify it that I want to group by staffId and pick only those records whose ObservationDate is Max for each staffId.
from ob in db.TDTObservations.OfType<TDTSpeedObservation>()
select new
{
Id = ob.ID,
AcademicYearId = ob.Teachers.FirstOrDefault().Classes.FirstOrDefault().AcademicYearID,
observationDate = ob.ObservationDate,
schoolId = ob.Teachers.FirstOrDefault().Classes.FirstOrDefault().SchoolID,
staffId=ob.Teachers.FirstOrDefault().ID
};
var observations =
from ob in db.TDTObservations.OfType<TDTSpeedObservation>()
select new {
Id = ob.ID,
AcademicYearId = ob.Teachers.FirstOrDefault().Classes.FirstOrDefault().AcademicYearID,
observationDate = ob.ObservationDate,
schoolId = ob.Teachers.FirstOrDefault().Classes.FirstOrDefault().SchoolID,
staffId=ob.Teachers.FirstOrDefault().ID
};
var result = from o in observations
group o by o.staffId into g
select g.OrderByDescending(x => x.observationDate).First();
what about this: hereby you first group your entries (Teachers) by their ID together and then from each group (grp) you pick that one with the latest ObservationDate
var observations = from d in db.TDTObservations.OfType<TDTSpeedObservation>()
group d by d.Teachers.FirstOrDefault().ID into grp
select grp.OrderByDescending(g => g.ObservationDate).FirstOrDefault();

Categories

Resources