How to include related objects in grouped Entity LINQ queries? - linq

I have some Setting entities that are related to a SettingDescription which is related to a SettingGroup.
Setting history is preserved by making a "Modified" field part of the key.
To get the settings matching a specific category I use this query (after help from here):
var latestSettings =
context.Settings.Include("Description.SettingGroup")
.OrderByDescending(x => x.Modified)
.GroupBy(x =>
new {
x.Category,
x.Group,
x.Name,
x.Target }, x => x)
.Where(x => x.Key.Category == category)
.Select(result => result.FirstOrDefault())
.ToArray();
This returns a set of the latest settings, but the "Include" part is completely ignored. However, I can force load the descriptions by running a second dummy query that loads the descriptions into the context.
var latestSettings =
context.Settings.Include("Description.SettingGroup")
.OrderByDescending(x => x.Modified)
.GroupBy(x =>
new {
x.Category,
x.Group,
x.Name,
x.Target }, x => x)
.Where(x => x.Key.Category == category)
.Select(result => result.FirstOrDefault())
.ToArray();
var settingDescriptions =
context.SettingDescriptions.Include("SettingGroup")
.Where(x => x.Category == category)
.ToArray();
Why is the include ignored in the "stand alone" group query?
Can I combine the setting and description loading into a single query?

AlexJ from the EF team posted an excellent series of tips, including:
"Tip 22 - How to make Include really Include"
http://blogs.msdn.com/b/alexj/archive/2009/06/02/tip-22-how-to-make-include-really-include.aspx
It looks to me like your query is returning "Settings" entities (without a "change of shape") so this tip should apply.

Related

How to update context in EF Core without ForEach?

I am getting some data from my database:
var productCollections = _context.ProductCollections
.Include(pc => pc.Products)
.ThenInclude(p => p.ProductDetails)
.FirstOrDefault(x => x.Id == productCollectionId);
With the products in the collection I am getting some product-details from a different backend and safe that data in a List<> containing the product-number and the details. I want to add these details to a List<> named ProductDetails in the products.
productCollection.Products.ForEach(p => p.ProductDetails.AddRange(
foundProductDetailsList
.Where(d => d.RequestedProductNumber == p.ProductNumber)
.Select(x => new StaticPartBasketItemFound
{
DetailX = x.DetailX,
DetailY = x.DetailY
})));
Both may contain 100.000 items and it's pretty slow to perform the code above.
Is there any chance to "join-update" the "ProductDetails" within my "productCollection.Products" or similar fast methods?
I am using .NET 5.0.3 and EF-Core.

could not resolve property (complex properties)

I have a asp.net mvc application with NHibernate and I do not know how to resolve a problem to query some data. I have this query:
// create query
var query = session.QueryOVer<Laudo>().Fetch(x => x.Equipament).Eager;
// add some filters
if (idEquipament.HasValue)
query = query.And(x => x.Equipament.Id == idEquipament.Value);
//I got the error here...
if (idCompany.HasValue)
query = query.And(x => x.Equipament.Company.Id == idCompany.Value);
When I try to execute this query, I've got an exception with this message:
"could not resolve property: Equipament.Company.Id of: DomainModel.Laudo"
what can I do to fix this problem?
Thanks
You cannot use another entity property like that. NHibernate expects expression that can be evaluated to property of the current entity. You need to use JoinQueryOver or JoinAlias to join another entity, and perform where after that.
With JoinQueryOver:
// ...
query = query.JoinQueryOver(x => x.Equipment)
.JoinQueryOver(x => x.Company)
.Where(c => c.Id == idCompany.Value);
With JoinAlias:
Equipment equipment = null;
Company company = null;
// ...
query = query.JoinAlias(x => x.Equipment, () => equipment)
.JoinAlias(() => equipment.Company, () => company)
.Where(() => company.Id == idCompany.Value);
Some more info:
What is the difference between JoinQueryOver and JoinAlias?
What can be used as a NHibernate QueryOver alias?
Complex nHibernate QueryOver expression
The tags chosen for your question make me think you didn't want to use QueryOver, but LINQ.
This is achieved by using the extension method Query, in the NHibernate.Linq namespace:
var query = session.Query<Laudo>().Fetch(x => x.Equipament);
if (idEquipament.HasValue)
query = query.Where(x => x.Equipament.Id == idEquipament.Value);
if (idCompany.HasValue)
query = query.Where(x => x.Equipament.Company.Id == idCompany.Value);

