NHibernate: QueryOver<> help - linq

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();

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.

LINQ to entity, sort the subset of returned values

var myView = await _context.foo
.Include(p => p.subFoos)
.SingleOrDefaultAsync(m => m.FooID == id);
This code returns a row from the database and the linked sub rows held in the collection subFoos, what I'd like to do is order the collection rather than have them displayed in table order. I've got a field OrderBy but I can't figure out how to use it.
var myView = await _context.foo
.Include(p => p.subFoos)
.OrderBy(x => x.OrderBy)
.SingleOrDefaultAsync(m => m.FooID == id);
This orders by the top level row, rather than the collection. How do I apply OrderBy to the collection.
Thanks
Couldn't get any of the above to work so just retrieved the data using
var myView = await _context.foo
.Include(p => p.subFoos)
.SingleOrDefaultAsync(m => m.FooID == id);
then ordered the myView.subFoos by converting them to a List
myView.subFoos = myView.subFoos.ToList().OrderBy(x => x.OrderBy).ToList();
Only one database call, works nicely.

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);

Selecting related pairs LINQ

I'm using LINQ to manipulate a datatable. I have 3 columns - I would like group by one and then select the remaining 2 columns together. At the moment I have something like this
var query = reportDataTable.AsEnumerable()
.GroupBy(c => c["Code"])
.Select(g =>
new {
Code = g.Key,
Rank = g.Select(f => new
{ f["rank"],
f["Name"]}).ToArray()
});
but I get issues due to anonymous types. I know this syntax would work if I could reference the the column headers directly (in say a list or w/e). How can I get around this with DataTables? Cheers.
Edit:
Well I'd like to be able to reference the fields later when I come to populate the data into a different datatable:
foreach (var q in query)
{
DataRow df = dp.NewRow();
df["Code"] = q.Code;
foreach (var rank in q.Rank)
{
df[rank.name] = rank.rank;
}
dp.Rows.Add(df);
}
define your Rank fields, Also if you have a class for it, call related class constructor,
you can see this in bellow code, before ToArray.
var query = reportDataTable.AsEnumerable()
.GroupBy(c => c["Code"])
.Select(g =>
new { Code = g.Key, Rank =
g.Select(f => new { rank = f["rank"], name = f["Name"]})
.ToArray() });

How to include related objects in grouped Entity LINQ queries?

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.

Resources