I have this query witch is working fine :
List<IGrouping<Country, VisitedCity>> tempQuery = null;
using (var db = new MyDataContext())
{
tempQuery = db.VisitedCities.Include(c => c.PersonWhoVisited).Include(c => c.PersonWhoVisitedNationality).Include(c => c.City)
.GroupBy(c => c.PersonWhoVisitedNationality)
.ToList();
}
var dataInput = tempQuery
.OrderBy(c => c.Key.Name)
.Select(cp => new
{
CountryName = cp.Key.Name,
VisitationsByCity = cp
.GroupBy(x => x.City, x => x.CityId)
.Select(c => new
{
City = c.Key,
NumberOfVisits = c.Count()
})
}).ToList();
But the problem is that it is loading all data to my application (i have now 300 000 rows already on my largest table) and its getting slow day by day of course because its loading all in the ToList() method call.
I have this splitted in two calls because i cannot figure out how to make a single call to the database and return only the dataInput, if i merge both calls into one i get "Object reference not set to an instance of an object." exeption, this is probably because some references are not being included, but i cannot figure out what more tables i include in the query...
also im using entity framework 7 which still doesn't support lazy loading and has some features missing still, but this should be possible right?
In the includes i tried using a select statement
.Include(c => c.LadiesInWaiting.Select(b => b.Princess))
like mentioned on here : http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
but is not recognized (because its new entity framework 7 ?)
Update :
Ok if i use the .AsEnumerable i can make all in a single query, but still it seems to take for about 6 seconds to load the data in the next call and also loads 250 mb worth of memory at this instance of time ...
var tempQuery = db.VisitedCities.Include(c => c.PersonWhoVisitedNationality).Include(c => c.City)
.GroupBy(c => c.PersonWhoVisitedNationality)
.AsEnumerable()
.OrderBy(c => c.Key.Name)
.Select(cp => new
{
CountryName = cp.Key.Name,
VisitationsByCity = cp
.GroupBy(x => x.City, x => x.CityId)
.Select(c => new
{
City = c.Key,
NumberOfVisits = c.Count()
})
});
var allCities1 = tempQuery
.SelectMany(x => x.VisitationsByCity.Select(c => c.City))
.Distinct().OrderBy(city => city.Name).ToList();
Ok so i managed (of sort :P) to put all in a single query but i still have to call the .ToList() in order for the next data retrievals to work, if someone has a better solution please let me know .
var dataInput = db.VisitedCities
.GroupBy(c => c.PersonWhoVisitedNationalityId)
.Join(db.Countries, s => s.Key, c => c.CountryId, (s, c) => new { s, c })
.OrderBy(c => c.c.Name)
.Select(cp => new
{
Country = cp.c,
VisitationsByCity = cp.s
.GroupBy(x => x.CityId)
.Join(db.Cities, s => s.Key, c => c.CityId, (s, c) => new { s, c })
.Select(c => new
{
City = c.c,
NumberOfVisits = c.s.Count()
})
}).ToList();
Related
I have a linq query:
var reports = await dbContex.ShoppingListPatientReports
.Where(report => patientIds.Contains(report.PatientId))
.GroupBy(report => new { report.PatientId, RecentDate = DbFunctions.TruncateTime(report.DateCreated) })
.OrderByDescending(g => g.Key)
.ToListAsync();
it returns groups sorted in descending order by the composite key (PatientId, RecentDate).
Keys:
10004, 2021-02-03
10004, 2021-01-01
10004, 2021-02-02
10002, 2021-01-05
10002, 2021-01-06
can I somehow take only the groups with the largest key (PatientId, RecentDate)
i.e groups with most recent dates (In this example, the result should be two groups):
10004, 2021-02-03
10002, 2021-01-06
You need to add a Where after your GroupBy:
.Where(grp =>
grp.OrderByDecending(x => x.Key.RecentDate).First().PatientId == grp.Key.PatientId)
Full query:
var reports = await dbContex.ShoppingListPatientReports
.Where(report => patientIds.Contains(report.PatientId))
.GroupBy(report => new
{
report.PatientId,
RecentDate = DbFunctions.TruncateTime(report.DateCreated)
})
.Where(grp =>
grp.OrderByDecending(x => x.Key.RecentDate).First().PatientId == grp.Key.PatientId)
.OrderByDescending(g => g.Key)
.ToListAsync();
Maybe It will be useful for you. It works with list of elements, but I've never tried it with EF.
var reports = new List<ShopingPatientReport>() { ... }
.Where(report => patientIds.Contains(report.PatientId))
.GroupBy(report => report.PatientId)
.Select(report => report.GroupBy(x => DbFunctions.TruncateTime(report.DateCreated))
.OrderByDescending(f => f.Key).First()).OrderByDescending(g => g.Key)
.ToList()
Here is a query that works for me:
await dbContex.ShoppingListPatientReports
.Where(report => patientIds.Contains(report.PatientId))
.GroupBy(report => report.PatientId)
.Select(patientShoppingList =>
new
{
patientId = patientShoppingList.Key,
//Grouping reports by created date and take group wiht the most recent date
reports = patientShoppingList.GroupBy(report => DbFunctions.TruncateTime(report.DateCreated))
.OrderByDescending(group => group.Key)
.FirstOrDefault()
})
.SelectMany(g => g.reports.Select(r => r))
.ToListAsync();
I have the following select:
var sortedCodes = Codes
.Where(c => c.Active)
.OrderByDescending(x => x.SortOrder)
.Select(b => new { b.Display, b.NumericCode, b.SortOrder })
.Distinct()
.ToList();
The table Code has many columns such as NumericCode, TextCode, AlphaCode, ThreeCode, FourCode, FiveCode. I am using this table to build selects in my UI. Is there a way I can create some dynamic code so I can pass in the column name to use for the value?
The above select would look like this:
.Select(b => new { b.Display, "TextCode", b.SortOrder })
I was thinking I could use an Expression, but I do not think this is exactly what I need as it is actually printing the lambda as the value.
var arg = Expression.Parameter(typeof(Code), "b");
var body = Expression.Property(arg, valueColumn);
var lambda = Expression.Lambda<Func<Code, string>>(body, arg);
var sortedCodes = Codes
.Where(c => c.Active)
.OrderByDescending(x => x.SortOrder)
.Select(b => new { b.Display, Value=lambda, b.SortOrder })
.Distinct()
.ToList();
I'm trying to get Ingredients through junction table for my Recipes.
_context.RecipeIngredients
.Include(rI => rI.Recipe)
.ThenInclude(r => r.RecipeIngredients)
.Where(rI => ingredients.Contains(rI.IngredientId))
.GroupBy(rI => rI.Recipe)
.Select(g => new
{
Recipe = g.Key,
MatchingIngredients = (double)g.Count() / (double)g.Key.RecipeIngredients.Count(),
g.Key.ComplexityTag,
g.Key.TypeTag,
g.Key.RecipeIngredients,
})
.OrderByDescending(r => r.MatchingIngredients)
.Take(MaxAmountBestRecipes)
.AsEnumerable()
.Select(a => new RecipeDTO()
{
Title = a.Recipe.Title,
Steps = a.Recipe.Steps,
ComplexityTag = a.ComplexityTag?.Complexity,
TypeTag = a.TypeTag?.Type,
IngredientAmount = a.RecipeIngredients?.ToDictionary(rI => rI.Ingredient.Name, rI => rI.Quantity),
})
.ToList();
I discovered it is caused by g.Key.RecipeIngredients, but I can't find any workaround, solution for this problem. I tried eagar loading(as you can see), and lazy loading, both didn't work. I hope there is solution in one query to db in linq. Moreover, will it work like in the above line, after update.:
IngredientAmount = a.RecipeIngredients?.ToDictionary(rI => rI.Ingredient.Name, rI => rI.Quantity)
EDIT
I have divied linq query and here you have after which statement ArgumentNullException is thrown:
var tmp = _context.RecipeIngredients
.Where(rI => ingredients.Contains(rI.IngredientId))
.GroupBy(rI => rI.Recipe)
.Select(g => new
{
Recipe = g.Key,
MatchingIngredients = (double)g.Count() / (double)g.Key.RecipeIngredients.Count(),
g.Key.ComplexityTag,
g.Key.TypeTag,
g.Key.RecipeIngredients
})
.ToList();
Include doesn't work when you change the shape of the query (e.g. when you're querying for RecipeIngredients but are projecting to another type).
The only place I'm thinking a NULL is a problem is the key selector when creating the dictionary. Since Include won't do anything for you, ri.Ingredient will always be NULL. Include the Ingredients in the original projection (and remove the Include since it's useless):
_context.RecipeIngredients
.Where( rI => ingredients.Contains( rI.IngredientId ) )
.GroupBy( rI => rI.Recipe )
.Select( g => new
{
Recipe = g.Key,
MatchingIngredientCount = (double)g.Count() / (double)g.Key.RecipeIngredients.Count(),
g.Key.ComplexityTag,
g.Key.TypeTag,
g.Key.RecipeIngredients,
// this eager-loads the `Ingredient` entities
// EF will automatically wire them up to the `RecipeIngredient` entities
// if tracking is enabled
Ingredients = g.Key.RecipeIngredients.Select( ri => ri.Ingredient ),
} )
.OrderByDescending(r => r.MatchingIngredients)
.Take(MaxAmountBestRecipes)
.ToArray()
.Select( a = new ...
{
...
IngredientAmount = a.RecipeIngredients.ToDictionary(
ri => ri.Ingredient.Name, // ri.Ingredient should now not be NULL
ri => ri.Quantity )
} );
Edit: if you don't need the entire RecipeIngredient or Recipe entities in your results, just project what you need in the orig name with the RecipeIngredients in the first projection:
IngredientNamesAndQuantity = g.Key.RecipeIngredients.Select( ri => new
{
ri.Quantity,
ri.Ingredient.Name,
}
Then use that projection to build your dictionary:
IngredientAmount = a.IngredientNamesAndQuantity.ToDictionary(
at => at.Name,
at => at.Quantity )
This is an extension of already answered question by Jon Skeet that you can find here.
The desired result is following:
A 100
A 80
B 80
B 50
B 40
C 70
C 30
considering you have following class:
public class Student
{
public string Name { get; set; }
public int Grade { get; set; }
}
to get to the result (in ideal scenario) can be done with Jon Skeet's answer:
var query = grades.GroupBy(student => student.Name)
.Select(group =>
new { Name = group.Key,
Students = group.OrderByDescending(x => x.Grade) })
.OrderBy(group => group.Students.FirstOrDefault().Grade);
However in my case I have to support paging in my query as well. This means performing SelectMany() and then do Skip() and Take(). But to do Skip() you have to apply OrderBy(). This is where my ordering breaks again as I need to preserve the order I get after SelectMany().
How to achieve this?
var query = grades.GroupBy(student => student.Name)
.Select(group =>
new { Name = group.Key,
Students = group.OrderByDescending(x => x.Grade) })
.OrderBy(group => group.Students.FirstOrDefault().Grade).SelectMany(s => s.Students).OrderBy(something magical that doesn't break ordering).Skip(s => skip).Take(t => take);
I know I could manually sort again the records when my query is materialised but I would like to avoid this and do all of it in one SQL query that is translated from LINQ.
You can take another approach using Max instead of ordering each group and taking the first value. After that you can order by max grade, name (in case two students have the same max grade) and grade:
var query = c.Customers
.GroupBy(s => s.Name, (k, g) => g
.Select(s => new { MaxGrade = g.Max(s2 => s2.Grade), Student = s }))
.SelectMany(s => s)
.OrderBy(s => s.MaxGrade)
.ThenBy(s => s.Student.Name)
.ThenByDescending(s => s.Student.Grade)
.Select(s => s.Student)
.Skip(toSkip)
.Take(toTake)
.ToList();
All these methods are supported by EF6 so you should get your desired result.
Just re-index your list results and remove the index before returning.
var query = grades.GroupBy(student => student.Name)
.Select(group =>
new { Name = group.Key,
Students = group.OrderByDescending(x => x.Grade)
})
.OrderBy(group => group.Students.FirstOrDefault().Grade)
.SelectMany(s => s.Students)
.Select((obj,index) => new {obj,index})
.OrderBy(newindex => newindex.index)
.Skip(s => skip).Take(t => take)
.Select(final=> final.obj);
I am using blow code for join in nhibernate, which is working fine. But i don't wants to use .List on both queries before join, i wants to use .List after join. I don't know nhibernate too much.. please provide me help that what i should make changes in below funciton to first join the data and then apply .List over it.
public IEnumerable<PGrp> GetSol()
{
_pGrpR = null;
_pGrp = null;
QueryOver<Phy, Phy> activePhyQuery = GetDataQuery();
var phyGrpR = _session.QueryOver(() => _pGrpR)
.Where(
Subqueries.WhereProperty<PGrpR>(p => _pGrpR.PhyId).In(
activePhyQuery))
.List<PGrpR>();
IList<PGrp> pGrps = _session.QueryOver(() => _pGrp)
.Where(x => !x.AC)
.List<PGrp>();
var newPGrps = pGrps
.Join(
pGrpR,
p => p.Id,
x => x.PGrpId,
(p, x) => p
).Distinct().OrderBy(x => x.Name);
return newPGrps;
}
Thanks
PGrpR pGrpR = null;
IList<PGrp> pGrps = _session.QueryOver(() => _pGrp)
.Where(x => !x.AC)
.JoinAlias(pgrps => pgrps.pGrpR, () => pGrpR) // Set real property name
.OrderBy(() => pGrpR.Name).Asc
.Select(Projections.Distinct(Projections.Property(() => pGrpR."PropertyForDistinct")))
.TransformUsing(Transformers.DistinctRootEntity)
.List<PGrp>();