NHibernate Linq Group By fails to group properly in SQL Server

I have the following LINQ query which uses NHibernate against a SQL Server backed repository...
var casesByCaseOwner = this.preGrantDetailRepository.All
.Where(x => x.CaseFileLocation.Id == cflId)
.GroupBy(x => x.CaseOwner)
.Select(x => new StagSummaryForCfItem
{
Id = x.Key.Id,
Description = x.Key.Name,
NumberOfCases = x.Count(),
UninvoicedNetFee = x.Sum(y => y.UninvoicedNetFee),
UninvoicedDisbursement = x.Sum(y => y.UninvoicedDisbursement)
}).AsEnumerable();
However, it complains that SQL Server is unable to group by the CaseOwner.Name column because it is not contained in the select list or group clause. Coming from a database world I understand that error, however, I'm not sure how to force NHibernate to group by both Id and Name but still have the CaseOwner entity available to me in my Select.
I found the answer finally...
var casesByCaseOwner = this.preGrantDetailRepository.All
.Where(x => x.CaseFileLocation.Id == cflId)
.GroupBy(x => new { x.CaseOwner.Id, x.CaseOwner.Name })
.Select(x => new StagSummaryForCfItem
{
Id = x.Key.Id,
Description = x.Key.Name,
NumberOfCases = x.Count(),
UninvoicedNetFee = x.Sum(y => y.UninvoicedNetFee),
UninvoicedDisbursement = x.Sum(y => y.UninvoicedDisbursement)
}).AsEnumerable();
return casesByCaseOwner;
This works nicley, it turns out I need to project a new entity with the properties I want to group on.

NHibernate: QueryOver<> help

I'm just starting out with NHibernate and I'm having trouble with running more complex queries.
I have entities with a list of tags attached. The user will provide two lists of tags, include and exclude.
I need to find all the entities that have all of the include tags, and exclude any entites that have any tag in the exclude list.
Below is my first effort- which is clearly wrong as its listing all Display objects that have any of the include tags rather than all!
Any assistance is greatly appeciated.
var includeTagIds = (from tag in regime.IncludeTags select tag.Id).ToList<int>();
var excludeTagIds = from tag in regime.ExcludeTags select tag.Id;
var displays = session.QueryOver<Display>()
.JoinQueryOver<DisplayTag>(display => display.Tags)
.WhereRestrictionOn(tag => tag.Id)
.IsIn(includeTagIds).List().Distinct();
return displays.ToList();
That query isn't trivial (have a think about how you might do this using raw SQL). I think the following will work (requiring two correlated sub-queries):
Display displayAlias = null;
var countIncludedTagsSubquery =
QueryOver.Of<Display>()
.Where(d => d.Id == displayAlias.Id)
.JoinQueryOver<DisplayTag>(d => d.Tags)
.WhereRestrictionOn(t => t.Id).IsInG(includedTagIds)
.Select(Projections.RowCount());
var excludedTagsSubquery =
QueryOver.Of<Display>()
.Where(d => d.Id == displayAlias.Id)
.JoinQueryOver<DisplayTag>(d => d.Tags)
.WhereRestrictionOn(t => t.Id).IsInG(excludedTagIds)
.Select(t => t.Id);
var displays =
session.QueryOver<Display>(() => displayAlias)
.WithSubquery.WhereValue(includedTagIds.Count).Eq(countIncludedTagsSubquery)
.WithSubquery.WhereNotExists(excludedTagsSubquery)
.List();

LINQ distinct on class item?

Note this question is similar this one except I'm not working with linq-to-sql, so the "let" is not usable.
Basically I have a select of the type
... .Select(c => new SampleClass { Id = c.Location.Name, Name = c.Location.Name }).Distinct().ToList()
which used to work when I just had
... .Select(c => c.Location.Name).Distinct().ToList()
How would I make a distinct call on one of the items within the SampleClass?
You can group items by the key, and then select what item from the group you want to use as value. I use FirstOrDefault as an example:
... .Select(c => new SampleClass { Id = c.Location.Name, Name = c.Location.Name })
.GroupBy(c => c.Id)
.Select(group => group.FirstOrDefault())
.ToList()
Is this what you need: http://sprokhorenko.blogspot.com/2009/11/convenient-distinct.html ?
This is an extension for IEnumerable that allows you to .Distinct() for any field (or even several ones using lambdas), which creates IEqualityComparer for you on the fly.

Resources