IGrouping does not contain a definition for - c#

I've been looking at other threads here to learn how to do a GroupBy in linq. I am following the EXACT syntax that has worked for others, but, it's not working.
Here's the query:
var results = from p in pending
group p by p.ContactID into g
let amount = g.Sum(s => s.Amount)
select new PaymentItemModel
{
ContactID = g.ContactID, // <--- Error here
Amount = amount
};
pending is a List<T> that contains, among other columns, ContactID and Amount, but those are the only two I care about for this query.
The trouble is, inside the the select new, the g. won't give me any of the columns inside the original list, pending. And when I try, I get:
IGrouping <int, LeadPurchases> does not contain a definition for ContactID, and no extension method blah blah blah...
This is the SQL I am trying to emulate:
SELECT
lp.PurchasedFromContactID,
SUM (lp.Amount)
FROM
LeadPurchases lp
GROUP BY
lp.PurchasedFromContactID

You are grouping on the basis of ContactID, so it should be the Key for the result, So you have to use g.Key instead of g.ContactID; Which means the query should be like the following:
from p in pending
group p by p.ContactID into g
let amount = g.Sum(s => s.Amount)
select new PaymentItemModel
{
ContactID = g.Key,
Amount = amount
};
updates :
If you want to perform grouping based on more than one column then the GroupBy clause will be like this:
group p by new
{
p.ContactID,
p.Field2,
p.Field3
}into g
select new PaymentItemModel()
{
ContactID = g.Key.ContactID,
anotherField = g.Key.Field2,
nextField = g.Key.Field3
};

Related

group by issue with extra columns

