LINQ : How to set value of Take() from a field in current query - linq

I have a table of Widgets with columns "Name" and "Count"
The "Count" field contains count of Posts each Widget would show:
Name | Count
---------------------
RecentNews | 6
SpecialNews | 5
NewsGallery | 10
The second table associated with Widgets Table and Posts Table :
PostID | WidgetID
------------------
100 | 6
101 | 5
102 | 10
For more performance, I just fetch last needed posts for each Widget by this query:
var postInWidgets = db.PostWidgets
.GroupBy(pw => pw.WidgetID)
.SelectMany(g => g.OrderByDescending(p => p.Post.DateCreated).Take(500))
.ToList();
and then get posts in each widget :
var postsInGalery = postInWidgets
.Where(wid => wid.WidgetID == 1).Take(6)
.ToList();
var postsInSpecialNews=postInWidgets
.Where(wid => wid.WidgetID == 2).Take(5)
.ToList();
var postsInRecentNews=postInWidgets
.Where(wid => wid.WidgetID == 5).Take(10)
.ToList();
and in each Widget Partial View :
foreach(var p in Model.PostsInRecentNews)
{
<li>#Html.ActionLink(p.Post.Title,"Index","Home")</li>
}
My Question : How to Set the int value of Take(count) Dynamically for each widget instead of Take(6) , Take(5) , Take(10) ...
I think I need to use TakeWhile() instead the Take()
Thanks for any assistance...

It sounds like you just need to fetch the counts first (you may want to cache them):
var counts = db.Counts.ToDictionary<string, int>(c => c.Name, c => c.Count);
Then:
var postsInGallery = postInWidgets
.Where(wid => wid.WidgetID == 1)
.Take(counts["NewsGallery"])
.ToList();
var postsInSpecialNews = postInWidgets
.Where(wid => wid.WidgetID == 2)
.Take(counts["SpecialNews"])
.ToList();
var postsInRecentNews = postInWidgets
.Where(wid => wid.WidgetID == 5)
.Take(counts["RecentNews"])
.ToList();
You could potentially use an enum instead of a string, to avoid the use of easily-typoed string constants. If the enum had a value of the related widget ID, you could wrap that up in a single method:
List<Widget> GetWidgets(WidgetType widgetType)
{
return postInWidgets.Where(wid => wid.WidgetID == (int) widgetType)
.Take(counts[widgetType])
.ToList();
}
Then call it as:
var postsInGallery = GetWidgets(WidgetType.NewsGallery);
var postsInSpecialNews = GetWidgets(WidgetType.SpecialNews);
var postInRecentNews = GetWidgets(WidgetType.RecentNews);
(This assumes counts is a field somewhere, of course - adjust as per your requirements.)

Related

Linq GROUP & SUM the same columns from different tables

I'm trying to combine these 2 Linq queries into 1:
var query = from s in _context.Set<StockInventoryItem>()
where s.StockCatalogueItemId == id
group s by s.StockType into g
select new
{
inStock = g.Sum(x => x.QtyInStock),
};
var query2 = from p in _context.Set<PurchaseOrderItem>()
where p.StockCatalogueItemId == id
group p by p.StockType into g2
select new
{
onOrder = g2.Sum(x => x.QtyStillDue)
};
Note that the filtering, grouping and output is the same from both tables, and I want the results to look like this:
StockType inStock onOrder
+----------+--------+--------+
Type 1 4 3
+----------+--------+--------+
Type 2 0 1
i.e. Quantities grouped by StockType
This is EF code first and there is no direct relationship between these tables, which is why I'm trying this query in the service layer so I can access both entities.
You should be able to "shoehorn" both groups into the same sequence with anonymous types and Concat, and then count the results separately, like this:
var query = _context.Set<StockInventoryItem>()
.Where(ii => ii.StockCatalogueItemId == id)
.Select(ii => new {
II = ii, PO = (PurchaseOrderItem)null
}).Concat(_context.Set<PurchaseOrderItem>()
.Where(po => po.StockCatalogueItemId == id)
.Select(po => new {
II = (StockInventoryItem)null, PO = po
})).GroupBy(p => II != null ? ii.StockType : PO.StockType)
.Select(g => new {
InStock = g.Sum(p => p.II != null ? p.II.QtyInStock : 0)
, OnOrder = g.Sum(p => p.PO != null ? p.PO.QtyStillDue: 0)
});

How to find all rows of items that have a part in common using LINQ?

