Writing Group By on Anonymous Types - linq

I am writting a group by clause on two tables which are joined and being accessed via Entity Data Model. I am not able to iterate over the anonymous type, can somebody help me out.
public string GetProductNameByProductId(int productId)
{
string prodName=string.Empty;
using (VODConnection vodObjectContext = new VODConnection())
{
var products = from bp in vodObjectContext.BFProducts
join bpf in vodObjectContext.BFProductMasters on bp.ProductMasterId equals bpf.ProductMasterId
where bp.ProductId == productId
group bp by new { ProductId = bp.ProductId, ProductName = bp.ProductName, ProductMasterName=bpf.ProductMasterName} into newInfo
select newInfo;
//Want to iterate over products or in fact need to get all the results. How can I do that? Want productmastername property to be set in prodName variable by iterating
return (prodName);
}
}

One problem is that you've used a query continuation for no reason. That still shouldn't have prevented you from using the Key property, mind you. Try this as a slightly cleaner approach:
var products = from bp in vodObjectContext.BFProducts
join bpf in vodObjectContext.BFProductMasters
on bp.ProductMasterId equals bpf.ProductMasterId
where bp.ProductId == productId
group bp by new { bp.ProductId,
bp.ProductName,
bpf.ProductMasterName};
foreach (var group in products)
{
var key = group.Key;
// Can now use key.ProductName, key.ProductMasterName etc.
}
As for what you set your prodName variable to - it's unclear exactly what you want. The first ProductName value? The last? A concatenation of all of them? Why do you need a grouping at all?

foreach(var prod in products)
{
prodName += prod.Key.ProductMasterName;
}

Related

How to select count value by linq

I want express sql below by linq
select catalog,queryname,COUNT(*) from doctemplatecells group by catalog,queryname
I don't know how to get count(*), thanks
Every group consists of it's key (catalog, queryname) and it's elements as represented by the IEnumerable<> implementation of the group.
So if you have a group as a result of LinQ, you can call the extension method Count() on it.
var groups = doctemplatecells.GroupBy(dtc => new { Catalog = dtc.catalog, QueryName = dtc queryname });
foreach(group in groups)
{
console.WriteLine("{0} {1} #{2}", group.Key.Catalog, group.Key.QueryName, group.Count());
}

LINQ Distinct set by column value

Is there a simple LINQ query to get distinct records by a specific column value (not the whole record)?
Anyone know how i can filter a list with only distinct values?
You could use libraries like morelinq to do this. You'd be interested in the DistinctBy() method.
var query = records.DistinctBy(record => record.Column);
Otherwise, you could do this by hand.
var query =
from record in records
group record by record.Column into g
select g.First();
Select a single value first and then run the Distinct.
(from item in table
select item.TheSingleValue).Distinct();
If you want the entire record you need to use group x by into y. You then need to find a suitable aggregate function like First, Max, Average or similar to select one of the other values in the group.
from item in table
group item by item.TheSingleValue into g
select new { TheSingleValue = g.Key, OtherValue1 = g.First().OtherValue1, OtherValue2 = g.First().OtherValue2 };
You could make an implementation of the IEqualityComparer interface:
public class MyObjectComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
return x.ColumnNameProperty == y.ColumnNameProperty;
}
public int GetHashCode(MyObject obj)
{
return obj.ColumnNameProperty.GetHashCode();
}
}
And pass an instance into the Distinct method:
var distinctSource = source.Distinct(new MyObjectComparer());

Linq extract a count() value from a data object

