Use LINQ to get comma separated list from different table - c#

I'm using ASP.NET and have a GridView that I want to populate with data from 2 tables. Right now, I have the following query:
var carListData = from x in db.cars
orderby x.carName
select new { x.carId, x.carName, locationIDs = string.Empty };
carList.DataBind();
Now, I want to set locationIDs to be a comma-separated string. I need to look up all the locations that sell each car. I have a table called db.locationCars and it has a locationId column and a carId column. Multiple locations can have the same carId. How do I update my carDataList query to get all the locations that sell each car so those locationIDs can be displayed in the carList gridview?
Thanks for any suggestions!

Using a GroupJoin should work. If the first gives you translation to SQL issues, try the second.
var carListData = db.cars
.GroupJoin(db.locationCars,
(o,i) => o.carId,
(o,i) => i.carId,
(o,i) => new { o.carId, o.carName, locationIds = string.Join(",", i.Select(l => l.locationId)))
.OrderBy(g => g.carName);
Version performing the string join in memory
var carListData = db.cars
.GroupJoin(db.locationCars,
(o,i) => o.carId,
(o,i) => i.carId,
(o,i) => new { o.carId, o.carName, locations = i.Select(l => l.locationId))
.OrderBy(g => g.carName)
.ToList()
.Select(g => new { g.carId, g.carName, locationIds = string.Join(",", g.locations));

foreach( var car in carList )
{
var locationIds = Locations.Where( loc => loc.CarId == car.Id ).Select( loc => loc.Id );
car.locationIDs = string.Join( ",", locationIds );
}

If your foreign keys are defined properly in the database, and your entity framework model his updated with those foreign keys. Then you would have a collection of locationCars in each Car object with the locationIds you need.
Create a new object called carListData and select into that object. This object can convert your collection of LocationCars into a csv of locationIds.

Related

Order by and group by and sum using SQL

What I am trying to do is get the top 10 most sold Vegetables by grouping them by an Id passed by parameter in a function and ordering them by the sum of their Quantity. I don't know how to use SUM or (total) quite yet but I thought I'd post it here seeking help. If you need me offering you anything else I will be ready.
This is my code:
TheVegLinQDataContext db = new TheVegLinQDataContext();
var query =db.OrderDetails.GroupBy(p => p.VegID)
.Select(g => g.OrderByDescending(p => p.Quantity)
.FirstOrDefault()).Take(10);
And this is an image of my database diagram
Group orders by Vegetable ID, then from each group select data you want and total quantity:
var query = db.OrderDetails
.GroupBy(od => od.VegID)
.Select(g => new {
VegID = g.Key,
Vegetable = g.First().Vegetable, // if you have navigation property
Total = g.Sum(od => od.Quantity)
})
.OrderByDescending(x => x.Total)
.Select(x => x.Vegetable) // remove if you want totals
.Take(10);
Since this is not clear that you are passing what type of id as function parameter, I'm assuming you are passing orderId as parameter.
First apply where conditions then group the result set after that order by Total sold Quantity then apply Take
LINQ query
var result = (from a in orderdetails
where a.OrderId == orderId //apply where condition as per your needs
group a by new { a.VegId } into group1
select new
{
group1.Key.VegId,
TotalQuantity = group1.Sum(x => x.Quantity),
group1.FirstOrDefault().Vegitable
}).OrderByDescending(a => a.TotalQuantity).Take(10);
Lamda (Method) Syntax
var result1 = orderdetails
//.Where(a => a.OrderId == 1) or just remove where if you don't need to filter
.GroupBy(x => x.VegId)
.Select(x => new
{
VegId = x.Key,
x.FirstOrDefault().Vegitable,
TotalQuantity = x.Sum(a => a.Quantity)
}).OrderByDescending(x => x.TotalQuantity).Take(10);

How to write linq query for this sql statement

How would you write a linq query with the following SQL statement. I've tried several methods referenced on stackoverflow but they either don't work with the EF version I'm using (EF core 3.5.1) or the DBMS (SQL Server).
select a.ProductID, a.DateTimeStamp, a.LastPrice
from Products a
where a.DateTimeStamp = (select max(DateTimeStamp) from Products where a.ProductID = ProductID)
For reference, a couple that I've tried (both get run-time errors).
var results = _context.Products
.GroupBy(s => s.ProductID)
.Select(s => s.OrderByDescending(x => x.DateTimeStamp).FirstOrDefault());
var results = _context.Products
.GroupBy(x => new { x.ProductID, x.DateTimeStamp })
.SelectMany(y => y.OrderByDescending(z => z.DateTimeStamp).Take(1))
Thanks!
I understand you would like to have a list of the latest prices of each products?
First of all I prefer to use group by option even over 1st query
select a.ProductID, a.DateTimeStamp, a.LastPrice
from Products a
where a.DateTimeStamp IN (select max(DateTimeStamp) from Products group by ProductID)
Later Linq:
var maxDateTimeStamps = _context.Products
.GroupBy(s => s.ProductID)
.Select(s => s.Max(x => x.DateTimeStamp)).ToArray();
var results = _context.Products.Where(s=>maxDateTimeStamps.Contains(s.DateTimeStamp));
-- all assuming that max datetime stamps are unique
I've managed to do it with the following which replicates the correlated sub query in the original post (other than using TOP and order by instead of the Max aggregate), though I feel like there must be a more elegant way to do this.
var results = from x
in _context.Products
where x.DateTimeStamp == (from y
in _context.Products
where y.ProductID == x.ProductID
orderby y.DateTimeStamp descending
select y.DateTimeStamp
).FirstOrDefault()
select x;
I prefer to break up these queries into IQueryable parts, do you can debug each "step".
Something like this:
IQueryable<ProductOrmEntity> pocoPerParentMaxUpdateDates =
entityDbContext.Products
//.Where(itm => itm.x == 1)/*if you need where */
.GroupBy(i => i.ProductID)
.Select(g => new ProductOrmEntity
{
ProductID = g.Key,
DateTimeStamp = g.Max(row => row.DateTimeStamp)
});
//// next line for debugging..do not leave in for production code
var temppocoPerParentMaxUpdateDates = pocoPerParentMaxUpdateDates.ToListAsync(CancellationToken.None);
IQueryable<ProductOrmEntity> filteredChildren =
from itm
in entityDbContext.Products
join pocoMaxUpdateDatePerParent in pocoPerParentMaxUpdateDates
on new { a = itm.DateTimeStamp, b = itm.ProductID }
equals
new { a = pocoMaxUpdateDatePerParent.DateTimeStamp, b = pocoMaxUpdateDatePerParent.ProductID }
// where
;
IEnumerable<ProductOrmEntity> hereIsWhatIWantItems = filteredChildren.ToListAsync(CancellationToken.None);
That last step, I am putting in an anonymous object. You can put the data in a "new ProductOrmEntity() { ProductID = pocoMaxUpdateDatePerParent.ProductID }...or you can get the FULL ProductOrmEntity object. Your original code, I don't know if getting all columns of the Product object is what you want, or only some of the columns of the object.

Entity Framework - Count distinct books pr. tag using navigation property

I have a table "Book" with a many-to-many relationship to "Tag" and need a distinct Book-count pr. Tag. In SQL, the query looks like this:
SELECT t.NAME,
count(DISTINCT b.BookId)
FROM _Tag t
JOIN Book.BookTag bt
ON t.Id = bt.TagId
JOIN Books b
ON b.BookId = bt.BookId
GROUP BY t.NAME
ORDER BY count(DISTINCT b.BookId) DESC;
I have fetched the tags and included the Books navigation-property and from this I should be able to get distinct BookId's pr. tagname. I want to get the result in a tuple.
So far I have tried the following:
var tagTuples = from tag in tags
join book in tags.Select(t => t.Books) on tag.Books equals book
group new {tag, book} by tag.Name
into g
select new Tuple<string, string, int>("tags", g.Key, g.Select(x => x.book).Count());
...and...
var tagTuples = tags.GroupBy(t => t.Name)
.Select(t2 => new Tuple<string, string, int>("tags", t2.Key, t2.Sum(t4 => t4.Books
.Select(b => b.BookId).Distinct().Count())))
.Where(t3 => !string.IsNullOrWhiteSpace(t3.Item2)).Take(15);
...and my latest version:
var tagTuples =
tags.Select(t => new {t.Name, BookId = t.Books.Select(b => b.BookId)})
.GroupBy(tb => tb.Name)
.Select(tb2 => new Tuple<string, string, int>("tags", tb2.Key, tb2.Sum(tb3 => tb3.BookId.Distinct().Count())));
Nevermind the small differences in the query's - I'm only interested in a solution to the problem described above.
Frustration! It takes me 2 minutes to write an SQL query that does this and I'm pretty sure there's a simple answer, but I lack EF routine.
Thankyou for your time. :)
using(var ctx = new EntitiesContext())
{
// GROUP By name
var bookCountByTag = ctx.Tags.GroupBy(t => t.Name)
.Select(t2 => new {
// SELECT the key (tag name)
t2.Key,
// "GroupBy" has many result, so use SelectMany
Count = t2.SelectMany(t3 => t3.book)
.Distinct()
.Count()})
.ToList();
}

Join an array of string with the result of an existing linq statement

As a follow up to my last question here:
Filtering a list of HtmlElements based on a list of partial ids
I need to take this statement:
doc.All.Cast<HtmlElement>()
.Where(x => x.Id != null)
.Where(x => ids
.Any(id => x.Id.Contains(id))).ToList();
and join it with an array of strings called fields. Assuming the array and list will have the same amount of elements each and line up correctly. I tried using Zip() but thought I might need to use an additional linq statement to make it work.
Assuming that fieldList[0] and IdList[0] corresponding to each other, you can do the following:
var IdList = doc.All.Cast<HtmlElement>()
.Where(x => x.Id != null)
.Where(x => ids
.Any(id => x.Id.Contains(id))).ToList();
var resultList = fieldList
.Select( (item, index) => new { Field = item, Id = IdList[index] })
.ToDictionary(x => x.Id, x => x.Field);
You have mentioned it already, you can use Enumerable.Join:
var joined = from id in fields
join ele in elements on id equals ele.Id
select new { Element = ele, ID = id };
var dict = joined.ToDictionary(x => x.ID, x => x.Element);
I've presumed that you want to join them via ID. I've also presumed that the string[] contains only unique ID's. Otherwise you need to use Distinct.

Use Linq to return first result for each category

I have a class (ApplicationHistory) with 3 properties:
ApplicantId, ProviderId, ApplicationDate
I return the data from the database into a list, however this contains duplicate ApplicantId/ProviderId keys.
I want to supress the list so that the list only contains the the earliest Application Date for each ApplicantId/ProviderId.
The example below is where I'm currently at, but I'm not sure how to ensure the earliest date is returned.
var supressed = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.First();
All advice appreciated.
Recall that each group formed by the GroupBy call is an IGrouping<ApplicationHistory>, which implements IEnumerable<ApplicationHistory>. Read more about IGrouping here. You can order those and pick the first one:
var oldestPerGroup = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).FirstOrDefault());
You are selecting first group. Instead select first item from each group:
var supressed = history
.GroupBy(x => new {
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).First());
Or query syntax (btw you don't need to specify names for anonymous object properties in this case):
var supressed = from h in history
group h by new {
h.ApplicantId,
h.ProviderId
} into g
select g.OrderBy(x => x.ApplicationDate).First();

Categories