QueryOver, GroupBy and Subselect - nhibernate

how can I do this with queryover and a subquery?
FakturaObjektGutschrift fog = null;
int[] idS = Session.QueryOver(() => fog)
.SelectList( list => list
.SelectMax(() => fog.Id)
.SelectGroup(() => fog.SammelKontierung)
).List<object[]>().Select(x => (int)x[0]).ToArray();
And using the ids in another query
IQueryOver<Beleg, Beleg> sdfsd = Session.QueryOver(() => bGut)
.AndRestrictionOn(() => fog.Id).IsIn(idS); //iDs is a list of int.
I would like do this with a subquery because there is a limitation on the number of parameters for a SQL query. How do I do this?
How do I write the first query, but without selecting the SelectGroup()? This is exactly where I got stuck.

Group by without projection in QueryOver API is currently not supported link.
You can use LINQ to create the projection in a subquery:
var subquery = Session.Query<FakturaObjektGutschrift>()
.GroupBy(x => x.SammelKontierung)
.Select(x => x.Max(y => y.Id));
var query = Session.Query<Beleg>()
.Where(x => subquery.Contains(x.Id));
If you really needs QueryOver to create more complex queries check this solution link.

Related

Nhibernate Restrictions.In Error

Finally tracked down my error which is a result of the query. I have an nhibernate query using a Restrictions.In. Problem is once query executes if no results returned query throws error immediately. Is there another restriction that I can use. I know if I was writing a linq query I could use the .Any to return bool value and go from there is there something similar I can do in this instance?
carMake is passed in
myQuery.JoinQueryOver(x => x.Car)
.Where(Restrictions.In("VIN",
Trades.Where(x => x.Car.Make.ToLower() == carMake.ToLower())
.Select(x => x.Car.PrimaryVIN)
.ToList()));
Assuming that Trades is a list of objects you can use .WhereRestrictionOn() instead. Try this (I split the code for better readability):
var vinsWithCarMake = Trades
.Where(x => x.Car.Make.ToLower() == carMake.ToLower())
.Select(x => x.Car.PrimaryVIN)
.ToList<string>();
var carAlias = null;
var result = myQuery.JoinAlias(x => x.Car, () => carAlias)
.WhereRestrictionOn(() => carAlias.VIN).IsInG<string>(vinsWithCarMake)
.List();

NHibernate QueryOver Where with internal select

I have a problem with translating this SQL into QueryOver notation. Can you help me?
SELECT * FROM Alerts a
WHERE a.CreatedOn = (
SELECT MAX(b.CreatedOn) FROM Alerts b WHERE b.UserFk=a.UserFk);
I try to select last alert for every user. I use CreatedOn and cannot use Id.
I have so far:
session.QueryOver(() => alertAlias)
.SelectList(list => list
.Select(() => alertAlias.User.Id)
.SelectSubQuery(
QueryOver.Of<Alerts>()
.Where(x => x.User.Id == alertAlias.User.Id)
.OrderBy(x => x.CreatedOn).Desc
.Select(x => x.CreatedOn)
.Take(1)));
I know it adds user's last alert date to every user's alert row. But I want to have only last alerts.
Your attempt is using subquery inside of a SELECT statement. But we need to move it into WHERE. This should be the way:
// this is a subquery (SELECT ....
var subquery = QueryOver.Of<Alerts>()
.Where(x => x.User.Id == alertAlias.User.Id)
.OrderBy(x => x.CreatedOn).Desc
.Select(x => x.CreatedOn)
.Take(1)));
// main Query could now have or do not have that subquery selected
var query = session.QueryOver(() => alertAlias)
.SelectList(list => list
.Select(() => alertAlias.User.Id)
// could be there
.SelectSubQuery(subquery)
)
// but mostly here we do use WHERE clause
.WithSubquery
.WhereProperty(() => alertAlias.CreatedOn)
.Eq(subquery)
;
// such a query could be returned as a list of arrays
var results = query.List<object[]>();
We can also use some Result Transformer, but this is another story...

Why do these two Fluent nHibernate queries produce different results?

I've been trying to work out why this query asking for a manager and his or her team only returns the first entry for the Team collection. Apparently, it's because I had FirstOrDefault at the end of the query. I was under the impression the FirstOrDefault would apply to the query as a whole, but it seems it's being applied to the Team collection as well.
Original query (shows only 1st member in Team):
session.Query<IEmployee>()
.Where(p => p.PersonalNumber == PersonalNumber)
.Fetch(p => p.Team)
.Fetch(p => p.Manager)
.FirstOrDefault();
New query which returns full team:
session.Query<IEmployee>()
.Where(p => p.PersonalNumber == PersonalNumber)
.Fetch(p => p.Team)
.Fetch(p => p.Manager)
.ToList().FirstOrDefault();
What would be the correct way to formulate this query? My need for a workaround implies I'm not doing this properly.
Background - mappings:
This is a basic hierarchical relationship with Manager being an IEmployee and Team being an IList of IEmployee.
References(x => x.Manager).Column("ManagerId");
HasMany(x => x.Team)
.AsList(index => index.Column("TeamIndex"))
.KeyColumn("ManagerId");
session.Query<IEmployee>()
.Where(p => p.PersonalNumber == PersonalNumber)
.Fetch(p => p.Team)
.Fetch(p => p.Manager)
.FirstOrDefault();
In this query the FirstOrDefault works on the database as you expected.
session.Query<IEmployee>()
.Where(p => p.PersonalNumber == PersonalNumber)
.Fetch(p => p.Team)
.Fetch(p => p.Manager)
.ToList().FirstOrDefault();
In this query the ToList works on the database. All behind works on the result of the ToList. So the FirstOrDefault gets the FirstOrDefault from the result set of the ToList. To get the same result you will need to add a order to your query. Sql does not grantee the same order of the result set when you do a select without order. The order in the result of ToList is different then the internal order in the first query.
I have been struggling with the same problem. For me, this occurred when upgrading from NH 3.1 -> 3.3. The problem is that with Linq, NHibernate 3.3 generates a SQL query that has a "Top(1)" statement in it, effectively killing the "Fetch"-part of the query. I solved it by switching from Linq to QueryOver. I believe this will work:
session.QueryOver<IEmployee>()
.Where(p => p.PersonalNumber == PersonalNumber)
.Fetch(p => p.Team)
.Fetch(p => p.Manager)
.SingleOrDefault();
The problem is that I am asking for a Cartesian product (using Fetch) but also using FirstOrDefault; from the generated SQL I can see that that combination doesn't work, as you only get the first row of the Cartesian product (SQL generated: “FETCH NEXT 1 ROWS ONLY”).
I will need to write a different type of query if I want to do this, or else just use the ToList workaround which in this instance isn't doing much harm as I'm only expecting one result from the database anyway.
Example solution:
session.QueryOver<IEmployee>()
.Where(p => p.PersonalNumber == PersonalNumber)
.Fetch(p => p.Team).Eager
.Fetch(p => p.Manager).Eager
.SingleOrDefault();

