Get tuple and list linked as result - linq

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();

Related

LINQ Query for populating collection inside another collection

I am looking forward to get a linq query for populating list of teachers and their respective divisons.
Here I have 2 classes Teacher and Division which are related by DivisionGroupID - GroupID
public class Teacher
{
public int ID { get; set; }
public string Name { get; set; }
public List<Division> lstDivison {get;set;}
public int DivisionGroupID { get; set; }
}
public class Division
{
public int GroupID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
In main method List of both Teacher and Division will be populated
static void Main(string[] args)
{
Teacher obj = new Teacher { ID = 1, DivisionGroupID = 11, Name = "abcd" };
Teacher obj1 = new Teacher { ID = 2, DivisionGroupID = 12, Name = "efgh" };
List<Teacher> objList = new List<Teacher>();
objList.Add(obj);
objList.Add(obj1);
Division dv = new Division { GroupID = 11 ,Name="Division1",Description="first" };
Division dv1 = new Division { GroupID = 11, Name = "Division2", Description = "second" };
Division dv2 = new Division { GroupID = 11, Name = "Division3", Description = "third" };
Division dv3 = new Division { GroupID = 12, Name = "Division4", Description = "fourth" };
Division dv4 = new Division { GroupID = 12, Name = "Division5", Description = "fifth" };
Division dv5 = new Division { GroupID = 12, Name = "Division6", Description = "sixth" };
List<Division> lstDiv = new List<Division>();
lstDiv.Add(dv);
lstDiv.Add(dv1);
lstDiv.Add(dv2);
lstDiv.Add(dv3);
lstDiv.Add(dv4);
lstDiv.Add(dv5);
}
The requirement here is to get the list of teachers and populate the sublist of divisions each teachers holding. I got the solution based on 2 approaches.
Using sub query approach :
var upd = from teacher in objList
select new Teacher
{
ID = teacher.ID,
Name = teacher.Name,
lstDivison = (from div in lstDiv
where div.GroupID == teacher.DivisionGroupID
select new Division
{
Name = div.Name,
Description = div.Description
}).ToList()
};
Using Foeach loop through Teacher collection(objList) and updating the lstDivision
objList.ForEach(x => x.lstDivison = lstDiv
.Where(y => y.GroupID == x.DivisionGroupID)
.Select(p => new Division { Name = p.Name, Description = p.Description })
.ToList());
Both of these approaches will give me the result. But i am looking forward a better approach in as part of my project requirement which has to improve the query performance. Could you please suggest which is the best approach to handle this situation?
use yours teacher object to populate list of divisions under it. as my understanding that how it was designed class structure.
//populate property in object
objList.ForEach(x => {
x.lstDivison = lstDiv.Where(w=> w.GroupID == x.DivisionGroupID).ToList();
});
objList.Dump();

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" }
}
>

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 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,
...
};

merging 2 collections and find all the unique items

I have 2 collections need to create a 3 one if you like by merging the 2 and giving me a third one with all the unique items only
class Program
{
static void Main(string[] args)
{
ObservableCollection<Person> collectionA = new ObservableCollection<Person>
{
new Person {Id = 1, Name = "Name1", Surname = "Surname1"},
new Person {Id = 2, Name = "Name2", Surname = "Surname2"},
new Person {Id = 3, Name = "Name3", Surname = "Surname3"},
new Person {Id = 4, Name = "Name4", Surname = "Surname4"}
};
ObservableCollection<Person> collectionB = new ObservableCollection<Person>
{
new Person {Id = 5, Name = "Name5", Surname = "Surname5"},
new Person {Id = 2, Name = "Name2", Surname = "Surname2"},
new Person {Id = 6, Name = "Name6", Surname = "Surname6"},
new Person {Id = 4, Name = "Name4", Surname = "Surname4"}
};
ObservableCollection<Person> result=????
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
}
Any ideas?Thanks a lot
EDIT CLARIFICATION
I have collectionA, then I create collection B, compare the two collection add any item to FIND ALL THE ITEMS IN COLLECTIONB THAT DONT EXISTS IN COLLECTION A AND CREATE A RESULT COLLECTION.Hope clear now
If Id is a unique identifier of you person try this one:
ObservableCollection<Person> result = new ObservableCollection<Person>(collectionB
.Where(p => !collectionA.Any(p2=>p2.Id==p.Id)));
Edited answer:
ObservableCollection<Person> result = new ObservableCollection<Person>(collectionB.Except(collectionA));
Note that this will create a new collection that is not tied to the old collections - so if you add a person to collectionA, they will not show up in result automatically.

Resources