LINQ with Subquery/Group By/Join - linq

Newbie to LINQ, and trying to write the following query...
select
f.Section_ID,
f.Page_ID,
f.SortOrder,
f.Type
from
(
select
Section_ID,
min(SortOrder) as minSortOrder
from
ContentPages
group by
Section_ID
) as x
inner join
ContentPages as f on
f.Section_ID = x.Section_ID and
f.SortOrder = x.minSortOrder;
Notes:
'Section' has many 'ContentPages'
Sections are ordered by a 'SortOrder' field
ContentPages are also ordered by a 'SortOrder' field
Table: Section
Section_ID....Name.......SortOrder
....1.........One..........1......
....2.........Two..........3......
....3.........Three........2......
Table: ContentPage
Page_ID.......Section_ID.......Title..............SortOrder
....11.............1.......... Page One.............1......
....12.............1...........Page Two.............3......
....13.............2...........Page Three...........2......
....16.............2.......... Page Four............4......
....17.............2...........Page Eight...........5......
....18.............1...........Page Ten.............6......
The above query could possibly be written another way, so here's what I'm trying to do:
I need to return a list of the first ContentPage within each Section (when sorted by ContentPage.SortOrder)
Sort results by Section.SortOrder
Show Section.Name (join on Section_ID?) in the result as well
Last 2 points are not covered by the sql query above and are more of a 'nice to have'...
Desired Result
Page_ID.......Section_ID...SectionName.....Title..............SortOrder
....11.............1.........One......... Page One.............1......
....13.............2.........Two..........Page Three...........2......
Any help is appreciated. Thanks!

Here's my first attempt at it:
from sectionPage in pages
group sectionPage by sectionPage.Section_ID into sectionGroup
join page in pages on sectionGroup.Key equals page.Section_ID
where page.SortOrder == sectionGroup.Min(p => p.SortOrder)
orderby page.SortOrder
select page;
What happens is first we create a group on the section id so that we can get the minimum sort order later. Next, we join a new reference to pages in on the section id, and filter by SortOrder being the minimum from the section group. Note, for simple expressions like the Min() call, I prefer the inline lambda expression over another query.
Finally, we add an orderby to order the pages, and we return the page (note you can change this to certain fields if you prefer).

