Linq query with count (extension method) - asp.net-mvc

I expect a very simple solution, but I can't for the life of me figure this out...
I am trying to create the LINQ equivalent of this:
SELECT Group, COUNT(*) as GroupCount
FROM table1
WHERE Valid > 0
GROUP BY Group, Project
I have this so far:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => new { r.GROUP, r.Project})
.Select(r => new{ r.GROUP, GroupCount = r.count()};
What is wrong with my query? Visual studio throws and error stating that:
System.Linq.IGrouping' does not contain a definition for 'GROUP' and no extension method 'GROUP' accepting a first argument of type 'System.Linq.IGrouping' could be found (are you missing a using directive or an assembly reference?)
EDIT: Just a note: The above error is given for r.Group in the Select clause.
It also throws an error stating that the count extension method doesn't exist, but I've seen other examples done this way. Maybe I was looking at an example from an old version of LINQ?
EDIT2: Some example data
GroupName ProjectID Step Other Info...
-----------------------------------------------
GroupA | 1 | 1 | ..............
GroupA | 1 | 2 |..............
GroupA | 3 | 1 | ..............
GroupB | 4 | 1 | ..............
GroupB | 5 | 1 | ..............
GroupC | 6 | 1 |..............
Desired result:
GroupName Group Count
---------------------------
GroupA | 2
GroupB | 2
GroupC | 1

Well, you have following errors in the .Select statement:
.count should be .Count (C# is case sensitive)
r.GROUP should be r.Key or r.Key.GROUP (.GroupBy() returns an IGrouping<TKey, TElement>)
You forgot the last parenthesis in the Select method. (But maybe that was just a typo in the example)
Result:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => new { r.GROUP, r.Project })
.Select(r => new { r.Key.GROUP, GroupCount = r.Count() });
UPDATE:
After the comments and question update; it looks like you are only grouping by group, so that would turn in to something like this to yield the output you requested:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => new { r.GROUP }) // First, group only on GROUP
.Select(r => new
{
r.Key.GROUP,
// Second, group on unique ProjectId's
GroupCount = r.GroupBy(g => g.Project).Count()
});
Or simplified:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => r.GROUP) // First, group only on GROUP
.Select(r => new
{
Group = r.Key,
// Second, group on unique ProjectId's
GroupCount = r.GroupBy(g => g.Project).Count()
});
As you asked in the comments, yes, you could say that the values are concatenated in the GroupBy clause. So new { r.GROUP, r.Project } would group all pairs with the same value.

