LINQ: Grouping SubGroup - c#

How to group SubGroup to create list of Continents where each Continent has it own counties and each country has its own cities like this table
Here is the t-sql:
select Continent.ContinentName, Country.CountryName, City.CityName
from Continent
left join Country
on Continent.ContinentId = Country.ContinentId
left join City
on Country.CountryId = City.CountryId
and the result of t-sql:
I tried this but it groups the data in wrong way i need to group exactly like the above table
var Result = MyRepository.GetList<GetAllCountriesAndCities>("EXEC sp_GetAllCountriesAndCities");
List<Continent> List = new List<Continent>();
var GroupedCountries = (from con in Result
group new
{
con.CityName,
}
by new
{
con.ContinentName,
con.CountryName
}
).ToList();
List<Continent> List = GroupedCountries.Select(c => new Continent()
{
ContinentName = c.Key.ContinentName,
Countries = c.Select(w => new Country()
{
CountryName = c.Key.CountryName,
Cities = c.Select(ww => new City()
{
CityName = ww.CityName
}
).ToList()
}).ToList()
}).ToList();

You need to group everything by continent, these by country and the countries by city:
List<Continent> List = MyRepository.GetList<GetAllCountriesAndCities>("EXEC sp_GetAllCountriesAndCities")
.GroupBy(x => x.ContinentName)
.Select(g => new Continent
{
ContinentName = g.Key,
Countries = g.GroupBy(x => x.CountryName)
.Select(cg => new Country
{
CountryName = cg.Key,
Cities = cg.GroupBy(x => x.CityName)
.Select(cityG => new City { CityName = cityG.Key })
.ToList()
})
.ToList()
})
.ToList();

You should apply grouping twice
var grouped = Result
.GroupBy(x => x.CountryName)
.GroupBy(x => x.First().ContinentName);
var final = grouped.Select(g1 => new Continent
{
ContinentName = g1.Key,
Countries = g1.Select(g2 => new Country
{
CountryName = g2.Key,
Cities = g2.Select(x => new City { CityName = x.CityName }).ToList()
}).ToList()
});

I know this is old, but I wanted to mention a much easier way per microsoft that is a bit more readable. This is an example with only 2 levels though but it will most likely work for others who reach this page (like me)
var queryNestedGroups =
from con in continents
group con by con.ContinentName into newGroup1
from newGroup2 in
(from con in newGroup1
group con by con.CountryName)
group newGroup2 by newGroup1.Key;
The documentation for that is at
https://learn.microsoft.com/en-us/dotnet/csharp/linq/create-a-nested-group
and is using a Student object as an example
I do want to mention that the method they use for printing is harder than just creating a quick print function on your continent object

Related

Linq to Entity to return a record from left table and 0 or 1 of right table using Method Syntax?

I'm trying to return a record from my main table with a join to a secondary table that may or may not return a record. I need to do this using Method Syntax if at all possible.
My current query is
Organizations.Where(a=>a.OrganizationId==10033)
.Join(Addresses, org => org.ClientId, addy => addy.ClientId, (org, addy) => new { org, addy })
.Join(Contacts, o => o.org.OwnerClientId, c => c.ClientId, (o, c) => new { o, c })
.Select(a => new Establishment
{
Name = a.o.org.Name,
City = a.o.addy.PhysicalAddressCity,
State = a.o.addy.PhysicalAddressState,
Id = Convert.ToInt32(a.o.org.OrganizationId),
AddressLine = a.o.addy.PhysicalAddressLine,
ZipCode = a.o.addy.PhysicalFullPostal,
AssignedInspector = "Jacques Clouseau",
ManagerName = a.c.LastName
}).FirstOrDefault()
This returns the correct information if there is a matching record in the contact table but I need it to return the Org and Address even if there is no Contact.
Is this possible using Method Syntax?
*****Additional note:
Forgot to mention this is using EF Core 3.0
You need to use GroupJoin:
Organizations.Where(a=>a.OrganizationId==10033)
.Join(Addresses, org => org.ClientId, addy => addy.ClientId, (org, addy) => new { org, addy })
.GroupJoin(Contacts, o => o.org.OwnerClientId, c => c.ClientId, (o, contact) => new { o, c = contact.FirstOrDefault() })
.Select(a => new Establishment
{
Name = a.o.org.Name,
City = a.o.addy.PhysicalAddressCity,
State = a.o.addy.PhysicalAddressState,
Id = Convert.ToInt32(a.o.org.OrganizationId),
AddressLine = a.o.addy.PhysicalAddressLine,
ZipCode = a.o.addy.PhysicalFullPostal,
AssignedInspector = "Jacques Clouseau",
ManagerName = a.c.LastName
}).FirstOrDefault()

LinQ select an object with a list as parameter

