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
})
};
Related
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
Sorry if the title was confusing.
Currently I am practicing with Entity Framework and LINQ expressions and got stuck on this.
I have a table with columns:"Personal ID", "Name", "Surname", "Phone ID" and all values on Phone ID are unique. I would like to create another table in which there would be same columns except for last being "Phone Count" which shows how many phones are associated with same Person(Personal ID). I want the table to show only 3 first highest count rows.
Here is the code i've wrote to make table that i've described above.
using (var db = new PRDatabaseEntities())
{
var query = from a in db.Person
join t in db.Repairs on a.PID equals t.Repairman_PID
orderby a.PID
select new
{
a.PID,
a.Name,
a.Surname,
t.Phone_ID
};
}
You could try with following group by LINQ query:
// First, generate a linq query
var query = from a in Persons
join t in Repairs on a.PID equals t.Repairman_PID
group new { a, t } by new { a.PID, a.Name, a.Surname } into g
select new
{
PID = g.Key.PID,
Name = g.Key.Name,
Surname = g.Key.Surname,
PhoneCount = g.Count()
};
// Then order by PhoneCount descending and take top 3 items
var list = query.OrderByDescending(t => t.PhoneCount).Take(3).ToList();
Try this:
using (var db = new PRDatabaseEntities())
{
var query = db.Person
.GroupJoin(db.Repairs,
p => p.PID, r => r.PID,
(p, r) => new {
PID = p.PID,
Name = p.Name,
Surname = p.Surname,
Count = r.Count()
};
}
I have a linq query which is working fine.How can i use group by in this query.I need to group by username and itemid and i should get sum(Amount)(All are in table called Carts)
FoodContext db = new FoodContext();
List<CartListing> fd = (from e in db.FoodItems
join o in db.Carts on e.itemid equals o.itemid
where e.itemid == o.itemid
select new CartListing
{
Itemname =e.itemname,
Amount =o.amount,
Price=(float)(e.price*o.amount),
}).ToList();
CartModel vm = new CartModel { CartListings = fd };
I can't see username anywhere in your code example, but to group by Itemname and sum Amount, you would something like:
var grouped = fd.GroupBy(
cl => cl.Itemname,
(key, group) => new CartListing
{
Itemname = key,
Amount = group.Sum(cl => cl.Amount),
Price = group.Sum(cl => cl.Price)
});
To also group by username, just generate a text key containing both values, for instance delimited by a character you know will be contained in neither.
Use:
var grouped = fd.GroupBy((a => new { a.itemid,a.name }) into grp
select new MyClass
{
MyProperty1=grp.key.itemid,
MyProperty2 =grp.Sum(x=>x.whatever)
}
Public MyClass
{
public string MyProperty1 {get;set;}
public int MyProperty2 {get;set;}
}
This way it won't be anonymous
I have a linq query that gets all data from customers and customer contacts.
But sometimes i don't want all contacts so i would like to specify a condition that if this value equals get contacts then run the query. Similar too..
switch (options)
{
case CustomerOptions.DefaultContacts:
break;
}
I currently have this linq query
var customersToReturn = new ContentList<CustomerServiceModel>()
{
Total = customers.Total,
List = customers.List.Select(c => new CustomerServiceModel
{
Id = c.Id,
ContractorId = c.ContractorId,
CompanyName = c.CompanyName,
Active = c.Active,
Address = new Address
{
Address1 = c.Address1,
Address2 = c.Address2,
Address3 = c.Address3,
Address4 = c.Address4,
},
CustomerContacts = c.CustomersContacts.Select(a => new ContactServiceModel
{
Name = a.Name,
Telephone = a.Telephone
}).Where(e => e.IsDefault)
}).ToList()
};
Is there a way I can set a condition or do I need to repeat the process twice one just for customers and one for customers and customer contacts?
If I understand it right, you want some of CustomServiceModel objects to have CustomerContacts, while others to have not? Then I'd do it like that
List = customers.List.Select(c => new CustomerServiceModel
{
Id = c.Id,
ContractorId = c.ContractorId,
CompanyName = c.CompanyName,
Active = c.Active,
Address = new Address
{
Address1 = c.Address1,
Address2 = c.Address2,
Address3 = c.Address3,
Address4 = c.Address4,
},
CustomerContacts = condition ?
c.CustomersContacts.Select(a => new ContactServiceModel
{
Name = a.Name,
Telephone = a.Telephone
}).Where(e => e.IsDefault)
:null
}).ToList()
If you need to use switch, create yourself a method that returns bool and put it instead of condition phrase in above example.
I'm looking for an elegant way of comparing 2 different List<> collections and removing the items in which a specific field value matches. For example:
Customer object
class Customer
{
string CustomerName;
string Email;
}
class Employee
{
string EmployeeName;
string EmployeeID;
}
List<Customer> Customers = new List<Customers(GetCustomers());
List<Employee> Employees = new List<Employees(GetEmployees());
And then I know I can do this to remove from the Employees list the Employees that are in the Customers list by doing like this:
Employees.RemoveAll(e => Customers.Any(c.CustomerName == e.EmployeeName));
What I would like to know is how I can also remove from the Customers list the corresponding Employee? Is it possible to do with a simple statement or do I need to build something out to handle this?
what about:
Employees.Where(e => Customers.Any(c=> c.CustomerName == e.EmployeeName)).ToList()
.ForEach(e=> {
Employees.Remove(e);
Customers.RemoveAll(c => c.CustomerName == e.EmployeeName);
});
Create one list of all customers to be deleted and another of all employees to be deleted first. Then delete all these from your original lists. I am not firm enough with lambda to write this out from memory, but it should be doable.
lets try:
List<Customer> c = new List<Customer>();
c.Add(new Customer() {CustomerName = "test", Email = "email" });
c.Add(new Customer() { CustomerName = "test1", Email = "email1" });
c.Add(new Customer() { CustomerName = "test2", Email = "email2" });
c.Add(new Customer() { CustomerName = "test3", Email = "email3" });
List<Employee> e = new List<Employee>();
e.Add(new Employee() { EmployeeName = "test2", EmployeeID = "1" });
e.Add(new Employee() { EmployeeName = "test1", EmployeeID = "2" });
e.Add(new Employee() { EmployeeName = "test5", EmployeeID = "3" });
e.Add(new Employee() { EmployeeName = "test4", EmployeeID = "4" });
//remove from the Customers list the corresponding Employee
e.RemoveAll(x => c.Any(y => y.CustomerName == x.EmployeeName));
foreach (var employee in e)
{
//TODO:
}
var inBoth = customers.Join(employees, customer => customer.CustomerName, employee => employee.EmployeeName, (customer, employee) => employee.EmployeeName).ToList();
customers.RemoveAll(customer => inBoth.Contains(customer.CustomerName));
employees.RemoveAll(employee => inBoth.Contains(employee.EmployeeName));
It can be done like this:
var toBeDeleted = employees.Select(e => e.EmployeeName)
.Where(name => customers.Any(c => c.CustomerName == name))
.ToList();
customers.RemoveAll(c => toBeDeleted.Contains(c.CustomerName));
employees.RemoveAll(e => toBeDeleted.Contains(e.EmployeeName));
You might be able to use Intersect instead to get the elements that should be deleted. I am unaware of any one line command that can do this.