LINQ queries are case-sensitive. You're trying to access r.GROUP, which probably doesn't exist. Use r.Group instead. Also change r.count() to r.Count(). Here's the corrected code:
var model = _db.table1
.Where(r => r.Valid > 0)
.GroupBy(r => new { r.Group, r.Project})
.Select(r => new{ r.Key, GroupCount = r.Count()};

When you're doing the Group By, you're creating a new anonymous type (not of the type you think it is). Thus, when you're doing the select statement, it has no idea what the heck GROUP is.
To mitigate this, follow the 2nd answer located here.

Related

How to execute an update on multiple rows using Linq Lambda Expression in ASP.Net MVC?

I'm having a hard time trying to execute an update on multiple rows in a table.
I've tried this code:
PayoutEntities payoutdb = new PayoutEntities();
public void SaveStatusBatch(string batchid)
{
payout_transaction data = payoutdb.payout_transaction.Where(x => x.batchid == batchid).FirstOrDefault();
data.status = "Success";
payoutdb.SaveChanges();
}
But it only updates only a single row where the batchid is a match to what is indicated in the parameter. I'm using Èntity Framework in this.
This is how my table looks like:
|batchid|name|status|
|1 |John| |
|1 |Nick| |
|1 |Bill| |
|2 |Ara | |
I wanted to update the status of John,Nick and Bill to Success.
Do you have any suggestions on how am i supposed to do this?
The concept is to change all the data and then call the SaveChanges of the DBContext object. e.g.:
public void SaveStatusBatch(string batchid)
{
payout_transaction data = payoutdb.payout_transaction
.Where(x => x.batchid == batchid)
.ToList();
data.ForEach(a=> a.status = "Success");
payoutdb.SaveChanges();
}

Lambda : GroupBy multiple columns where one of them is DbFunctions.TruncateTime()

NOTE : This is NOT a duplicate of this useful SO question, my problem is all about the TruncateTime inside the GroupBy clause. See explanations below :
I'd like to use DbFunctions.TruncateTime in a multiple GroupBy clause, but it doesn't seem to work in my ASP.NET MVC5 project.
Here is a first lambda I wrote, it gives me the total number of views per day for a set of data.
It gives me the expected result :
var qViews = dbContext.TABLE_C.Where(c => c.IdUser == 1234)
.Join(dbContext.TABLE_V.Where(v => v.Date > DbFunctions.AddMonths(DateTime.Now, -1)), c => c.Id, v => v.Id, (c, v) => new { c, v })
.GroupBy(x => DbFunctions.TruncateTime(x.v.MyDateTimeColumn))
.Select(g => new
{
Date = (DateTime)g.Key,
NbViews = g.Count(),
}).ToDictionary(p => p.Date, p => p.NbViews);
Result is something like that :
...
Date | Views
03/07/2018 | 15
03/08/2018 | 8
03/09/2018 | 23
Now, I'd like a more detailled result, with the number of views per day AND PER ITEM on the same set of data.
Here is what I'd like to write :
var qViews = dbContext.TABLE_C.Where(c => c.IdUser == 1234)
.Join(dbContext.TABLE_V.Where(v => v.Date > DbFunctions.AddMonths(DateTime.Now, -1)), c => c.Id, v => v.Id, (c, v) => new { c, v })
.GroupBy(x => new { DbFunctions.TruncateTime(x.v.MyDateTimeColumn), x.c.Id}) // Issue #1
.Select(g => new
{
Date = g.Key.Date, //Issue #2
NbViews = g.Count(),
}).ToDictionary(p => p.Date, p => p.NbViews);
And I expected something like that :
...
Date | Views | ID Item
03/07/2018 | 4 | 456789
03/07/2018 | 11 | 845674
03/08/2018 | 6 | 325987
03/08/2018 | 1 | 548965
03/08/2018 | 1 | 222695
03/09/2018 | 23 | 157896
So, this request have two issues (see comments above)
Issue #1 : It seems I can't GroupBy multiple columns, which one of them use DbFunctions. If I use .GroupBy(x => new { x.v.MyDateTimeColumn, x.c.Id }), code compiles, but doesn't give me the expected result, as I want to group by date, not date + time
Issue #2 : Date = g.Key.Date, seems wrong for the compiler. When I wrote g.Key, autocompletion only suggests me the Id column, but it doesn't see the truncated date.
Why can't I GroupBy multiple columns, with one of them is a truncated Date ?
Is there any workaround ?
You need to give your anonymous type's properties names if you want to use them later on:
.GroupBy(x => new
{ Date = DbFunctions.TruncateTime(x.v.MyDateTimeColumn),
Id = x.c.Id
})
Then you can project on that:
.Select(g => new
{
Date = g.Date,
NbViews = g.Count(),
})
And finally you cannot do this:
.ToDictionary(p => p.Date, p => p.NbViews);
because you will get this error:
An item with the same key has already been added.
Why? Because the Date is not unique since you just grouped by Date and Id so Date(s) will be duplicated. It is the same as this but this is a list of string:
var nums = new List<string> { "1", "1", "1", "2" };
nums.ToDictionary(x => x, x => x);
But, perhaps, you may want to do this:
var lu = nums.ToLookup(x => x, x => x);
And now you can look them up:
// Returns 3 items since there are 3 "1"s
IEnumerable<string> ones = lu["1"];

How to group product by category on list view by LinQ?

I'm a newbiew asp.net mvc !
I'm having a list view to display Products:
Id CateId CateName
1 2 Cate-B
|__ Product-B
2 1 Cate-A
|__ Product-A
3 1 Cate-A
|__ Product-C
but now I want to display a list view group product by category like below. How can I do it ?
Id CateId CateName
1 2 Cate-B
|__ Product-B
2 1 Cate-A
|__ Product-B
|__ Product-C
this is my code:
public IQueryable<MyGroup> GetAllProduct()
{
var query = (from c in _categoryRepository.Table()
join p in _productRepository.Table() on c.Id equals p.CateId
select new MyGroup
{
Categories= c,
Products = p
}).ToList();
return query.AsQueryable();
}
If I have understood your question correctly, this is what you need:-
var query = _categoryRepository.Table()
.GroupBy(x => new { x.Id, x.CateId, x.CateName })
.Select(x => new
{
x.Key.Id,
x.Key.CateId,
x.Key.CateName,
ProductsNames = _productRepository.Table()
.Where(p => p.CategoryId == x.Key.CateId)
.Select(p => p.ProductName).ToList()
}).ToList();
Here, is a Demo for same where I have used LINQ-To-Objects.
Use the GroupBy method to group by the category Id.
Eg. Viewmodel.GroupBy(r => r.cateid)
See here

orderby in linq to Entities

AllMovieInfo = from movieInfo in AllMovieInfo
from countryMovie in movieInfo.SeenItWantToSeenIt
where countryMovie.Status==1
select movieInfo;
var seenitorderby = db.SeenItWantToSeeIt.Where(m => m.Status == 1).GroupBy(m => m.MovieID).Select(g => new {MovieID =g.Key,count=g.Count()}).OrderBy(o=>o.count);
List<int> seenItList=seenitorderby.Select(s=>s.MovieID).ToList();
AllMovieInfo = (from a in AllMovieInfo
from s in seenItList
where seenItList.Contains(a.MovieID)
select a).Distinct();
This query is ordering the result according to "AllMovieInfo.MovieID" which is obvious but I have to order the "result" according to the id that comes is "seenitorderby" eg: seen it orderby may take movieID 2,25,7,14,25 then I need AllMovieInfo according same order as seenitorderby .How can I order the "result" according to "seenitorderby " ?
Based on your join aren't AllInfo.ID and SeenInfo.ID the same?
If I'm mistaken the following should do it
var result= (from a in AllInfo
from s in SeenInfo
where s.ID==a.ID
orderby s.ID // <-- this should be the SeenInfo primary key
select a).Distinct();
UPDATE: Based on question update
Thanks for the update. I think I now understand your problem. Do you wish to order by the count for a particular movie...
AllMovieInfo = from movieInfo in AllMovieInfo
from countryMovie in movieInfo.SeenItWantToSeenIt
where countryMovie.Status==1
select movieInfo;
var seenItOrderBy = db.SeenItWantToSeeIt
.Where(m => m.Status == 1)
.GroupBy(m => m.MovieID)
.Select(g => new { MovieID = g.Key, Count=g.Count()});
var result = (from a in AllMovieInfo
from s in seenItOrderBy
where s.MovieID = a.ID
orderby s.Count
select a).Distinct();
You may be able to simplify this as follows...
PLEASE NOTE: This is off the top of my head and based on what I believe you are trying to achive in your code, so please take it as such.
var result = db.AllMovieInfo
// Where someone wants to see it wants to see it
.Where(mi => mi.SeenItWantToSeeIt.Any(m => m.Status == 1))
// Order by number of people that have seen or want to see it
.OrderBy(mi => mi.SeenItWantToSeeIt.Count(m => m.Status == 1));

Joining of two queries and returning in list format

I am developing a MVC application.
I am using the two queries to fetch the record, and I want to get the common records from these queries .
I want to return the data set in list
Like this
return Json(poList, JsonRequestBehavior.AllowGet);
My two queries are..
var poList = (from po in db.PurchaseOrders
where po.CompanyId == companyId && po.PartyId == partyId && (po.IsDeleted == false || po.IsDeleted == null)
select po into newPO
select new
{
Name = newPO.PONo,
Id = newPO.Id
});
//.ToList().OrderBy(e => e.Name);
var poList2 = (db.Employees.Where(x => x.Id == EmpID)
.SelectMany(x => x.Roles)
.SelectMany(x => x.Employees)
.Distinct()
.SelectMany(x => x.PurchaseOrders)
.Select(po => new { Name = po.PONo, Id = po.Id }));
var finalPO = from PO in poList.ToList().Union(poList2).ToList() select PO);
The reason you can't union them is that the two lists return different objects.
The first list returns an anonymous type with members Name and Id. If, instead, you just wanted to return the purchase orders in query one, then you could simply use the following:
var poList = (
from po in db.PurchaseOrders
where po.CompanyId == companyId &&
po.PartyId == partyId &&
(po.IsDeleted == false || po.IsDeleted == null)
select po
);
You may need to append .ToList() to the query above in order to use the Union(...) method. Then, you should be able to union the two sequences together (assuming poList2 is also a sequence of db.PurhaseOrders objects.
Conversely, instead of changing query for poList above, you could change the query behind poList2 to the following to achieve the same effect, but different results:
var poList2 = (db.Employees.Where(x => x.Id == EmpID)
.SelectMany(x => x.Roles)
.SelectMany(x => x.Employees)
.Distinct()
.SelectMany(x => x.PurchaseOrders)
.Select(po => new { Name = po.PONo, Id = po.Id }));
Personally, I think the first one is more clear (unless there are many fields on the PO object and you only need the two as shown).
UPDATE: I see the original post was edited so that both queries now return the same object (or shape of object). However, the poster is still trying to combine the results incorrectly. The poster is using yet another LINQ query in an attempt to use the Union(...) method. This is completely unnecessary. Might as well write out the code for him/her:
var finalPO = poList.Union(poList2).ToList(); // ToList() only necessary if you need a list back
That should do it.
Really, the two books I mentioned in my comments below will get you a long way in understanding .NET and LINQ: APress - Pro C# and the .NET Framework 4.0; O'Reilly - C# 5 in a Nutshell. There are also many books on LINQ alone--but without a solid grasp of .NET (and C#, F#, or VB), you can't hope to understand or use LINQ.
I dont not think you need the ToList() in the intermediate results, just use the union and do the ToList in the final result, like:
var finalPO = poList.Union(poList2).ToList()
First, create a ViewModel like this:
public class PurchaseOrderViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Then, use it in your code like this:
var poList1 = (from po in db.PurchaseOrders
where po.CompanyId == companyId && po.PartyId == partyId
&& (po.IsDeleted == false || po.IsDeleted == null)
select po into newPO
select new PurchaseOrderViewModel
{
Name = newPO.PONo,
Id = newPO.Id
}).ToList();
var poList2 = (db.Employees.Where(x => x.Id == EmpID)
.SelectMany(x => x.Roles)
.SelectMany(x => x.Employees)
.Distinct()
.SelectMany(x => x.PurchaseOrders)
.Select(po => new PurchaseOrderViewModel
{
Name = po.PONo,
Id = po.Id
})).ToList();
var finalList = poList1.Union(poList2);

Resources