var countries= ctx.Country
.Include("cities") // I want to take only 10 cities. How to take top 10 cities and city name starts from "A"
.Include("Schools")
.Where(x => (x.CountryID == 100))
.ToList();
1 - Top 10 ciites
2 - Where criteria on CityName field
I am using Entity Framework 6
Use something like this:
var countries = ctx.Country.Select( c => new {
Country = c,
Cities = c.Cities.Where(ci = > ci.CityName.ToLower().Startwith("A".ToLower())).Take(10),
Schools = Cities.select(ci => ci.Schools)
}).Where(x => x.CountryID == 100).ToList();
I didn't test it, maybe you will get some compile errors, cuz i don't know how you named your classes.
Let me know if you need any clarification or have any question
Set up the navigation property relationships between country, school, and city then select a structure based on the data you want to receive into an anonymous type and let EF handle the query composition.
var countryData = ctx.Countries
.Include(x => x.Schools)
.Where(x => x.CountryID == 100)
.Select(x => new { Country = x, Cities = x.Cities.OrderBy(c => c.CityName).Take(10).ToList() })
.ToList(); // This likely only returns 1 row due to the CountryId Where Clause...
This will give you a structure containing the Country reference and the list of up to 10 cities associated to each country.
If you access the Cities collection on a Country object in the results you will still lazy-load all cities, but the .Cities collection returned in the above statement would be the 10 you care about.
If there are a lot of cities in a country and loading this complete set is potentially expensive then you may want to consider leaving the entities disconnected so rather than having a Cities collection associated to a country, treat cities as a top-level entity that happens to have a relationship to country. (I.e. City mapping .HasRequired(x=> Country).WithMany() rather than mapping a .HasMany(x=> x.Cities).WithRequired(x=>x.Country) on the country.)
This would change the query somewhat if you want more than one country, by using a GroupBy expression, though it'd only return countries that had at least one city based on the search criteria.
Related
I was trying to select data using LINQ
and I have a list called "products" and I want just these items that exist in products list
var Owner = db.Owners
.Where(m => m.ID == id)
.Include(m => m.Products.Where(item1 => products.Any(item2 => item2.ProductID == item1.ProductID)).ToList())
.FirstOrDefault();
but I'm getting this error :
System.ArgumentException: 'The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parameter name: path'
Include is meant to fetch complete rows of a table, inclusive primary and foreign keys.
Usually it is not efficient to fetch the complete rows of a table. For example, suppose you have a database with Schools and Students. There is a one-to-many relation between Schools and Students: every School has zero or more Students, every Student attends exactly one School, namely the School that the foreign key refers to.
If you fetch School [10] with its 2000 Students, then every Student will have a foreign key SchoolId with a value 10. If you use Include and fetch complete Student rows you will transfer this value 10 over 2000 times. What a waste of processing power!
A DbContext has a ChangeTracker object. Whenever you fetch data without using Select, so if you fetch complete rows, then the fetched rows are stored in the ChangeTracker, together with a Clone of it. You get a reference to the Clone (or the original, doesn't matter). When you change properties of the fetched data, you change the value in the Clone. When you call SaveChanges, the values of all properties of all originals in the ChangeTracker are compared with the values in the Clones. The items that are changed are updated in the database.
So if you fetch School [10] with its 2000 Students, you are not only fetching way more data than you will ever use, but you will also store all these Students in the ChangeTracker together with a Cloned Student. If you call SaveChanges for something completely different (change of the telephone number of the School for instance), then all Students are compared by value property by property with their Clones.
Generic rule:
Whenever you fetch data using Entity Framework, always use Select, and Select only the properties that you actually plan to use. Only fetch complete rows and only use Include if you plan to update the fetched data.
Using Select will also solve your problem:
int ownerId = ...
IEnumerable<Product> products = ...
var Owner = db.Owners.Where(owner => owner.ID == ownerId)
.Select(owner => new
{
// Select only the Owner properties that you actually plan to use
Id = owner.Id,
Name = owner.Name,
// get the Products of this Owner that are in variable products
Products = owner.Products
.Where(product => products.Any(p => p.ProductId == product.ProductId)
.Select(product => new
{
// Select only the Product properties that you plan to use
Id = product.Id,
Price = product.Price,
...
// No need to fetch the foreign key, you already fetched the value
// OwnerId = product.OwnerId,
})
.ToList(),
...
})
.FirstOrDefault();
I used automatic types (new {...}). If you really want to create Owner and Properties, use:
var Owner = db.Owners.Where(...)
.Select(owner => new Owner
{
Id = owner.Id,
...
Products = owner.Products.Where(...).Select(product => new Product
{
Id = product.Id,
...
})
.ToList(),
})
.FirstOrDefault();
Try the following:
var productIds = products.Select(x => x.ProductID);
var Owner = db.Owners
.Where(m => m.ID == id)
.Include(m => m.Products.Where(product => productIds.Contains(product.ProductID))
.FirstOrDefault();
When I am accessing all cities my code is like this.
public IQueryable<City> GetAll()
{
var result = from s in this.Context.Cities.Include("States.Countries") select s;
return result;
}
This is working fine and including states and countires. I want to get cities by Country Id, below is my code. In the below code, I want to include States.Countires for each city. How can i do this ?
public IEnumerable<City> GetByCountriesId(int Id)
{
var result = from s in this.Context.Countries
join a in this.Context.States on s.Id equals a.Country_Id
join b in this.Context.Cities on a.Id equals b.States_Id
where s.Id == Id
select b;
return result;
}
public IEnumerable<City> GetByCountriesId(int id)
{
return from country in this.Context.Countries
where country.Id == id
from state in country.States
from c in this.Context.Cities.Include(c => c.States.Select(s => s.Countries))
where c.States.Any(s => s == state)
select c;
}
or, even better:
public IEnumerable<City> GetByCountryId(int id)
{
return from c in this.Context.Cities
.Include(c => c.States.Select(s => s.Countries))
where c.States.Any(s => s.Countries.Any(c => c.Id == id))
select c;
}
However – while it's clear why Country has a States collection and State has a Cities collection – why does your City have a States collection and your State have a Countries collection? Shouldn't these be State and Country properties, respectively?
Assuming your City really does have a single State, and your State has a single Country, this simplifies it a lot:
return from c in this.Context.Cities
.Include(c => c.State.Select(s => s.Country))
where c.State.Country.Id == id
select c;
Are you sure a city could belong to several states? IMHO you should have a one to many relationship, where an State could have several Cities and a City should belong to one State. The same happens with State and Country. I think you have pluralized those nav. property names (States in City and Cities in Country) but there are not collections. In case you have those two one to many relationships in the same way that I describe above, you can write a query as I show as follow to achieve what you need:
var result = this.Context.Cities.Include(c=>c.State.Country).Where(c=>c.State.Country.Id==Id‌​);
Is better use the DbExtensions.Include extension method because is strongly typed.
Now, maybe you can think this query could end with a NullReferenceException due to the c.State.Country.Id expression in case one of those nav. properties could be null.But that is not going to happen because you need to set those navigation properties (or the FK properties in case that already exist in DB) when you need to save a new City or State in DB, in other word, they are required.
If you use Fluent Api to configure those relationships you will end with something like this:
modelBuilder.Entity<City>().HasRequired(c=>c.State).WithMany(s=>s.Cities).HasForeignKey(c=>c.State_Id);
modelBuilder.Entity<State>().HasRequired(s=>s.Country).WithMany(c=>c.States).HasForeignKey(s=>s.Country_Id);
I have two objects that are linked, States and Cities, so each State has his Cities and each Citie is linked to an State. I also have some Units that have stateID and citieID but they are not linked since i have them only in Json.
What i need is to get only the States and Cities that have Units. I managed to get the first two but was wondering if there was any faster way to do it since i will have to make an update on those datas everyday:
//unitsData have a List of Units objects, this only have stateID, citieID and the unit data
var unitsData = objUnidade.BuscaUnidades();
//unitsState have all units grouped by State, here i also only have stateID and citieID, same data as above
var unitsState = unitsData.GroupBy(x => x.codigoEstado);
//Here is where i make my search inside the unidadesEstados and select only the Estados that i need
var activeStates = unitsState.Select(est => db.States.FirstOrDefault(x => x.ID == est.Key)).Where(state => state != null).ToList();
To do the Cities search i'm doing the same but using an extra foreach, is there a way to make this better ?
You are querying the database multiple times. It's better to use a SELECT ... IN query, which in LINQ looks like:
var units = objUnidad.BuscaUnidades();
var stateIds = units.Select(u => u.codigoEstado).ToList();
var activeStates = db.States.Where(s => stateIds.Contains(s.Id)).ToList();
EDIT: you asked about cities as well. It's more of the same:
var cityIds = units.Select(u => u.codigoCuidad).ToList()
var activeCities = db.Cities.Where(c => cityIds.Contains(c.Id)).ToList();
This solution gives you every city whose ID is referred to by a unit. #StriplingWarrior 's solution will give you every city in (the states that have a unit).
If db.States queries the database, then for each group in unitsState the query will get executed. If the number of states isn't extremely large, you can store them in a list.
var dbStates = db.States.ToList();
var activeStates = unitsState.Select(est => dbStates.FirstOrDefault(x => x.ID == est.Key)).Where(state => state != null).ToList();
var bndlSummary = GetBundleSummary(GroupIds);
var cntrSummary = GetContainerSummary(GroupIds);
var finalSummary = GetFinalSummary(GroupIds);
Above var are fetching some data from Database. They all have one Common Field Name "City".
City value can be repeated many time like City = Chicago can be 3 times or more). now I want this Field City value into allCityNames. I don't want City Info to be repeated from any var.
var allCityNames = new cityAnalysisSummary();
Please help me how how should i do it. Thank you very much for your help.
bndlSummary.Select(b => b.City)
.Concat(cntrSummary.Select(c => c.City))
.Concat(finalSummary.Select(f => f.City))
.Distinct();
Use Select to get all the cities from each collection, Concat to put them all together, and Distinct to remove any duplicates.
You can also use Union which will remove duplicates while concatenating:
bndlSummary.Select(b => b.City)
.Union(cntrSummary.Select(c => c.City))
.Union(finalSummary.Select(f => f.City));
I have a list of objects and need to get the list of records from this list. like I have of Countries and I need to get the list of countries which are in between country with name "Australia" and country "Indonasia", the list will not be sorted.
Am using c#.
I tried to use something like, get the index of first and second and then use that to get the list with a for loop, but would be handy if it can be done in single query.
If you do the following:
var elementsBetween = allElements
.SkipWhile(c => c.Name != "Australia")
.Skip(1) // otherwise we'd get Australia too
.TakeWhile(c => c.Name != "Indonasia");
you'll get the result you want without iterating through the list 3 times.
(This is assuming your countries are e.g. Country items with a Name string property.)
Note that this doesn't sort the countries at all - it's unclear from your question whether you want this or not but it's trivial to add an OrderBy before the SkipWhile.
this should do the job
var query = data.SkipWhile(x => x != "Australia").TakeWhile(x => x != "Indonesia")