NHibernate multi table query returning same row more than once

The following nhibernate query is causing me issues because it returns the same row more that once as the child tables have more than one row that match the criteria supplied. What i would like to know is the most efficient/ best practice in nhibernate to do this same query but to only get each row in DataMappingBase once. Returning multiple of the same row is breaking my number of results returned as i try to limit it 25 but sometimes i get the same row 25 times.
MappedID id = null;
DataMappingBase mapBase = null;
NameDetails name = null;
dmbs = mappingSession.QueryOver<DataMappingBase>(() => mapBase)
.JoinAlias(() => mapBase.IDs, () => id).WhereRestrictionOn(() => id.SecondaryDataIDType).IsNull()
.JoinAlias(() => mapBase.Names, () => name).WhereRestrictionOn(() => name.Name).IsInsensitiveLike(request.Filter, MatchMode.Anywhere)
.Take(request.MaxResults)
.List();
i am currently looking at converting the query above to a detached query and removing the "take" clause and getting it to just return the ID of the matching rows and have it used in a sub-query selecting from "DataMappingBase" where the rows ID is in the ids returned by the sub-query but i am not sure if that is the best way or not.
I'm not sure, but you can do like this:
MappedID id = null;
DataMappingBase mapBase = null;
NameDetails name = null;
dmbs = mappingSession.QueryOver<DataMappingBase>(() => mapBase)
.JoinAlias(() => mapBase.IDs, () => id).WhereRestrictionOn(() => id.SecondaryDataIDType).IsNull()
.JoinAlias(() => mapBase.Names, () => name).WhereRestrictionOn(() => name.Name).IsInsensitiveLike(request.Filter, MatchMode.Anywhere)
.Take(request.MaxResults)
// add this
.TransformUsing(Transformers.DistinctRootEntity)
.List();

NHibernate QueryOver, Projections and Aliases

I have an nhibernate issue where I am projecting the sql Coalesce function.
I am comparing two string properties having the same name, from two different entities. In the resulting sql, the same property from only the first entity is being compared thus:
var list = Projections.ProjectionList();
list.Add(
Projections.SqlFunction("Coalesce",
NHibernateUtil.String,
Projections.Property<TranslatedText>(tt => tt.ItemText),
Projections.Property<TextItem>(ti => ti.ItemText)));
var q = Session.QueryOver<TextItem>()
.Left.JoinQueryOver(ti => ti.TranslatedItems);
Evaluating q results in this sql
coalesce(this_.ItemText, this_.ItemText)
the this_ in the RHS needs to be an aliased table
I can use Projections.Alias(Projections.Property<TranslatedText>(tt => tt.ItemText), "ttAlias") but am not sure how to map "ttAlias" in the JoinQueryOver.
I can create an alias there too, but can't see how to name it.
TranslatedText ttAlias = null;
...
JoinQueryOver(ti => ti.TranslatedItems, () => ttAlias)
Aliases are variables in QueryOver, like you showed in the JoinQueryOver call. Alias names (strings) should not be needed in QueryOver, they are for Criteria queries.
To the problem itself: I can't test it right now, but I think this should work:
Projections.Property(() => ttAlias.ItemText)
I used this topic as a resource while writing a unit test. This QueryOver works well and may help others with similar issues. QueryOver still struggles with property mapping to transformers using expressions. It's technically possible to remove "Id" but IMHO it hinders clarity.
The complete example is on GitHub
String LocalizedName = "LocalizedName";
//Build a set of columns with a coalese
ProjectionList plColumns = Projections.ProjectionList();
plColumns.Add(Projections.Property<Entity>(x => x.Id), "Id");
plColumns.Add(Projections.SqlFunction("coalesce",
NHibernateUtil.String,
Projections.Property<Entity>(x => x.EnglishName),
Projections.Property<Entity>(x => x.GermanName))
.WithAlias(() => LocalizedName));
ProjectionList plDistinct = Projections.ProjectionList();
plDistinct.Add(Projections.Distinct(plColumns));
//Make sure we parse and run without error
Assert.DoesNotThrow(() => session.QueryOver<Entity>()
.Select(plDistinct)
.TransformUsing(Transformers.AliasToBean<LocalizedEntity>())
.OrderByAlias(() => LocalizedName).Asc
.Skip(10)
.Take(20)
.List<LocalizedEntity>());