I think this is what you're looking for...
internal class Section
{
public int SectionId { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
}
internal class ContentPage
{
public int PageId { get; set; }
public int SectionId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
}
static void Main(string[] args)
{
List<Section> sections = new List<Section>();
sections.Add(new Section() { SectionId = 1, Name = "One", SortOrder = 1 });
sections.Add(new Section() { SectionId = 2, Name = "Two", SortOrder = 3 });
sections.Add(new Section() { SectionId = 3, Name = "Three", SortOrder = 2 });
List<ContentPage> contentPages = new List<ContentPage>();
contentPages.Add(new ContentPage() { PageId = 11, SectionId = 1, Title = "Page One", SortOrder = 1 });
contentPages.Add(new ContentPage() { PageId = 12, SectionId = 1, Title = "Page Two", SortOrder = 3 });
contentPages.Add(new ContentPage() { PageId = 13, SectionId = 2, Title = "Page Three", SortOrder = 2 });
contentPages.Add(new ContentPage() { PageId = 16, SectionId = 2, Title = "Page Four", SortOrder = 4 });
contentPages.Add(new ContentPage() { PageId = 17, SectionId = 2, Title = "Page Eight", SortOrder = 5 });
contentPages.Add(new ContentPage() { PageId = 18, SectionId = 1, Title = "Page Ten", SortOrder = 6 });
var items = from section in sections
orderby section.SortOrder
join contentPage in
(from contentPage in contentPages
orderby contentPage.SortOrder
group contentPage by contentPage.SectionId into grp
select grp.FirstOrDefault())
on section.SectionId equals contentPage.SectionId
select new
{
PageId = contentPage.PageId,
SectionId = section.SectionId,
SectionName = section.Name,
Title = contentPage.Title,
SortOrder = section.SortOrder
};
foreach (var newItem in items)
{
Console.WriteLine(string.Format("{0}\t{1}\t{2}\t{3}\t{4}", newItem.PageId, newItem.SectionId, newItem.SectionName, newItem.Title, newItem.SortOrder));
}
}
Note that the sample data you provided shows a sort order of 3 for section 2, but your sample results list its sort order as 2.

Related

How to write a Linq that can retrieve all parent table records and total of sub-table record, I mean 'separate' into two parts

Let's say I have two tables, parent table 'P' and sub-table 'S', I usually wrote the Linq like this to get what I want:
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select new{
Id = p.Id,
Title = p.Title
SubContentCount = subContent.Count()
}
It's very simple, but if for some reason I have to pass a parameter into this query when there has one (let's say 'key'), I have to do this (I guess :-):
var rows = from p in db.P
join s in db.S on p.Id equals s.ParentId into subContent
where (some condition here)
select p;
if(!string.IsNullOrEmpty(key)){ // I'm using C#
rows = rows.Where(q => q.Title.Contains(key))
}
And then:
var list = rows.Select(q => new ()
{
Id = q.Id,
Title = q.Title,
subCount = ???.Count()
});
Is that passable to do Linq like this? if so, how?
Thanks for any kind help!
You could create a method that receives a Func<Table, bool>as parameter and use it to filter your dataset:
public static void Main(string[] args)
{
var rows = new List<Table>
{
new Table { Id = 1, Title = "A", SubContent = new [] { "A1" } },
new Table { Id = 2, Title = "B", SubContent = new [] { "B1", "B2" } },
new Table { Id = 3, Title = "C", SubContent = new [] { "C1", "C2", "C3" } },
};
var title = "C";
foreach (var item in Filter(rows, table =>
String.IsNullOrEmpty(title) || table.Title == title))
{
Console.WriteLine(
"Title={0}, SubContent.Length={1}",
item.Title, item.SubContent.Length);
}
}
public static List<Table> Filter(List<Table> original, Func<Table, bool> filter)
{
return original.Where(filter).ToList();
}
public class Table
{
public int Id { get; set; }
public string Title { get; set; }
public string[] SubContent { get; set; }
}
Why not include the filter in the where clause?
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
Quick example in the interactive console:
public class Parent { public int Id {get; set;} public string Title {get; set;} }
public class SubTable { public int Id {get; set;} public int ParentId {get; set;} }
public class Result { public int Id {get; set;} public string Title {get; set;} public int SubContentCount {get; set;} }
var p1 = new Parent() { Id = 1, Title = "Parent_1" };
var p2 = new Parent() { Id = 2, Title = "Parent_2" };
var p3 = new Parent() { Id = 3, Title = "Parent_3" };
var s1_1 = new SubTable() { Id = 11, ParentId = 1 };
var s1_2 = new SubTable() { Id = 12, ParentId = 1 };
var s1_3 = new SubTable() { Id = 13, ParentId = 1 };
var s2_1 = new SubTable() { Id = 21, ParentId = 2 };
var s2_2 = new SubTable() { Id = 22, ParentId = 2 };
var s3_1 = new SubTable() { Id = 31, ParentId = 3 };
var db_P = new List<Parent>() { p1, p2, p3 };
var db_S = new List<SubTable>() { s1_1, s1_2, s1_3, s2_1, s2_2, s3_1 };
public IEnumerable<Result> GetResults(string key = null)
{
var rows = from p in db_P
join s in db_S on p.Id equals s.ParentId into subContent
where string.IsNullOrEmpty(key) || p.Title.Contains(key)
select new Result() {
Id = p.Id,
Title = p.Title,
SubContentCount = subContent.Count()
};
return rows;
}
And example output (formatted onto multiple lines for readability)
> GetResults().ToList()
List<Submission#0.Result>(3) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" },
Submission#0.Result { Id=2, SubContentCount=2, Title="Parent_2" },
Submission#0.Result { Id=3, SubContentCount=1, Title="Parent_3" }
}
> GetResults("1").ToList()
List<Submission#0.Result>(1) {
Submission#0.Result { Id=1, SubContentCount=3, Title="Parent_1" }
}
>