I have divAssignments that has potential multiple rows by rNI, an official id, according to a compound key of Indictment and booking numbers.
rNI Booking Indictment
12345 954445 10 12345
12345 954445 10 12346
12345 954445 10 12347
So ID has a count of 3 for a single booking number for this rni.
I get lost attempting to generate a count and a group by booking Number:
var moreThen = from dA in divAssignments
select new { dA.rNI, IndictmentCount = dA.indictmentNumber.Count() };
Most of the examples are dealing with static int[] and don't seem to work in my case.
How do I get a group and then a count? If I could put in a having that would be fantastic.
from a t-sql POV I'd use this:
Select rni, bookingNumber, count(*) IndictmentCount
from divAssignments
group by rni, bookingNumber
having count(*) > 0
TIA
How about something like this:
var query = from item in divAssignments
group item by item.rNI into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
If you're interested in grouping by both the rNI and the booking number, I would change it to this:
var query = from item in divAssignements
group item by new { item.rNI, a.Booking } into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count
};
OR
var query = from item in divAssignments
group item by item into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
and implement IEquatable on the divAssignment object to support equality comparison. The other option if you'd like is to write an IEqualityComparer instance to do the composite key comparison. Your query could then look like:
var query =
divAssignments
.GroupBy(i => i, new MyCustomEqualityComparer())
.Select(i => new { Key = i.Key, Count = i.Count());
var query =
from dA in divAssignments
group dA by new { dA.rNI, dA.bookingNumber };
foreach(var grp in query)
{
Console.WriteLine("rNI={0}, bookingNumber={1} => Count={2}", grp.Key.rNI, grp.Key.bookingNumber, grp.Count());
}
If you use a Grouping operator in Linq you will get what you need. The code:
var count = from a in divAssignments
group a by new { a.rNI, a.Booking } into b
select b;
will return a collection of IGrouping objects. This will give you the Key (in my example this will be an anonymous type with an rNI and a Booking property) and a collection of the divAssignments that match the key.
Using Method syntax (much easier to read in my opinion):
First group the records, then select a new result for each group that contains the count.
var groups = divAssignments.GroupBy(d => new { d.rNI, d.Booking });
groups.Select(g=> new { g.Key.rNI, g.Key.Booking, IndictmentCount = g.Count() });

When IQueryable is created from a linq query why is it not a "new" variable?

I am using the Entity Framework and have got a loop that looks at a set of People and using a foreach loop creates a query for the address of each person. As each address query is created it is added to the node of a treeview where it can be later used (to populate children nodes):
IQueryable<Person> pQuery = (IQueryable<Person>)myContext.People; //get a list of people
//go through and get the set of addresses for each person
foreach (var p in pQuery)
{
var addressQuery = from a in myContext.Addresses
from al in a.Address_Links
where al.P_ID == p.P_ID
orderby a.A_POST_CODE
select a;
//add the query to a TreeView node (use the tag to store it)
TreeNode newNode = new TreeNode();
newNode.Tag = addressQuery;
}
Now, the problem that I am finding upon running the app is that ALL the queries are the last query created i.e. the last iteration of the loop. It is like the addressQuery is created on the first iteration of the loop and then overwritten on each subsequent query. The result of this is that it is like all the address queries in the treenodes are references to the last query made(?)
Further investigation that I could solve the problem by using a static class to generate the address query and pass that into each the TreeNode, as follows:
public static class Queries
{
public static IQueryable<Address> AddressesForPerson(GenesisEntities myContext, int key)
{
var query = from a in myContext.Addresses
from al in a.Address_Links
where al.P_ID == key
orderby a.A_POST_CODE
select a;
return query;
}
}
The question I have is that I am baffled by this behaviour. Why does having a static query class help me? Can anyone explain to me what is going on?
Confused.Com!
The reason is that the p variable (the foreach loop variable) is captured and the query is evaluated lazily. As a result, when the query is actually run, it uses the current value of p variable at that time, which is the last value. Read my answer to "What is the exact definition of a closure?" for more info.
To solve the problem, simply try introducing a temporary variable:
// `loopVariable` is scoped inside loop body AND the loop declaration.
foreach (var loopVariable in pQuery)
{
var p = loopVariable; // This variable is scoped **inside** loop body.
var addressQuery = from a in myContext.Addresses
from al in a.Address_Links
where al.P_ID == p.P_ID
orderby a.A_POST_CODE
select a;
//add the query to a TreeView node (use the tag to store it)
myTreeView.Tag = addressQuery
}

How do I use LINQ to obtain a unique list of properties from a list of objects?

I'm trying to use LINQ to return a list of ids given a list of objects where the id is a property. I'd like to be able to do this without looping through each object and pulling out the unique ids that I find.
I have a list of objects of type MyClass and one of the properties of this class is an ID.
public class MyClass
{
public int ID { get; set; }
}
I want to write a LINQ query to return me a list of those Ids.
How do I do that, given an IList<MyClass> such that it returns an IEnumerable<int> of the ids?
I'm sure it must be possible to do it in one or two lines using LINQ rather than looping through each item in the MyClass list and adding the unique values into a list.
IEnumerable<int> ids = list.Select(x=>x.ID).Distinct();
Use the Distinct operator:
var idList = yourList.Select(x=> x.ID).Distinct();
Using straight LINQ, with the Distinct() extension:
var idList = (from x in yourList select x.ID).Distinct();
When taking Distinct, we have to cast into IEnumerable too. If the list is <T> model, it means you need to write code like this:
IEnumerable<T> ids = list.Select(x => x).Distinct();
int[] numbers = {1,2,3,4,5,3,6,4,7,8,9,1,0 };
var nonRepeats = (from n in numbers select n).Distinct();
foreach (var d in nonRepeats)
{
Response.Write(d);
}
Output
1234567890

Resources