I need to return all records (items) that has a part (X) so I can use that in a group or .GroupBy afterwards
Using this summary data:
ItemName PartName
1 A
1 B
2 A
3 C
So Item1 has two parts (A,B), etc...
I need a LINQ query that will
- find all items that have part A (i.e items 1 and 2)
- return all rows for all these items
1 A
1 B
2 A
Notice that the end result returned the row (1 B) because Item1 has PartA and so I need to get back all rows for Item1.
I was looking at something like:
let items = from data in summary where data.PartName == A select new { data.ItemName } // to get all the items I need
But then, now that I have that list I need to use it to get all the rows for all items listed, and I can't seem to figure it out ...
Actual Source Code (for reference):
NOTE:
Recipe = ITEM
Ingredient = PART
(I was just trying to make it simpler)
ViewFullRecipeGrouping = (
from data in ViewRecipeSummary
group data by data.RecipeName into recipeGroup
let fullIngredientGroups = recipeGroup.GroupBy(x => x.IngredientName)
select new ViewFullRecipe()
{
RecipeName = recipeGroup.Key,
RecipeIngredients = (
from ingredientGroup in fullIngredientGroups
select new GroupIngredient()
{
IngredientName = ingredientGroup.Key
}
).ToList(),
ViewGroupRecipes = (
from data in ViewRecipeSummary
// this is where I am looking to add the new logic to define something I can then use within the next select statement that has the right data based on the information I got earlier in this query.
let a = ViewRecipeSummary.GroupBy(x => x.RecipeName)
.Where(g => g.Any(x => x.IngredientName == recipeGroup.Key))
.Select(g => new ViewRecipe()
{
RecipeName = g.Key,
IngredientName = g.Select(x => x.IngredientName)
})
select new GroupRecipe()
{
// use the new stuff here
}).ToList(),
}).ToList();
Any help would be much appreciated.
Thanks,
I believe this does what you want:
var data = /* enumerable containing rows in your table */;
var part = "X";
var items = new HashSet<int>(data
.Where(x => x.PartName == part)
.Select(x => x.ItemName));
var query = data.Where(x => items.Contains(x.ItemName));
If I understand your comment at the end, I believe this also does what you want:
var query = data
.GroupBy(x => x.ItemName)
.Where(g => g.Any(x => x.PartName == part))
.Select(g => new
{
ItemName = g.Key,
PartNames = g.Select(x => x.PartName)
});

Entity Framework/ Linq - groupby and having clause

Given the query below
public TrainingListViewModel(List<int> employeeIdList)
{
this.EmployeeOtherLeaveItemList =
CacheObjects.AllEmployeeOtherLeaves
.Where(x => x.OtherLeaveDate >= Utility.GetToday() &&
x.CancelDate.HasValue == false &&
x.OtherLeaveId == Constants.TrainingId)
.OrderBy(x => x.OtherLeaveDate)
.Select(x => new EmployeeOtherLeaveItem
{
EmployeeOtherLeave = x,
SelectedFlag = false
}).ToList();
}
I want to put in the employeeIdList into the query.
I want to retrieve all of the x.OtherLeaveDate values where the same x.OtherLeaveDate exists for each join where x.EmployeeId = (int employeeId in employeeIdList)
For example if there are EmployeeIds 1, 2, 3 in employeeIdList and in the CacheObjects.AllEmployeeOtherLeaves collection there is a date 1/1/2001 for all 3 employees, then retreive that date.
If I read you well it should be something like
var grp = this.EmployeeOtherLeaveItemList =
CacheObjects.AllEmployeeOtherLeaves
.Where(x => x.OtherLeaveDate >= Utility.GetToday()
&& x.CancelDate.HasValue == false
&& x.OtherLeaveId == Constants.TrainingId
&& employeeIdList.Contains(x.EmployeeId)) // courtesy #IronMan84
.GroupBy(x => x.OtherLeaveDate);
if (grp.Count() == 1)
{
var result = g.First().Select(x => new EmployeeOtherLeaveItem
{
EmployeeOtherLeave = x,
SelectedFlag = false
})
}
First the data is grouped by OtherLeaveDate. If the grouping results in exactly one group, the first (and only) IGrouping instance is taken (which is a list of Leave objects) and its content is projected to EmployeeOtherLeaveItems.
To the where statement add "&& employeeIdList.Contains(x.EmployeeId)"
I need to thank #IronMan84 and #GertArnold for helping me along, and I will have to admonish myself for not being clearer in the question. This is the answer I came up with. No doubt it can be improved but given no one has responded to say why I will now tick this answer.
var numberOfEmployees = employeeIdList.Count;
var grp = CacheObjects.AllEmployeeOtherLeaves.Where(
x =>
x.OtherLeaveDate >= Utility.GetToday()
&& x.CancelDate.HasValue == false
&& x.OtherLeaveId == Constants.TrainingId
&& employeeIdList.Contains(x.EmployeeId))
.GroupBy(x => x.OtherLeaveDate)
.Select(x => new { NumberOf = x.Count(), Item = x });
var list =
grp.Where(item => item.NumberOf == numberOfEmployees).Select(item => item.Item.Key).ToList();