Get tuple and list linked as result

I have this query :
var query = (from tables ...
where ...
select new
{
ClientName = ClientName,
ClientNumber = ClientNumber,
ClientProduct = ClientProduct
}).Distinct();
which returns rows with 3 values.
ClientName and ClientNumber can be linked to multiple products.
So we can have :
NameA NumberA Product1
NameA NumberA Product2
NameA NumberA Product3
NameB NumberB Product4
NameC NumberC Product5
I would like to know if it is possible to store that in a List of a certain class which would be like :
class MyClass
{
string ClientName,
int ClientNumber,
List<int> ClientProducts
}
So there are no duplicate of ClientName and ClientNumber.
Thank you in advance.
With this class structure to represent your data:
class MyClass
{
public string ClientName { get; set; }
public int ClientNumber { get; set; }
public List<int> ClientProducts { get; set; }
}
class Procuct
{
public string ClientName { get; set; }
public int ClientNumber { get; set; }
public int ProductID { get; set; }
}
and this test data:
List<Procuct> Products = new List<Procuct>()
{
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 1},
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 2},
new Procuct() { ClientName = "A", ClientNumber = 1, ProductID = 3},
new Procuct() { ClientName = "B", ClientNumber = 2, ProductID = 4},
new Procuct() { ClientName = "C", ClientNumber = 2, ProductID = 5}
};
you can use the following linq query:
var q = from p in Products
group p by new
{
cName = p.ClientName,
cNumber = p.ClientNumber
} into pGroup
select new MyClass
{
ClientName = pGroup.Key.cName,
ClientNumber = pGroup.Key.cNumber,
ClientProducts = pGroup.Select(x => x.ProductID).ToList()
};
to get exactly what you want, i.e. a collection of MyClass objects.
The Grouping performed in the above linq query essentially guarantees that there will be no duplicates on (ClientName, ClientNumber).
Since you mention Linq-to-sql, most probably you Client entity already has the products linked. You might look for an overcomplicated solution.
It depends a bit on your foreign key stucture, but if your datamodel would be
Client has 1-many product and you have a Foreign key from product to client it is already present.
So you can just reference client.Products.
So in your case it would be
var query = (from Clients...
where ...
select new
{
ClientName = Client.ClientName,
ClientNumber = Client.ClientNumber,
ClientProduct = Client.Products.Select(s=>s.id).ToList()
});
But you might as well simply use your client entity with a eager load of the products.
It all depends on your datamodel + proper foreign key structure
if you have a many-many associations like Product-per-client between your client and product you can start from that entity. Have a look at this documentation - it provides a good starting point for Linq-2-sql.
http://weblogs.asp.net/scottgu/using-linq-to-sql-part-1
I solve same problem , I think it useful to you
Only check your Where Condition properly
Thank...
var query = (from tables ...
where ...
select new
{
ClientName = ClientName,
ClientNumber = ClientNumber,
ClientProduct = ClientProduct.ToList()
}).Distinct();

Linq to objects, joining to collections and simplify select new