I'm using Linq queries to select from CRM 2013 with my webservice.
My final entity looks like this:
class FOO{
Guid fooId{get;set;}
String fooName{get;set;}
List<Car> fooCars{get;set;}
List<House> fooHouses{get;set;}
}
Where Car looks like this:
class Car{
Guid carId{get;set;
String carName{get;set;}
Guid carFooId{get;set;}
}
And House looks like:
class House{
Guid houseId{get;set;}
String houseName{get;set;}
Guid houseFooId{get;set;}
}
Now, my problem is the following:
I wanna query only once the crm and retrieve a list of FOO with all lists inside it. At the moment I do like this:
List<FOO> foes = crm.fooSet
.Where(x => x.new_isActive != null && x.new_isActive.Value = true)
.Select(x => new FOO(){
fooId = x.Id,
fooName = x.Name,
fooCars = crm.carSet.Where(c => c.fooId == x.Id)
.Select(c => new Car(){
carId = c.Id,
carName = c.Name,
carFooId = c.fooId
}).ToList(),
fooHouses = crm.houseSet.Where(h => h.fooId == x.Id)
.Select(h => new House(){
houseId = h.Id,
houseName = h.Name,
houseFooId = h.fooId
}).ToList()
}).ToList();
My aim is to use LinQ with the non-lambda queries and retrieve all the datas I need with a join and a group-by, but I don't really know how to achieve it, any tip?
Thanks all
I think what you are looking for is a group join:
var query= from f in crm.fooSet
join c in crm.carSet on c.fooId equals f.Id into cars
join h in crm.houseSet on h.fooId equals f.Id into houses
where f.new_isActive != null && f.new_isActive.Value == true
select new FOO{ fooId = f.Id,
fooName = f.Name,
fooCars=cars.Select(c => new Car(){
carId = c.Id,
carName = c.Name,
carFooId = c.fooId
}).ToList(),
fooHouses=houses.Select(h => new House(){
houseId = h.Id,
houseName = h.Name,
houseFooId = h.fooId
}).ToList()
};

LINQ not sorting List<> properly

My EF query is supposed to be sorting by the date of the first Product in the list, but for some reason, it only sorts most of the products and some of the dates are in the wrong order.
Here's the code...
using (var context = new SalesEntities())
{
var groupedData = context.s84_Schedule.AsExpandable()
.Where(predicate)
.GroupBy(c => new { c.CustomerID, c.s84_Customer.CustomerName, c.SubdivisionID, c.s84_Subdivision.SubdivisionName, c.LotNumber })
.Select(grouped => new s84_Report_Project_POCO
{
CustomerID = grouped.Key.CustomerID,
CustomerName = grouped.Key.CustomerName,
SubdivisionID = grouped.Key.SubdivisionID,
SubdivisionName = grouped.Key.SubdivisionName,
LotNumber = grouped.Key.LotNumber,
Products = grouped.Select(x => new s84_Report_Project_Product
{
ProductID = x.ProductID,
ProductName = x.s84_Product.ProductName,
ProductDate = x.CustomerExpectedDate,
FieldRepID = x.FieldRepID,
FieldRepName = x.s84_FieldRep.FieldRepName,
InstallerID = x.InstallerID,
InstallerName = x.s84_Installer.InstallerName,
StatusID = x.StatusID,
StatusColor = x.s84_Status.StatusColor,
StatusName = x.s84_Status.StatusName,
Completed = x.Completed
}).ToList()
});
var finalList = groupedData.ToList().Where(x => x.Products.Last().Completed == false).ToList();
List<s84_Report_Project_POCO> lst = finalList.OrderBy(x => x.Products.First().ProductDate).ToList();
return lst;
}
Code seems good to me, but look at how one of the dates is out of order...
weird sorting http://www.84sales.com/weird_sort.png
Try doing the order by on the inital select
var groupedData = context.s84_Schedule.AsExpandable()
.Where(predicate)
.GroupBy(c => new { c.CustomerID,
c.s84_Customer.CustomerName,
c.SubdivisionID,
c.s84_Subdivision.SubdivisionName,
c.LotNumber })
.Select(grouped => new s84_Report_Project_POCO
{
CustomerID = grouped.Key.CustomerID,
CustomerName = grouped.Key.CustomerName,
SubdivisionID = grouped.Key.SubdivisionID,
SubdivisionName = grouped.Key.SubdivisionName,
LotNumber = grouped.Key.LotNumber,
Products = grouped
.Select(x => new s84_Report_Project_Product
{
ProductID = x.ProductID,
ProductName = x.s84_Product.ProductName,
ProductDate = x.CustomerExpectedDate,
FieldRepID = x.FieldRepID,
FieldRepName = x.s84_FieldRep.FieldRepName,
InstallerID = x.InstallerID,
InstallerName = x.s84_Installer.InstallerName,
StatusID = x.StatusID,
StatusColor = x.s84_Status.StatusColor,
StatusName = x.s84_Status.StatusName,
Completed = x.Completed
}).OrderBy(x => x.CustomerExpectedDate).ToList()
});
The problem is the .First() function, witch returns the first record, but not necessarly in date order. if you wich to order your grouped datas by date so that the First() function returns the most recent date, you'll need to order your datas before grouping them, and then REorder your results with the First()function :
using (var context = PrimaryConnection.returnNewConnection())
{
var groupedData = context.s84_Schedule.AsExpandable()
.Where(predicate)
.GroupBy(c => new { c.CustomerID, c.s84_Customer.CustomerName, c.SubdivisionID, c.s84_Subdivision.SubdivisionName, c.LotNumber })
.Select(grouped => new s84_Report_Project_POCO
{
CustomerID = grouped.Key.CustomerID,
CustomerName = grouped.Key.CustomerName,
SubdivisionID = grouped.Key.SubdivisionID,
SubdivisionName = grouped.Key.SubdivisionName,
LotNumber = grouped.Key.LotNumber,
Products = grouped
.Select(x => new s84_Report_Project_Product
{
ProductID = x.ProductID,
ProductName = x.s84_Product.ProductName,
ProductDate = x.CustomerExpectedDate,
FieldRepID = x.FieldRepID,
FieldRepName = x.s84_FieldRep.FieldRepName,
InstallerID = x.InstallerID,
InstallerName = x.s84_Installer.InstallerName,
StatusID = x.StatusID,
StatusColor = x.s84_Status.StatusColor,
StatusName = x.s84_Status.StatusName,
Completed = x.Completed
}).Orderby(t => t.CustomerExpectedDate).ToList()
});
var finalList = groupedData.ToList().Where(x => x.Products.Last().Completed == false).ToList();
List<s84_Report_Project_POCO> lst = finalList.OrderBy(x => x.Products.First().ProductDate).ToList();
All SQL queries (and hence Linq queries, when attached to a SQL database) have a random order, unless you sort them.
Products is not sorted - hence it has a random order.
You sort by Products.First(), but Products has a random order, so your sort will also be random.
Make sure Products is sorted within the query, and you should be ok.
Products = grouped.Select(....)
.OrderBy(x => x.ProductDate)
.ToList()

How to change in elegant way List<> structure

I am using LINQ to entitiy in my project.
I have this LINQ:
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
select new
{
Id = inspArch.Id,
clientId = inspArch.CustomerId,
authId = inspAuth.Id
}).ToList();
After LINQ is executed result has this value :
Is there any elegant way (for example using LINQ or change above existing LINQ) to create from the list above, new list like that:
I haven't built this to see if it compiles, but this should work. You need to aggregate the Id and AuthId fields.
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
select new
{
Id = inspArch.Id,
clientId = inspArch.CustomerId,
authId = inspAuth.Id
})
.GroupBy(g => g.clientId)
.select(s => new {
Id = string.Join(",", s.Select(ss => ss.Id.ToString())),
ClientId = s.Key,
AuthId = string.Join(",", s.Select(ss => ss.authId.ToString()).Distinct()),
}).ToList();
You need group by and you can apply String.Join on the resulting IGrouping:-
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
group new { inspArch, inspAuth } by inspArch.CustomerId into g
select new
{
Id = String.Join(",",g.Select(x => x.inspArch.Id),
clientId = x.Key,
authId = String.Join(",",g.Select(x => x.inspAuth.Id)
}).ToList();
The tricky part here is to group both objects i.e. new { inspArch, inspAuth } because we need to access properties from both.
Update:
Since this is entity framework, it won't be able to translate the method String.Join to SQL, so we can bring back the grouped object to memory using AsEnumerable and then project it like this:-
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
group new { inspArch, inspAuth } by inspArch.CustomerId into g
select g).AsEnumerable()
.Select(g => new
{
Id = String.Join(",",g.Select(x => x.inspArch.Id),
clientId = x.Key,
authId = String.Join(",",g.Select(x => x.inspAuth.Id)
}).ToList();

Entity Framework: Query is slow

I am having some problems with Entity Framework.
I have simplified this to make it easier to explain.
These are my mssql tables
I use the following code to get all cities for each of the countries in my MSSQL database
var country = new Country()
{
Cities = obj.Counties.SelectMany(e => e.Cities).Select(city => new DCCity
{
Name = city.Name,
Population = city.Population
})
};
This is returned as json
There is a bit more then 40.000 records in the city table. To retrieve a list with all the countries and their respective cities it takes around 8 seconds. I am trying to reduce this. Anyone know some optimization tips to achieve this?
You need to query the Cities table first to get all data:
var cities = _context.Cities.Select(x => new {
ContryId = x.County.Country.CountryId,
ContryName = x.County.Country.Name,
CityId = x.Id,
CityName = x.Name
});
var countryLookup = new Dictionary<int, CountryDto>(approximatelyCountOfCountries);
foreach (var city in cities)
{
CountryDto country;
if (!countryLookup.TryGetValue(city.CountryId, out country))
{
country = new CountryDto {
Name = city.CountryName,
Id = city.CountryId
Cities = new List<CityDto>(approximatelyCountOfCities)
};
countryLookup.Add(country.Id, country);
}
country.Cities.Add(new CityDto { Name = city.Name, Id = city.Id });
}
In this way the result will be the:
countryLookup.Values
Try to do somthing like this:
var result = from c in countries
join conty in counties on c.id equals conty.CountryId
join city in cities on conty.id equals city.CountyId
group city by c.Name into g
select new
{
Name = g.Key,
Cities = g.Select(x =>
new
{
x.Name,
x.Population
})
};

Categories

Resources