I group the result on the customers zipcode. For each zipcode, I want to see the amount of bookings and the amount of equipment that is ordered.
So far my code looks like this:
var statistics = from b in db.Bookings
from c in db.Customers
where b.customerID == c.id
group c by c.zipcode into stat
select new {
Zipcode = stat.Key,
NumberOfBookings = stat.Count()
};
This code groups result into zipcodes and gives me the amount of bookings in each zipcode. How to get the amount of equipment also?
Rather than using joins like in SQL, you can (and it's better) use the navigation properties from your model:
var statistics =
from b in db.Bookings
group b by b.Customer.zipcode into g
select new
{
Zipcode = g.Key,
NumberOfBookings = g.Count(),
NumberOfEquipments = g.SelectMany(b => b.Equipments).Count(),
};
Note that the g variable represents a set of bookings with the same zipcode, so SelectMany is used to get all associated equipments before applying the Count operator.
Of course that's not the only way, for instance you can use Sum instead:
NumberOfEquipments = g.Sum(b => b.Equipments.Count())

Linq query - multiple joins and selecting 2 columns

I am using Entity Framework to work with my DB and I need help forming a LINQ query that will help me to get columns LoginId and AccNumber. I need only 1 LoginId for every single AccNumber, doesn't matter which LoginId it is. As it stands now, there are about 800K LoginIds and only 5000 AccNumber. Therefore, at the end I need 1 AccNumber associated with 1 LoginId. So I should have 5000 rows and 2 columns.
Here are the tables that I need joined:
Item
ItemId, AccNumber, other irrelevant columns
Block_Item
ItemId, BlockId, other irrelevant columns
Bookversion_Block
BookversionId, BlockId, other irrelevant columns
Sudent
LoginId, BookversionId, other irrelevant columns
Ideally, I want to replicate this SQL query using LINQ (just trying to provide as much info as possible)
select
LoginId,
AccessionNumber
from Item I
outer apply (select top 1 * from Block_Item where I.ItemId = ItemId) BI
outer apply (select top 1 * from BookletVersion_Block where BlockId = BI.BlockId) BVB
outer apply (select top 1 LoginId from Student where BookletVersionId = BVB.BookletVersionId) ST
Here is what I tried, however, the results that I get back are not correct and I get back like 183,000 records and it takes like 10 minutes to execute this. This line "var uniques = q.ToList();" is the one taking a long time.
using (var context = new MyContext())
{
context.Database.CommandTimeout = 1200;
var listOfAccessionNumbers = GetListOfAllAccessionNumbers(context);
var q = (from items in context.Items
join blockItem in context.Block_Item
on items.ItemId equals blockItem.ItemId into bi
join bookletVersion in context.BookletVersion_Block
on bi.Select(x => x.BlockId).FirstOrDefault() equals bookletVersion.BlockId into BVB
join student in context.Students
on BVB.Select(x => x.BookletVersionId).FirstOrDefault() equals student.BookletVersionId into st
//'VH098334'
select new { LoginId = st.Select(x => x.LoginId).FirstOrDefault().ToString(),
AccNum = items.AccessionNumber.ToString() });
**var uniques = q.ToList();**
IList<string> listOfLogins = new List<string>();
foreach (var accessionNumber in listOfAccessionNumbers)
{
var i = q.ToList().Find(x => x.AccNum.Contains(accessionNumber));
listOfLogins.Add(i.LoginId);
}
}
Try to separate your linq operations, to hopefully see which one is taking the longest and where its going wrong.
Also, I suggest using a dictionary with accNumber as key and LoginID as value so you can make sure each accNumber is associated with 1 and only 1 LoginID.
Dictionary<string, string> uniques = new Dictionary<string, string>();
var q = (from items in context.Items
join bi in context.Block_Item
on items.ItemId equals bi.ItemId
select items).ToList();
//var first = bi.Select(x => x.BlockId).FirstOrDefault();
var val = (from b in context.BookletVersion_Block.Select(bv => bv.BlockId)
join a in q on b equals q.FirstOrDefault().BlockId into BVB
join c in context.Students on BVB.FirstOrDefault().BookletVersionId equals c.BookletVersionId into st
select new { LoginId = st.Select(x => x.LoginId).FirstOrDefault().ToString(), AccNum = a.AccessionNumber.ToString() });
//if (!uniques.ContainsKey(bi.AccNum))
//uniques.Add(val.AccNum, val.LoginId);
uniques = val.ToDictionary(c => c.AccNum, c => c.LoginId);
Haven't tested it, so let me know the time taken and whether you get correct values.
The dictionary should ensure you get 5000 values, but I am not sure why it is taking so long, but lets fix the number of records problem first.

How to return an object with the number of grouped rows plus the group by field value?

I'm on C#, .NET 4, and using LINQ I need to group all rows in a table, due to a fixed field, returning that groupby field value and the number of each rows grouped thanks to that clause.
The code I wrote is:
var result = (from p in db.MyPersons
group p by new { p.IDPerson } into g
select new
{
IDPerson = g.Key.IDPerson,
Counter = g.Sum(p => p.IDPerson)
});
but it returns IDPerson on both IDPerson and Counter field in the new generated object.
What's the mistake? It seems Sum doesn't sum?
If you want the number of rows in the group use Count instead: (note you don't need an anonymous type either)
var result = (from p in db.MyPersons
group p by p.IDPerson into g
select new
{
IDPerson = g.Key,
Counter = g.Count()
});
Sum would add up all of the IDPerson values in the group - which would return the same value as IDPerson if there were only one item in the group.
If you want to group by more than one column, you can do this:
var result = (from p in db.MyPersons
group p by new{ p.IDPerson, p.Surname} into g
select new
{
IDPerson = g.Key,
Surname=g.Surname,
Counter = g.Count()
});

Get foreign key table with a single query using LINQ

I have the following two tables
Groups
- Id
- Name
Members
- Id
- GroupId
- Name
I want to display a list of Groups with their member count. I can get all the groups and then use a foreach statement to count the number of members. But this is very inefficient.
Is there a way to write a LINQ query that will return the member count in a single query?
You can do it with a simple linq-to-entities query. You can either project it to an anonymous type or you can create your own class to store the information.
var query = from g in context.Groups
join m in context.Members on g.Id equals m.GroupId into members
select new
{
Group = g,
MemberCount = members.Count(),
};
If there are foreign key relationships, wouldn't this work?
var q = context.Groups.Select(x => new { Group = x.Name, Count = x.Members.Count() } );

How to cast a Linq Dynamic Query result as a custom class?

Normally, I do this:
var a = from p in db.Products
where p.ProductType == "Tee Shirt"
group p by p.ProductColor into g
select new Category {
PropertyType = g.Key,
Count = g.Count() }
But I have code like this:
var a = Products
.Where("ProductType == #0", "Tee Shirt")
.GroupBy("ProductColor", "it")
.Select("new ( Key, it.Count() as int )");
What syntax could I alter to produce identical results, i.e., how do I do a projection of Category from the second Linq statement?
I know in both that g and it are the same and represent the entire table record, and that I am pulling the entire record in just to do a count. I need to fix that too. Edit: Marcelo Cantos pointed out that Linq is smart enough to not pull unnecessary data. Thanks!
Why would you have to do it at all? Since you still have all of the information after the GroupBy call, you can easily do this:
var a = Products
.Where("ProductType == #0", "Tee Shirt")
.GroupBy("ProductColor", "it")
.Select(c => new Category {
PropertyType = g.Key, Count = g.Count()
});
The type of Products should still flow through and be accessible and the regular groupings/filtering shouldn't mutate the type that is flowing through the extension methods.

Categories

Resources