Update existing list values with values from another query

I have a linq statement which calls a stored proc and returns a list of items and descriptions.
Like so;
var q = from i in doh.usp_Report_PLC()
where i.QTYGood == 0
orderby i.PartNumber
select new Parts() { PartNumber = i.PartNumber, Description = i.Descritpion.TrimEnd() };
I then have another SQL statement which returns the quantities on order and delivery date for each of those items. The Parts class has two other properties to store these. How do I update the existing Parts list with the other two values so that there is one Parts list with all four values?
UPDATE
The following code now brings out results.
var a = from a1 in db.usp_Optos_DaysOnHand_Report_PLC()
where a1.QTYGood == 0
orderby a1.PartNumber
select new Parts() { PartNumber = a1.PartNumber, Description = a1.Descritpion.TrimEnd() };
var b = from b1 in db.POP10110s
join b2 in db.IV00101s on b1.ITEMNMBR equals b2.ITEMNMBR
//from b3 in j1.DefaultIfEmpty()
where b1.POLNESTA == 2 && b1.QTYCANCE == 0
group b1 by new { itemNumber = b2.ITMGEDSC } into g
select new Parts() { PartNumber = g.Key.itemNumber.TrimEnd(), QtyOnOrder = g.Sum(x => Convert.ToInt32(x.QTYORDER)), DeliveryDue = g.Max(x => x.REQDATE).ToShortDateString() };
var joinedList = a.Join(b,
usp => usp.PartNumber,
oss => oss.PartNumber,
(usp, oss) =>
new Parts
{
PartNumber = usp.PartNumber,
Description = usp.Description,
QtyOnOrder = oss.QtyOnOrder,
DeliveryDue = oss.DeliveryDue
});
return joinedList.ToList();
Assuming your "other SQL statement" returns PartNumber, Quantity and DeliveryDate, you can join the lists into one:
var joinedList = q.Join(OtherSQLStatement(),
usp => usp.PartNumber,
oss => oss.PartNumber,
(usp, oss) =>
new Parts
{
PartNumber = usp.PartNumber,
Description = usp.Description,
Quantity = oss.Quantity,
DeliveryDate = oss.DeliveryDate
}).ToList();
You can actually combine the queries and do this in one join and projection:
var joinedList = doh.usp_Report_PLC().
Where(i => i.QTYGood == 0).
OrderBy(i => i.PartNumber).
Join(OtherSQLStatement(),
i => i.PartNumber,
o => o.PartNumber,
(i, o) =>
new Parts
{
PartNumber = i.PartNumber,
Description = i.Description,
Quantity = o.Quantity,
DeliveryDate = o.DeliveryDate
}).ToList();
And again: I assume you have PartNumber in both returned collections to identify which item belongs to which.
Edit
In this case the LINQ Query syntax would probably be more readable:
var joinedList = from aElem in a
join bElem in b
on aElem.PartNumber equals bElem.PartNumber into joinedAB
from abElem in joinedAB.DefaultIfEmpty()
select new Part
{
PartNumber = aElem.PartNumber,
Description = aElem.Description,
DeliveryDue = abElem == null ? null : abElem.DeliveryDue,
QtyOnOrder = abElem == null ? null : abElem.QtyOnOrder
};
Your DeliveryDue and QtyOnOrder are probably nullable. If not, replace the nulls by your default values. E.g. if you don't have the element in b and want QtyOnOrder to be 0 in the resulting list, change the line to
QtyOnOrder = abElem == null ? 0 : abElem.QtyOnOrder

How to combine Where clause and group by in LINQ

Can any one help me convert the below code to LINQ?
Select Catg,Count(*) From Mycatg where IsPublic=1 or FirstName='XXX' Group By Catg .
In C#, something like:
var query = from category in mycatg
where category.IsPublic == 1
|| category.FirstName == "XXX"
group 1 by category.Catg into grouped
select new { Catg = grouped.Key,
Count = grouped.Count() };
The projection of "1" makes it clear that all we need is the key of the grouping and the count - the individual entries in each grouping are irrelevant.
Using lambda syntax and dot notation:
var query = mycatg.Where(category => category.IsPublic == 1
|| category.FirstName == "XXX")
.GroupBy(category => category.Catg,
category => 1)
.Select(grouped => new { Catg = grouped.Key,
Count = grouped.Count() });

Resources