I need some help to simplify a linq query. I have 2 classes Invoice and Customer.
The Invoice have a property CustomerId and a property Customer.
I need to get all invoices and include the Customer object.
I don't like my query, as it needs to change if new properties are added to the Invoice object.
I can't join the invoice and customer earlier than this stage so that is not an alternative.
My query.
var customers = GetCustomers();
var invoices = GetInvoices();
var joinedList = (from x in invoices
join y in customers on x.CustomerId equals y.CustomerId
select new Invoice
{
Amount = x.Amount,
CustomerId = x.CustomerId,
Customer = y,
InvoiceId = x.InvoiceId
}).ToList();
The classes
public class Invoice
{
public int InvoiceId { get; set; }
public double Amount { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
}
private static IEnumerable<Invoice> GetInvoices()
{
yield return new Invoice
{
Amount = 34,
CustomerId = 1,
InvoiceId = 1
};
yield return new Invoice
{
Amount = 44.7,
CustomerId = 1,
InvoiceId = 2
};
yield return new Invoice
{
Amount = 67,
CustomerId = 2,
InvoiceId = 3
};
yield return new Invoice
{
Amount = 89,
CustomerId = 3,
InvoiceId = 4
};
}
private static IEnumerable<Customer> GetCustomers()
{
yield return new Customer
{
CustomerId = 1,
Name = "Bob"
};
yield return new Customer
{
CustomerId = 2,
Name = "Don"
};
yield return new Customer
{
CustomerId = 3,
Name = "Alice"
};
}
Why not just a simple foreach loop:
// Dictionary for efficient look-up
var customers = GetCustomers().ToDictionary(c => c.CustomerId);
var invoices = GetInvoices().ToList();
//TODO: error checking
foreach(var i in invoices)
i.Customer = customers[i.CustomerId];

Linq Split properties of Class and assign it to another Custom Class

I have a Complex Situation now and i am terribly stuck. Kindly Let me know if you can share some light to it.
I have a
List Which will have the Following properties
public class Categories
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
}
This will have Values like
Category1/SubCategory1
cat1/sc1
5
Category1/SubCategory2
cat1/sc2
4
Category 2/Subcategory1
cat2/sc1
5
Category 2/Subcategory2
cat2/sc2
23
I created a Custom Class to fill in the values
public class JobCateogry
{
public string DisplayName { get; set; }
public string ValueCode { get; set; }
public string Count { get; set; }
public List<JobCateogry> SubCategories { get; set; }
}
I have to Split the String in the Code Value and assign it to the SubCategory.
Like My Final out of jobCategory would be
Category1
Cat1
9
SubCategory1
sub1
5
SubCateogry2
sub2
4
I tried to Split the string and assign it to the new class in two step first by splitting and then by assiging. But i am sure i am doing it the wrong way, because the moment i split, i loose the count .
var lstCategory = Categories
.Where(i => i.count > 0)
.Select(item => item.valueCode.Split('/')
.Select(k =>(k)).ToList();
List<JobCategories> jobcategories = lstCategory
.Select(item => item.Split(QueryStringConstants.CAT_SEPERATOR.ToCharArray()[0]))
.GroupBy(tokens => tokens[0].Trim(), tokens => tokens[1])
.Select(g => new JobCategories(g.Key, g.DisplayName,g.ToList(),)).ToList();
Can you please help?
A bit weird task
It might not be the best solution and it only works with the two layers :-), and i tried keeping a lot of linq for the fun of it
anyway hope it can get you moving forward.
full code snippet https://gist.github.com/cbpetersen/db698def9a04ebb2abbc
static void Main(string[] args)
{
var cats = new[]
{
new Categories { Count = "5", ValueCode = "cat1/sc1", DisplayName = "Category1/SubCategory1" },
new Categories { Count = "4", ValueCode = "cat1/sc2", DisplayName = "Category1/SubCategory2" },
new Categories { Count = "5", ValueCode = "cat2/sc1", DisplayName = "Category2/Subcategory1" },
new Categories { Count = "23", ValueCode = "cat2/sc2", DisplayName = "Category2/Subcategory2" }
};
var categories = cats.Select(x => x.DisplayName.Split('/')[0]).Distinct();
var list = new List<JobCateogries>();
foreach (var category in categories)
{
var a = new JobCateogries
{
ValueCode = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => x.ValueCode.Split('/')[0]).FirstOrDefault(),
DisplayName = category,
SubCategories = cats.Where(x => x.DisplayName.Split('/')[0] == category)
.Select(x => new JobCateogries
{
SubCategories = new List<JobCateogries>(),
Count = x.Count,
DisplayName = x.DisplayName.Split('/')[1],
ValueCode = x.ValueCode.Split('/')[1]
}).ToList(),
};
a.Count = a.SubCategories.Select(x => int.Parse(x.Count)).Sum().ToString();
list.Add(a);
}
list.ForEach(x => Print(x));
Console.ReadKey();
}
public static void Print(JobCateogries category, int indent = 0)
{
var prefix = string.Empty.PadLeft(indent);
Console.WriteLine(prefix + category.DisplayName);
Console.WriteLine(prefix + category.ValueCode);
Console.WriteLine(prefix + category.Count);
category.SubCategories.ForEach(x => Print(x, indent + 4));
}

