Ok after several attempts I am stuck on this one!
I am using NHibernate with QueryOver as below. I have a Product and ProductReview as
public class Product
{
....
public virtual IList<ProductReview> CustomerReviews {get;set;}
....
}
public class ProductReview
{
....
public virtual Product Product {get;set;}
....
}
Mapping on Product side is
HasMany(x => x.CustomerReviews).KeyColumn("ProductId").Inverse().Cascade.None().LazyLoad();
The Query is
Product px = null;
ProductReview rev = null;
var result = CurrentSession
.QueryOver<ProductReview>()
.Where(r => r.IsActive && !r.IsDraft)
.Select(
Projections.Property<ProductReview>(r => r.Id).WithAlias(() => rev.Id),
Projections.Property<ProductReview>(r => r.Title).WithAlias(() => rev.Title)
)
.OrderBy(r => r.ReviewDate).Desc()
.TransformUsing(Transformers.AliasToBean<ProductReview>())
.JoinAlias(r => r.Product, () => px)
.Select(
Projections.Property(() => px.UPC).WithAlias(() => px.UPC),
Projections.Property(() => px.FullName).WithAlias(() => px.FullName)
)
.TransformUsing(Transformers.AliasToBean<Product>())
.Take(5)
.List();
The error is:
The value "Reviews.Models.Product" is not of type "Reviews.Models.ProductReview" and cannot be used in this generic collection. Parameter name: value
I really do not want to create another DTO. I would like to get the list of Last 5 new reviews and have it's Product populated (only a few required fields on both entities should be filled).
Is this possible by any means (except raw sql) in NHibernate 3.0?
Product px = null;
ProductReview rev = null;
var result = CurrentSession.QueryOver<ProductReview>()
.Where(r => r.IsActive && !r.IsDraft)
.JoinQueryOver(r => r.Product)
.OrderBy(r => r.ReviewDate).Desc()
.Take(5)
.List();
Related
I'm new on stackoverflow and i hope this question will be appreciated.
Simply said: I select everything from table x left outer join table y. Table x has way too many columns so i make new object x. This object is used for the Projection. I can project every single column of table x i want. But when i try to project/select an collection (the collection of table y) then i get the same error: 'Index was outside the bounds of the array'.
My question: Does NHibernate support to select/project a collection at all? Because i've seen this question multiple times by searching on google (and stackoverflow), but none of those questions were answered.
Code example:
public class X
{
public virtual int ID { get; set; }
public virtual int IDontNeedMoreInfoAboutClassXItTakesToMuchTimeToRetrieve { get; set; }
public virtual IList<Y> YCollection { get; set; }
}
public class Y
{
public virtual int YID { get; set; }
}
public class XRepository{
public ISession Session {get; set;}
public IList<X> Get()
{
X xAlias = null;
Y yAlias = null;
X resultAlias = null;
return Session.QueryOver<X>()
.JoinAlias(() => xAlias.YCollection, () => yAlias, JoinType.LeftOuterJoin)
.SelectList(list => list
.SelectGroup(() => xAlias.ID).WithAlias(() => resultAlias.ID)
.SelectGroup(() => xAlias.YCollection).WithAlias(() => resultAlias.YCollection)) // Index was outside the bounds of the array
.TransformUsing(Transformers.AliasToBean<X>()).List<X>();
}
}
the results returned have to be grouped with the contents intact (so sql is out since it can only aggregate over the grouped items) but NHibernate can only do this for m,apped entities because there it knows the identity to group by. It is simple to do it manually
Y yAlias = null;
var tempResults = Session.QueryOver<X>()
.JoinAlias(x => x.YCollection, () => yAlias, JoinType.LeftOuterJoin)
.SelectList(list => list
.Select(() => xAlias.Id)
.Select(() => xAlias.Name)
...
.Select(() => yAlias.Id)
...
.ToList<object[]>()
List<X> results = new List<X>(100); // switch to Hashset if too slow and order does not matter
foreach(var row in tempResults)
{
Y y = new Y { Id = (long)row[4], ... };
X x = results.FirstOrDefault(x => x.Id == (long)row[0]));
if (x != null)
x.YCollection.Add(y);
else
results.Add(new X { Id = (long)row[0], ..., YCollection = { y } };
}
return results;
I have the following entities
public class Vehicle
{
public virtual string InternalNumber { get; set; }
public virtual IList<VehicleDriverHistory> DriverHistory { get; set; }
}
public class VehicleDriverHistory : HistoryEntity
{
public virtual Vehicle Vehicle { get; set; }
public virtual DriverPerson Driver { get; set; }
public virtual DateTime Date { get; set; }
}
Now I need to select every Vehicle and, if present, the newest VehicleDriverHistory-Entry according to "Date".
But I'm kinda struggling, I have no problem doing it in sql, but failed in nhibernate so far.
What I have so far:
VehicleDriverHistory vdHistory = null;
VehicleDriverHistory vdHistory2 = null;
q.Left.JoinAlias(x => x.DriverHistory, () => vdHistory);
q.Left.JoinAlias(x => x.DriverHistory, () => vdHistory2).Where(x => vdHistory2.Date > vdHistory.Date);
q.Where(x => vdHistory2.Id == null);
This doesn't work and it was just my attempt to "translate" the sql query (which yields the correct data) to nhibernate.
So, how would I select parents and the newest child (or none if none is present)
Thanks
UPDATE
With Firos help I ended up with the following:
VehicleDriverHistory historyAlias = null;
var maxDateQuery = QueryOver.Of<VehicleDriverHistory>()
.Where(h => h.Vehicle == historyAlias.Vehicle)
.Select(Projections.Max<VehicleDriverHistory>(h => h.Date));
var vehiclesWithEntry = DataSession.Current.QueryOver(() => historyAlias)
.WithSubquery.WhereProperty(h => h.Date).Eq(maxDateQuery)
.Fetch(h => h.Vehicle).Eager
.Future();
Vehicle VehicleAlias = null;
var vehiclesWithoutEntry = DataSession.Current.QueryOver<Vehicle>(() => VehicleAlias)
.WithSubquery
.WhereNotExists(QueryOver.Of<VehicleDriverHistory>()
.Where(x => x.Vehicle.Id == VehicleAlias.Id).Select(x => x.Id))
.Future();
return vehiclesWithEntry.Select(h => new PairDTO { Vehicle = h.Vehicle, NewestHistoryEntry = h }).Concat(vehiclesWithoutEntry.Select(v => new PairDTO { Vehicle = v })).ToList();
I had to replace the Any() statement in vehclesWithoutEntry with a subquery-clause because it raised an exception.
some other idea using Futures to make only one roundtrip
VehicleDriverHistory historyAlias = null;
var maxDateQuery = QueryOver.Of<VehicleDriverHistory>()
.Where(h => h.Vehicle == historyAlias.Vehicle)
.Select(Projections.Max<VehicleDriverHistory>(h => h.Date));
var vehiclesWithEntry = session.QueryOver(() => historyAlias)
.WithSubquery.WhereProperty(h => h.Date).Eq(maxDateQuery)
.Fetch(h => h.Vehicle).Eager
.Select(h => new PairDTO { Vehicle = h.Vehicle, NewestHistoryEntry = h })
.Future<PairDTO>();
var vehiclesWithoutEntry = session.QueryOver<Vehicle>()
.Where(v => !v.DriverHistory.Any())
.Select(v => new PairDTO{ Vehicle = v })
.Future<PairDTO>();
return vehiclesWithoutEntry.Concat(vehiclesWithEntry); // .ToList() if immediate executing is required
Update: i can not reproduce the exception but you could try this
VehicleDriverHistory historyAlias = null;
var maxDateQuery = QueryOver.Of<VehicleDriverHistory>()
.Where(h => h.Vehicle == historyAlias.Vehicle)
.Select(Projections.Max<VehicleDriverHistory>(h => h.Date));
var vehiclesWithEntry = session.QueryOver(() => historyAlias)
.WithSubquery.WhereProperty(h => h.Date).Eq(maxDateQuery)
.Fetch(h => h.Vehicle).Eager
.Future();
var vehiclesWithoutEntry = session.QueryOver<Vehicle>()
.Where(v => !v.DriverHistory.Any())
.Select(v => new PairDTO{ Vehicle = v })
.Future<PairDTO>();
return vehiclesWithoutEntry.Select(h => new PairDTO { Vehicle = h.Vehicle, NewestHistoryEntry = h }).Concat(vehiclesWithEntry);
I have a Class A that have a class B List ...
So, with QueryOver I have :
ClassB lb = null;
var result = session.QueryOver<ClassA>
.JoinAlias(x => x.ListB, () => lb, JoinType.LeftOuterJoin)
.Where(() => lb.Property == 1)
.List<ClassA>();
How Can I do that using Nhibernate Query<> ?
Thanks
Paul
Assuming what you want to do is get a list of ClassA having at least one ClassB with Property == 1:
var result = session.Query<ClassA>()
.Where(a => a.ListB.Any(b => b.Property == 1))
.ToList();
This wouldn't be an outer join, though. You might emulate that by adding || !a.ListB.Any().
I have a query by QueryOver In Nhibernate 3.1
var q = SessionInstance.QueryOver<Person>()
.Where(p => p.Code == code);
.Select(p => p.Name,p => p.Code);
return q.SingleOrDefault();
This query without select is correct, but with select has a runtime error by this message: Unable to cast object of type 'System.String' to type 'MyNameSpace.Domain.Entities.Person'. How can i select some field of Person?
you need to tell NHibernate explicitly what is the type of your return data since you choose to select some specific properties of your original entity p.Name,P.Code these properties must by returned as of a new type of entity so the new type would be a new object
var query = SessionInstance.QueryOver<Person>()
.Where(p => p.Code == code)
.Select(
p => p.Name,
p => p.Code
)
.List<object>();
this will solve your problem
or if you defined a new entity to hold the new return data as following :
public newPeople
{
public Id { get; set; }
public Nam { get; set; }
}
you can write it like that:
var query = SessionInstance.QueryOver<Person>()
.Where(p => p.Code == code)
.Select(
p => p.Name,
p => p.Code
)
.Select( list =>
new newPeople()
{
Id = (int) list[0],
Name = (string) list[1]
})
.ToList<newPeople>();
I want to do this:
NHibernate.IQueryOver<DataAccess.Domain.Product, DataAccess.Domain.Product> query = session.QueryOver<DataAccess.Domain.Product>();
query = query.Where(x => x.Name == "X");
query = query.Take(1).Skip(3);
List<Product> results = query.List().ToList();
I cant find any help on Skip or Take. The tooltip help (yes I'm that desperate) says that Skip and Take return IQueryOver but the error message says something to the effect "Cant implicitly convert IQueryOver{T} to IQueryOver{T,T}. I don't know what IQueryOver{T,T} is. I didn't ask for one of those anyway.
Try to change your code like this:
NHibernate.IQueryOver<DataAccess.Domain.Product> query = session.QueryOver<DataAccess.Domain.Product>();
query = query.Where(x => x.Name == "X");
query = query.Take(1).Skip(3);
var results = query.List();
Or, even better:
var results = session.QueryOver<DataAccess.Domain.Product>()
.Where(x => x.Name == "X")
.Take(1)
.Skip(3)
.List();
You can check my code here downloading NHibernateQueryOver.
UPDATE:
I think you're missing something. I would suggest you to read this article which has been really helpful for me.
In the paragraph about Associations they say:
An IQueryOver has two types of interest; the root type (the type of entity that the query returns), and the type of the 'current' entity
being queried. For example, the following query uses a join to create
a sub-QueryOver (analagous to creating sub-criteria in the ICriteria
API):
IQueryOver<Cat,Kitten> catQuery =
session.QueryOver<Cat>()
.JoinQueryOver(c => c.Kittens)
.Where(k => k.Name == "Tiddles");
The JoinQueryOver returns a new instance of the IQueryOver than has
its root at the Kittens collection. The default type for restrictions
is now Kitten (restricting on the name 'Tiddles' in the above
example), while calling .List() will return an IList. The type
IQueryOver inherits from IQueryOver.
This is what I do when I want to build multiple filter:
Domain.OrderAddress addressDestination = null;
Domain.Customer customer = null;
Domain.TermsConditionsOfSale termsConditionsOfSale = null;
ICriterion filter1 = Restrictions.Where<Domain.Order>(t => t.Company == "MYCOMPANY");
ICriterion filter2 = Restrictions.Where<Domain.Order>(t => t.WareHouseDelivery == "DEPXX");
ICriterion filter3 = Restrictions.Where<Domain.Order>(t => t.Status == "X");
ICriterion filter4 = Restrictions.Where(() => addressDestination.AddressType == "99");
ICriterion filter5 = Restrictions.Where(() => addressDestination.Province.IsIn(new string[] { "AA", "BB", "CC" }));
ICriterion filter6 = Restrictions.Where(() => termsConditionsOfSale.ReturnedGoodsCode != "01");
var ordersForProvinces = session.QueryOver<Domain.Order>()
.Inner.JoinAlias(t => t.OrderAddresses, () => addressDestination)
.Inner.JoinAlias(t => t.Customer, () => customer)
.Left.JoinAlias(t => t.TermsConditionsOfSale, () => termsConditionsOfSale);
ordersForProvinces
.Where(filter1)
.And(filter2)
.And(filter3)
.And(filter4)
.And(filter5)
.And(filter6);
var Results = ordersForProvinces.Skip(50).Take(20).List();
UPDATE-UPDATE:
NHibernate.IQueryOver<Domain.Person> person = session.QueryOver<Domain.Person>();
var myList = DoSomething(person);
Method:
private static IList<Domain.Person> DoSomething(NHibernate.IQueryOver<Domain.Person> persons)
{
ICriterion filter1 = Restrictions.Where<Domain.Person>(t => t.CompanyName.IsLike("Customer%"));
persons.RootCriteria.Add(filter1);
var x = persons.Skip(1).Take(3).List();
return (x);
}