aiuto con una query LINQ due tavola
-
29-09-2019 - |
Domanda
Voglio categorie di visualizzazione e sottocategorie in questo modo:
Categoria 1
Sotto 1
Sotto 2
Sotto 3
Categoria 2
Sotto 5
Sotto 6
Sotto 7
In altre parole, categoria foreach, visualizzare le sottocategorie che appartengono ad ognuno di sotto.
I miei due tabelle sono in questo modo:
categoria-
IDCategoria
Nome
SubCategory-
SubCategoryID
SubCategoryName
IDCategoria
Ho una chiave esterna da categoria a sottocategoria un uno a molti.
Ecco dove ho ottenuto il codice, che visualizza tutte le sottocategorie foreach categoria.
public void displayLinqCategory()
{
MyDataContext dbm = new MyDataContext();
var q = from category in dbm.Categories
join subCat in dbm.SubCategories
on category.CategoryID equals subCat.CategoryID
select new { category.Name, subCat.SubCategoryName };
resultSpan.InnerHtml += "<table>";
foreach (var c in q)
{
resultSpan.InnerHtml += "<tr><td>" + c.Name + "</td></tr>";
foreach (var s in q)
{
resultSpan.InnerHtml += "<tr><td> " + s.SubCategoryName + "</td></td>";
}
}
resultSpan.InnerHtml += "</table>";
}
Soluzione
Se si aggiunge una clausola INTO che raggrupperà le relative categorie in una collezione che si può facilmente iterare sopra.
Ecco come:
using (var dbm = new MyDataContext())
{
var query = dbm.Categories
join s in dbm.SubCategories on c.CategoryID equals s.CategoryID
//group the related subcategories into a collection
into subCollection
select new { Category = c, SubCategories = subCollection };
foreach (var result in query)
{
//use result.Category here...
//now go through the subcategories for this category
foreach (var sub in result.Subcategories)
{
//use sub here...
}
}
}
Altri suggerimenti
Se si dispone di proprietà di navigazione nel modello:
MyDataContext dbm = new MyDataContext();
var groups = dbm.SubCategories
.Select(x=> new { CatName = x.Category.Name, x.SubCategoryName });
.GroupBy(x=>x.CatName);
resultSpan.InnerHtml += "<table>";
foreach (var group in groups)
{
resultSpan.InnerHtml += "<tr><td>" + group.Key + "</td></tr>";
foreach (var s in group)
{
resultSpan.InnerHtml += "<tr><td> " + s.SubCategoryName + "</td></td>";
}
}
resultSpan.InnerHtml += "</table>";
Se non avete aggiunto i riferimenti al modello è ancora possibile ottenere quello che ti serve utilizzando GroupJoin
var groups = dbm.Categories
.GroupJoin(
dbm.SubCategories,
x => x.CategoryID,
x => x.CategoryID,
(x, y) => new {Category = x.CategoryName, SubCategories = y.Select(s=>s.SubCategoryName)}
);
Come si può vedere, ci sono un certo numero di risposte "giuste". Ecco come lo farei:
// Data access belongs in its own area. Don't do it alongside HTML generation.
// Program to an interface so you can mock this repository in unit tests.
public interface ICategoryInfoRepository {
IEnumerable<CategoryInfo> GetCategoryInfo();
}
public class CategoryInfo {
public string CategoryName {get;set;}
public IEnumerable<string> SubCategoryNames {get;set;}
}
public class CategoryInfoRepository : ICategoryInfoRepository
{
public IEnumerable<CategoryInfo> GetCategoryInfo()
{
// The 'using' clause ensures that your context will be disposed
// in a timely manner.
using (var dbm = new MyDataContext())
{
// This query makes it pretty clear what you're selecting.
// The groupings are implied.
var q = from category in dbm.Categories
select new {
CategoryName = category.Name,
SubCategoryNames =
from subcategory in category.SubCategories
select subcategory.Name
};
// Make sure all the data is in memory before disposing the context
return q.ToList();
}
}
}
// Since all this method does is convert its input into a string, it would
// be very easy to unit-test.
public string GetCategoriesHtml(IEnumerable<CategoryInfo> categoryInfos) {
// A StringBuilder will make this HTML generation go much faster
var sb = new StringBuilder();
// Don't use tables to represent non-tabular data.
// This is a list, so let's make it a list.
// Use CSS to format it to your liking.
sb.Append("<ul>");
foreach(var categoryInfo in categoryInfos)
{
sb.Append("<li>").Append(categoryInfo.CategoryName).Append("<ul>");
foreach(var subCategoryName in categoryInfo.SubCategoryNames)
{
sb.Append("<li>").Append(subCategoryName).Append("</li>");
}
sb.Append("</ul></li>");
}
sb.Append("</ul>");
return sb.ToString();
}
public void DisplayLinqCategory()
{
// The repository would ideally be provided via dependency injection.
var categoryInfos = _categoryInfoRepository.GetCategoryInfo();
resultSpan.InnerHtml = GetCategoriesHtml(categoryInfos);
}
Ho fatto diversi "miglioramenti" che avrebbe senso in una prospettiva a lungo termine, progetto del mondo reale. Sentitevi liberi di ignorare quelli che non hanno senso per la vostra situazione particolare.