LINQ with Group, Join and Where Easy in SQL not in LINQ?

I have trouble understand how to translate SQL into LINQ. I would like to do the following but can't figure out how to get the Group By to work
var query = from s in Supplier
join o in Offers on s.Supp_ID equals o.Supp_ID
join p in Product on o.Prod_ID equals p.Prod_ID
where s.City == "Chicago"
group s by s.City into Results
select new { Name = Results.Name };
I just need to do something simple like display the product name of this simple query, how does the group by work with joins and a where?
You haven't provided classes so I assumed that they are like below:
public class Supplier
{
public int SupplierID { get; set; }
public string SuppierName { get; set; }
public string City { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
}
public class Offer
{
public int SupplierID { get; set; }
public int ProductID { get; set; }
}
Then I added data for testing:
List<Supplier> supplierList = new List<Supplier>()
{
new Supplier() { SupplierID = 1, SuppierName = "FirstCompany", City = "Chicago"},
new Supplier() { SupplierID = 2, SuppierName = "SecondCompany", City = "Chicago"},
new Supplier() { SupplierID = 3, SuppierName = "ThirdCompany", City = "Chicago"},
};
List<Product> productList = new List<Product>()
{
new Product() { ProductID = 1, ProductName = "FirstProduct" },
new Product() { ProductID = 2, ProductName = "SecondProduct" },
new Product() { ProductID = 3, ProductName = "ThirdProduct" }
};
List<Offer> offerList = new List<Offer>()
{
new Offer() { SupplierID = 1, ProductID = 2},
new Offer() { SupplierID = 2, ProductID = 1},
new Offer() { SupplierID = 2, ProductID = 3}
};
If you want to show names of suppliers whiches products have been offered then your LINQ query should be as this:
IEnumerable<string> result = from supplier in supplierList
join offer in offerList on supplier.SupplierID equals offer.SupplierID
join product in productList on offer.ProductID equals product.ProductID
where supplier.City == "Chicago"
group supplier by supplier.SuppierName into g
select g.Key;
You can see if correct names have been selected:
foreach (string supplierName in result)
{
Console.WriteLine(supplierName);
}
It must give following result:
FirstCompany
SecondCompany
You could try this:
var query = from s in Supplier
join o in Offers on s.Supp_ID equals o.Supp_ID
join p in Product on o.Prod_ID equals p.Prod_ID
where s.City == "Chicago"
group s
by new {s.City, s.Name} //added this
into Results
select new { Name = Results.Key.Name };
You group s (Supplier) by s.City. The result of this is an IGrouping<City, Supplier>. I.e. only City and Supplier are within reach after the grouping: for each City you get an IEnumerable<Supplier> of its suppliers (which will be multiplied by the joins, by the way).
Since you also have the condition where s.City == "Chicago" grouping by city is of no use. There is only one city. So I think you may as well do something like this:
from s in Supplier
join o in Offers on s.Supp_ID equals o.Supp_ID
join p in Product on o.Prod_ID equals p.Prod_ID
where s.City == "Chicago"
select new {
City = s.City.Name,
Supplier = s.Name,
Product = p.Name,
...
};

Resources