I have three tables like this:
public partial class PriceSite
{
public DateTime ValidFromGmtDtm { get; set; }
public long PriceSiteId { get; set; }
public virtual ICollection<PriceSiteProduct> PriceSiteProduct { get; set; }
}
public partial class PriceSiteProduct
{
public long PriceSiteId { get; set; }
public long ProductId { get; set; }
public virtual Product Product { get; set; }
}
public partial class Product
{
public string ProductCd { get; set; }
public string ProductNm { get; set; }
public long ProductId { get; set; }
public virtual ICollection<PriceSiteProduct> PriceSiteProduct { get; set; }
}
We need to group the data by Productname and choose the product with highest ValidFromGmtDtm (Pricesite).
I tried this query
using (var dbContext = _dbContextProvider.DbContext)
{
var data = from t1 in dbContext.PriceSite
join t2 in dbContext.PriceSiteProduct
on t1.PriceSiteId equals t2.PriceSiteId
join t3 in dbContext.Product
on t2.ProductId equals t3.ProductId
where t1.ValidFromGmtDtm > shellSiteNotification.ValidFromDtm
select new
{
PriceSiteId = t1.PriceSiteId,
ValidFrom = t1.ValidFromGmtDtm,
ProductId = t3.ProductId,
ProductNm = t3.ProductNm,
ProductValues = t3.PumpPriceProduct
};
var res1 = data.ToList();
var data2 = from element in res1
group element by element.ProductNm into groups
select groups.OrderByDescending(p => p.ValidFrom).FirstOrDefault();
}
I am getting timeout error by running this query.
I also tried this query:
var grouped = from priceSite in _dbContextProvider.DbContext.PriceSite
where priceSite.ValidFromGmtDtm < shellSiteNotification.ValidFromDtm
from priceSiteProduct in priceSite.PriceSiteProduct
group priceSiteProduct by priceSiteProduct.Product.ProductNm into produtGroup
select new
{
ProductNamKey = produtGroup.Key,
produtGroup = produtGroup.OrderByDescending(x => x.PriceSite.ValidFromGmtDtm)
};
This code throws a runtime error as we cannot access the navigation property in select in EF Core.
I also tried another query - but this also results in a timeout
var result = from s in _dbContextProvider.FreshReadOnlyPricingDbContext.PriceSite
from r in s.PriceSiteProduct
let p = r.Product
select new
{
PriceSiteId = s.PriceSiteId,
ValidFrom = s.ValidFromGmtDtm,
ProductId = p.ProductId,
ProductNm = p.ProductNm,
ProductValues = p.PumpPriceProduct
};
var list = result.ToList();
It is known EF and SQL limitation, you cannot get items after grouping. Only Key and aggregation result is supported. So basically EF proposes to put ToList() before grouping and it means that you will load several million records to the client.
Without third party extensions which can do that effectively, you can use the following workaround:
using (var dbContext = _dbContextProvider.DbContext)
{
var dataQuery =
from t1 in dbContext.PriceSite
join t2 in dbContext.PriceSiteProduct
on t1.PriceSiteId equals t2.PriceSiteId
join t3 in dbContext.Product
on t2.ProductId equals t3.ProductId
where t1.ValidFromGmtDtm > shellSiteNotification.ValidFromDtm
select new
{
PriceSiteId = t1.PriceSiteId,
ValidFrom = t1.ValidFromGmtDtm,
ProductId = t3.ProductId,
ProductNm = t3.ProductNm,
ProductValues = t3.PumpPriceProduct
};
var query =
from key in dataQuery.Select(d => new { d.ProductNm }).Distinct()
from d in dataQuery.Where(d => d.ProductNm == key.ProductNm)
.OrderByDescending(d => d.ValidFrom)
.Take(1)
select d;
var result = query.ToList();
}
Related
public class CategoryDomainModel
{
public string? _id { get; set; }
public string? RestaurantID { get; set; }
public string? CategoryName { get; set; }
public Enums.Status Status { get; set; }
}
public class MenuItemDomainModel
{
public string? _id { get; set; }
public string? CategoryID { get; set; }
public string? ImageLink { get; set; }
public string? ItemName { get; set; }
public string? ItemDescription { get; set; }
}
Imagine you have these two tables in mongodb and a category has many menus.
When you want to join the two tables and get all categories + menus by Restaurant ID with a result like this
public class CategoryAndMenusDomainModel
{
public string? _id { get; set; }
public string? RestaurantID { get; set; }
public string? CategoryName { get; set; }
public Enums.Status Status { get; set; }
public List<MenuItemDomainModel>? Menus { get; set; }
}
How do you go about it?
Ive tried:
var categoryCollection = database.GetCollection<CategoryDomainModel>("Categories");
var menuCollection = database.GetCollection<MenuItemDomainModel>("Menus");
var categoriesAndMenus = (from b in categoryCollection.AsQueryable()
join c in menuCollection.AsQueryable()
on b._id equals c.CategoryID
where b.RestaurantID == restautantID
select new CategoryAndMenusDomainModel
{
_id = b._id,
CategoryName = b.CategoryName,
RestaurantID = b.RestaurantID,
Menus = new List<MenuItemDomainModel>
{
new MenuItemDomainModel
{
_id = c._id,
ItemName = c.ItemName,
ItemDescription = c.ItemDescription
}
}
}).ToList();
But its throwing an exception:
"$project or $group does not support new List`1()
Consider this workaround:
var categoriesAndMenus = (from b in categoryCollection.AsQueryable()
join c in menuCollection
on b._id equals c.CategoryID
where b.RestaurantID == restautantID
select new
{
_id = b._id,
CategoryName = b.CategoryName,
RestaurantID = b.RestaurantID,
C_id = c._id,
C_CategoryName = c.ItemName,
C_ItemDescription = c.ItemDescription
}
)
.ToList()
// below step is client side tranformation, but all required data is already received from the server, so it's just minor projection
.Select(i => new CategoryAndMenusDomainModel
{
_id = i._id,
CategoryName = i.CategoryName,
RestaurantID = i.RestaurantID,
Menus = new List<MenuItemDomainModel>
{
new MenuItemDomainModel
{
_id = i._id,
ItemName = i.CategoryName,
ItemDescription = i.C_ItemDescription
}
}
});
The generated MQL query for this case will be:
{
"aggregate": "Categories",
"pipeline": [
{
"$lookup":
{
"from": "Menus",
"localField": "_id",
"foreignField": "CategoryID",
"as": "c"
}
},
{ "$unwind": "$c" },
{ "$match": { "RestaurantID": "1" } },
{
"$project":
{
"_id": "$_id",
"CategoryName": "$CategoryName",
"RestaurantID": "$RestaurantID",
"C_id": "$c._id",
"C_CategoryName": "$c.ItemName",
"C_ItemDescription": "$c.ItemDescription"
}
}
]
}
I'm 99% confident that client side projection can be moved to the server side, for example via a raw MQL query with $map, see MongoDB project into an array, but it will require more investigation
Linq translates the C# code into SQL. SQL doesn't know about C# lists, so it doesn't know how to format that. You need to do this in two steps: 1) get the data from the database in flat rows, then 2) in C#, put in into the structure you want.
Here I've used the standard Northwind database, but you should be able to adapt it to your data.
var rows = (from c in Categories
join p in Products on c.CategoryID equals p.CategoryID
//where c.CategoryID == 1
select new
{
_id = c.CategoryID,
CategoryName = c.CategoryName,
RestaurantID = c.CategoryID,
menu_id = p.ProductID,
ItemName = p.ProductName,
ItemDescription = p.ProductName
}).ToList();
var categoriesAndMenus =
(from r in rows
group r by r._id into grp
let cat = grp.First()
select new
{
_id = cat._id,
cat.CategoryName,
cat.RestaurantID,
Menu = grp.Select(g =>new
{
_id = g.menu_id,
g.ItemName,
g.ItemDescription
} )
}).ToList();
Hi I have following two model classes
public class c1
{
public int id { get; set; }
public int ptId { get; set; }
public int bId { get; set; }
public int rId { get; set; }
public IEnumerable<styles> newStruct { get; set; }
}
public class styles
{
public int id { get; set; }
public int bId { get; set; }
public string desc { get; set; }
}
I am trying to write a linq query
var records = (from y in db.main
join c in db.secondary on y.bId equals c.bId
where c.id == id
select new c1
{
pId= c.pId,
id = c.id,
newStruct = new List<styles>
{
new styles
{
id=y.room_id,
desc=y.desc,
}
}
});
return records.ToList();
Problem I am having is that in newStruct is suppose to be List of all the styles but it just returns one style at a time rather than one list.
Please let me know how can it return record where inside it contains list of styles
Thanks
If you want to get a sublist by main list, you should use group by,
You can try this, but I'm not sure it worked. Becasue I couldn't compile it.
var records = (from y in db.main
join c in db.secondary on y.bId equals c.bId
where c.id == id
group c by new
{
c.pId,
c.id
} into gcs
select new c1
{
pId = c.Key.pId,
id = c.Key.id,
newStruct = from g in gcs select new styles { id=g.room_id, desc=g.desc}
});
Is this LINQ to Entities? If so, and the mappings are correct in the edmx, you can try:
var records =
from c in db.secondary
where c.id == id
select new c1
{
pId = c.pId,
id = c.id,
newStruct = c.main.Select(m => new styles { id = m.room_id, desc = m.desc })
};
return records.ToList();
My models look like:
public class ReturnItem
{
public int returnItemId { get ; set; }
public int returnRequestId { get; set; }
public int quantity { get; set; }
public string item { get; set; }
}
public class ReturnRequest
{
public int returnRequestId { get; set; }
public string orderNumber { get; set; }
public IEnumerable<ReturnItem> returnItems { get; set; }
}
And I have the following query:
SELECT item, sum(quantity)
FROM ReturnItem
JOIN ReturnRequest
ON ReturnRequest.returnRequestId = ReturnItem.returnRequestId
WHERE ReturnRequest.orderNumber = '1XX'
GROUP BY item
How do I convert the query to Entity Framework and return a List<ReturnItem>? Can I use .Include instead of .Join?
from ri in db.ReturnItems
join rr in db.ReturnRequests
on ri.returnRequestId equals rr.returnRequestId
where rr.orderNumber == "1XX"
group ri by ri.item into g
select new {
Item = g.Key,
Quantity = g.Sum(i => i.quantity)
}
You can't use Include instead of Join because Include translated into Left Outer Join but you need Inner Join here.
But you can use navigation property to perform join implicitly:
db.ReturnRequests
.Where(rr => rr.orderNumber == "1XX")
.SelectMany(rr => rr.returnItems)
.GroupBy(ri => ri.item)
.Select(g => new {
Item = g.Key,
Quantity = g.Sum(ri => ri.quantity)
});
I have this linq query in GridView
var table = from ll in dbo.Workers
join p in dbo.WorkDays on ll.Id equals p.Id
orderby p.Enter
select new
{
ll.Id,
ll.Name,
ll.Salary,
p.Enter,
p.ExitT,
p.Place,
WorkTime = Math.Round(getWorkTime(p.Enter, p.ExitT), 2),
Earned = Math.Round(getEarned(p.Enter, p.ExitT, ll.Salary), 2),
};
How can I add column of sum to the table?
Create class which will hold data and provide calculations based on that data:
public class WorkerInfo
{
// I don't know exact type of fields, but its enough for you to get the idea
public int Id { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
public DateTime Enter { get; set; }
public DateTime ExitT { get; set; }
public string Place { get; set; }
public int WorkTime
{
get { return (ExitT - Enter).TotalDays; }
}
public decimal Earned
{
get { return WorkTime * Salary; }
}
}
Fill that these entities with data:
var workers = from ll in dbo.Workers
join p in dbo.WorkDays on ll.Id equals p.Id
orderby p.Enter
select new WorkerInfo
{
Id = ll.Id,
Name = ll.Name,
Salary = ll.Salary,
Enter = p.Enter,
ExitT = p.ExitT,
Place = p.Place
};
UPDATE Sample for returning aggregated data for all days:
var query = from w in dbo.Workers
join d in dbo.WorkDays on w.Id equals d.Id into days
let WorkTime = days.Sum(d => d.ExitT - d.Enter)
select new
{
w.Id,
w.Name,
w.Salary,
WorkTime,
Earned = WorkTime * w.Salary
};
I have a linq query, which is further having a subquery, I want to store the result of that query into a user defined type, my query is
var val = (from emp in Employees
join dept in Departments
on emp.EmployeeID equals dept.EmployeeID
select new Hello
{
EmployeeID = emp.EmployeeID
Spaces = (from order in Orders
join space in SpaceTypes
on order.OrderSpaceTypeID equals space.OrderSpaceTypeID
where order.EmployeeID == emp.EmployeeID group new { order, space } by new { order.OrderSpaceTypeID, space.SpaceTypeCode } into g
select new
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})
}).ToList();
Definition for my Hello class is
public class Hello
{
public IEnumerable<World> Spaces { get; set; }
public int PassengerTripID { get; set; }
}
Definition for my World class is
public class World
{
public int ID { get; set; }
public string Code { get; set; }
public int Count { get; set; }
}
You are creating anonymous object but you need to specify type name World
select new World
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})