nhibernate queryover how to select an alias column? - nhibernate

Assuming I have three simple tables
schedule
{
Student student { get; set;}
Teacher teacher { get; set;}
bool Deleted { get; set; }
}
Student
{
string Name { get; set; }
IList<schedule> TeacherMeetings {get; set; }
}
and assume teacher has the same thing, name and list of student schedules.
I want to select, from schedule, the list of all student names for a particular teacher. I can write the query for conditions and everything, but having trouble selecting just the student names.
Here is my current query:
DetachedCriteria dc = QueryOver.Of<Schedule>(() => sAlias)
.JoinAlias(() => sAlias.student, () => studentAlias)
.JoinAlias(() => sAlias.teacher, () => teacherAlias)
.Where(() => teacherAlias.MembershipGuid == teacherGuid)
.AndNot(() => sAlias.isDeleted)
//.Select(() => studentAlias.Name)
.SelectList(list => list
.SelectGroup(x => x.student.Name).WithAlias(() => studentAlias.Name))
.DetachedCriteria
;
If I comment out all select, profiler shows the query being generated as the right one, with right joins on right tables, only, it selects all columns on all tables.
I can't seem to get the select right, I only need the student.Name only.
How do I write this please? I've been at this for over an hour trying out different things but it is always the select that errors out, saying NHibernate.QueryException: could not resolve property and that studentName isn't recognizable.
thanks.

If you just want a list of strings, you shouldn't need to use WithAlias. WithAlias is used to project that column into a member on a result object.
Something like this should work:
DetachedCriteria dc = QueryOver.Of<Schedule>(() => sAlias)
.JoinAlias(() => sAlias.student, () => studentAlias)
.JoinAlias(() => sAlias.teacher, () => teacherAlias)
.Where(() => teacherAlias.MembershipGuid == teacherGuid)
.AndNot(() => sAlias.isDeleted)
.SelectList(list => list
.Select(() => studentAlias.Name))
.DetachedCriteria;
So the following should get you a list of strings:
IList<string> names = dc.GetExecutableCriteria(session)
.List<string>();
Update (per comment). Here's how you would order by student name, descending:
DetachedCriteria dc = QueryOver.Of<Schedule>(() => sAlias)
.JoinAlias(() => sAlias.student, () => studentAlias)
.JoinAlias(() => sAlias.teacher, () => teacherAlias)
.Where(() => teacherAlias.MembershipGuid == teacherGuid)
.AndNot(() => sAlias.isDeleted)
.SelectList(list => list
.Select(() => studentAlias.Name))
.OrderBy(() => studentAlias.Name).Desc()
.DetachedCriteria;

Related

NHibernate QueryOver - Join without path (or with "reversed" path)

I am trying to fetch several entities without having a single root entity ( the directed graph of entity-relations is only a weakly connected graph, not strongly connected) and I cant figure out how to do it in Nhibernates QueryOver api (or Linq, but that seems to be even weaker)
These are the relations I have:
ClientTaxEntity references 1 Client and N Manufacturers
InstructionTemplate references 1 Client and 1 Manufacturer
I want to get a result with all possible Client-Manufacturer pairs (possible = they are together within a ClientTaxEntity) and fetch a Template to them if it exists (null otherwise)
This is what I have tried so far:
Client client = null;
Manufacturer manufacturer = null;
InstructionTemplate template = null;
ClientTaxEntity taxEntity = null;
InstructionTemplateInfo info = null;
var query =Session.QueryOver<ClientTaxEntity>(() => taxEntity)
.JoinAlias(x => x.Client, () => client)
.JoinAlias(x => x.Manufacturers, () => manufacturer)
.Left
.JoinQueryOver(() => template, () => template,() => template.Client == client && template.Manufacturer == manufacturer);
var result = query
.SelectList(builder => builder
.Select(() => client.Name).WithAlias(() => info.ClientName)
.Select(() => client.Id).WithAlias(() => info.ClientId)
.Select(() => manufacturer.Name).WithAlias(() => info.ManufacturerName)
.Select(() => manufacturer.Id).WithAlias(() => info.ManufacturerId)
.Select(() => template.Id).WithAlias(() => info.TemplateId)
.Select(() => template.Type).WithAlias(() => info.Type)
)
.TransformUsing(Transformers.DistinctRootEntity)
.TransformUsing(Transformers.AliasToBean<InstructionTemplateInfo>())
.List<InstructionTemplateInfo>();
The info object is the result I would like.
However the syntax .JoinQueryOver(() => template does not seem to be valid for a path parameter (exception says : could not resolve property: template of: ClientTaxEntity
To get the result you want when writing a query in SQL it would be necessary to write something along the lines of:
SELECT Client_Id, Client_Name, Manufacturer_Id, Manufacturer_Name
FROM
(
SELECT Client.Id as Client_Id, Client.Name as Client_Name,
Manufacturer.Id as Manufacturer_Id, Manufacturer.Name as Manufacturer_Name
FROM ClientTax
INNER JOIN Client on Client.Id = ClientTax.Client_Id
INNER JOIN Manufacturer on Manufacturer.Id = Manufacturer.Manufacturer_id
UNION
SELECT Client.Id, Client.Name, Manufacturer.Id, Manufacturer.Name
FROM InstructionTemplate
INNER JOIN Client on Client.Id = InstructionTemplate.Client_Id
INNER JOIN Manufacturer on Manufacturer.Id = InstructionTemplate.Manufacturer_id
) a
GROUP BY Client_Id, Client_Name, Manufacturer_Id, Manufacturer_Name;
Unfortunately converting such a query to one of NHibernate's query APIs is not possible because NHibernate does not support the UNION statement*. See this question and the feature request NH-2710 in NHibernate's bug tracker.
*Except when using union-subclass. See the docs for further details
The only options I can see are
Perform an SQL Query and map this to a DTO
public class InstructionTemplateInfo
{
public int Client_Id { get; set; }
public string Client_Name { get; set; }
public int Manufacturer_Id { get; set; }
public string Manufacturer_Name { get; set; }
}
then
var result = session
.CreateSQLQuery(theSQLQueryString)
.SetResultTransformer(Transformers.AliasToBean<InstructionTemplateInfo>())
.List<InstructionTemplateInfo>();
Create a view in the database and map this like a normal entity.
If the DBMS supports multiple results sets, like SQL Server, you could write two queries but mark them both as Future and then merge the two results set in code, i.e.
var resultSet1 = Session.QueryOver<ClientTaxEntity>(() => taxEntity)
.JoinAlias(x => x.Client, () => client)
.JoinAlias(x => x.Manufacturers, () => manufacturer)
.SelectList(builder => builder
.SelectGroup((() => client.Name).WithAlias(() => info.ClientName)
.SelectGroup((() => client.Id).WithAlias(() => info.ClientId)
.SelectGroup((() => manufacturer.Name).WithAlias(() => info.ManufacturerName)
.SelectGroup((() => manufacturer.Id).WithAlias(() => info.ManufacturerId)
.TransformUsing(Transformers.AliasToBean<InstructionTemplateInfo>())
.Future<InstructionTemplateInfo>;
var resultSet2 = Session.QueryOver<InstructionTemplate>(() => taxEntity)
.JoinAlias(x => x.Client, () => client)
.JoinAlias(x => x.Manufacturers, () => manufacturer)
.SelectList(builder => builder
.SelectGroup((() => client.Name).WithAlias(() => info.ClientName)
.SelectGroup((() => client.Id).WithAlias(() => info.ClientId)
.SelectGroup((() => manufacturer.Name).WithAlias(() => info.ManufacturerName)
.SelectGroup((() => manufacturer.Id).WithAlias(() => info.ManufacturerId)
.TransformUsing(Transformers.AliasToBean<InstructionTemplateInfo>())
.Future<InstructionTemplateInfo>;
var result = resultSet1.Concat(resultSet2 )
.GroupBy(x=>x.ClientId+"|"+x.ManufacturerId)
.Select(x=>x.First());
The advantage of this approach is that the DBMS will only be hit once.
See Ayende's blog post for further details about this feature.

nHibernate joining multiple tables and using AliasToBean Transformer

I have a very basic need to get some data from the database and return a DTO. I found that joining multiple tables using nHibernate and "projecting" so to say, to a DTO to be quite a bit of code. After looking at several examples, most which didn't work leaving me a DTO with null values, I cam up with the following and was wondering if you nHibernate ninja's out there could tell me if there is a better way.
public IOpenIdUser GetOpenIdUser(string claimedIdentifier, IOpenIdUser openIdUserDto)
{
User user = null;
OpenIdUser openIdUser = null;
Profile profile = null;
UserType userType = null;
return
SessionWrapper.Session.QueryOver(() => user).JoinAlias(() => user.Profiles, () => profile).
JoinAlias(() => user.OpenIdUsers, () => openIdUser).JoinAlias(() => user.UserType, () => userType)
.Where(() => user.UserName == claimedIdentifier)
.SelectList(l => l
.Select(x => openIdUser.OpenIdUserId).WithAlias(() => openIdUser.OpenIdUserId)
.Select(x => user.UserId).WithAlias(() => openIdUserDto.UserId)
.Select(x => openIdUser.OpenIdClaimedIdentifier).WithAlias(
() => openIdUserDto.ClaimedIdentifier)
.Select(x => openIdUser.OpenIdFriendlyIdentifier).WithAlias(
() => openIdUserDto.FriendlyIdentifier)
.Select(x => openIdUser.OpenIdEndPoint).WithAlias(
() => openIdUserDto.OpenIdEndPoint)
.Select(x => user.UserName).WithAlias(() => openIdUserDto.UserName)
.Select(x => userType.Type).WithAlias(() => openIdUserDto.UserType)
.Select(x => profile.DisplayName).WithAlias(() => openIdUserDto.DisplayName)
.Select(x => profile.EmailAddress).WithAlias(() => openIdUserDto.EmailAddress)
.Select(x => openIdUser.DateCreated).WithAlias(() => openIdUserDto.DateCreated)
.Select(x => openIdUser.LastUpdated).WithAlias(() => openIdUserDto.LastUpdated)
.Select(x => openIdUser.UsageCount).WithAlias(() => openIdUserDto.UsageCount)
).TransformUsing(Transformers.AliasToBean<OpenIdUserDto>()).Future<OpenIdUserDto>().Single();
}
This method sits in my UserRepository and is called by my UserService. Please not that this actually works, I just think it is overkill for such a simple task. Also please note that I am new to this so if this code is crappy I apologize in advance.
If you use Queryover then this is the only way.
If you really think less lines of code are preferable, more intuitive, or sits better with you then you can do either:-
Create a DB view and create mapings files for the view with
mutable="false" in your class definition and use protected set; on your class properties
Use the LINQ provider instead e.g. .Query (see
this blog post for more info)

NHibernate and JoinAlias throw exception

I have query in HQL which works good:
var x =_session.CreateQuery("SELECT r FROM NHFolder f JOIN f.DocumentComputedRights r WHERE f.Id = " + rightsHolder.Id + " AND r.OrganisationalUnit.Id=" + person.Id);
var right = x.UniqueResult<NHDocumentComputedRight>();
Basically I receive NHDocumentComputedRight instance.
I've tried to implement the same query in QueryOver. I did this:
var right = _session.QueryOver<NHFolder>().JoinAlias(b => b.DocumentComputedRights, () => cp).Where(h => h.Id == rightsHolder.Id && cp.OrganisationalUnit.Id == person.Id)
.Select(u => cp).List<NHDocumentComputedRight>();
But I get null reference exception.
How can I implement this query in QueryOver?
Update (added mappings) - NHibernate 3.2:
public class FolderMapping: ClassMapping<NHFolder>
{
public FolderMapping()
{
Table("Folders");
Id(x => x.Id, map =>
{
map.Generator(IdGeneratorSelector.CreateGenerator());
});
//more not important properties...
Set(x => x.DocumentComputedRights, v =>
{
v.Table("DocumentComputedRightsFolder");
v.Cascade(Cascade.All | Cascade.DeleteOrphans);
v.Fetch(CollectionFetchMode.Subselect);
v.Lazy(CollectionLazy.Lazy);
}, h => h.ManyToMany());
Version(x => x.Version, map => map.Generated(VersionGeneration.Never));
}
}
public class DocumentComputedRightMapping : ClassMapping<NHDocumentComputedRight>
{
public DocumentComputedRightMapping()
{
Table("DocumentComputedRights");
Id(x => x.Id, map =>
{
map.Generator(IdGeneratorSelector.CreateGenerator());
});
//more not important properties...
ManyToOne(x => x.OrganisationalUnit, map =>
{
map.Column("OrganisationalUnit");
map.NotNullable(false);
map.Cascade(Cascade.None);
});
}
}
public class OrganisationUnitMapping : ClassMapping<NHOrganisationalUnit>
{
public OrganisationUnitMapping()
{
Table("OrganisationalUnits");
Id(x => x.Id, map =>
{
map.Generator(IdGeneratorSelector.CreateGenerator());
});
//more not important properties...
}
}
Thanks
AFAIK criteria/queryOver can only return the entity it was created for (NHFolder in your example) or columns which are set to entity with aliastobean. you could do a correlated subquery instead.
var subquery = QueryOver.Of<NHFolder>()
.JoinAlias(b => b.DocumentComputedRights, () => cp)
.Where(h => h.Id == rightsHolder.Id && cp.OrganisationalUnit.Id == person.Id)
.Select(u => cp.Id);
var right = _session.QueryOver<NHDocumentComputedRight>()
.WithSubquery.Where(r => r.Id).Eq(subquery)
.SingleOrDefault<NHDocumentComputedRight>();
I think you have a problem with the select statement, have you tried something like this:
var right = _session.QueryOver<NHFolder>()
.JoinAlias(b => b.DocumentComputedRights, () => cp)
.Select(x => x.DocumentComputedRights)
.Where(h => h.Id == rightsHolder.Id && cp.OrganisationalUnit.Id == person.Id)
.List<NHDocumentComputedRight>();
This is what is working for me so it should work in you case as well.
I would guess that the main reason behind the problem is the lack of proper overload on the Select method. In reality you would like to write it like this:
.JoinAlias(b => b.DocumentComputedRights, () => cp)
.Select(() => cp)
but the Expression<Func<object>> is not there. Hopefully it's going to be included in the next version.

How to do subqueries in nhibernate?

I need to do a subquery on a sub collection but I can't get it to work.
I tried this
Task tAlias = null;
List<Task> result = session.QueryOver<Task>(() => tAlias)
.Where(Restrictions.In(Projections.Property(() => tAlias.Course.Id), courseIds))
.WithSubquery.WhereExists(QueryOver.Of<CompletedTask>().Where(x => x.Student.StudentId == settings.StudentId))
().ToList();
Yet I get
Cannot use subqueries on a criteria
without a projection.
session.QueryOver<Task>(() => tAlias)
.WhereRestrictionsOn(x => x.Course.Id).IsIn(courseIds)
.WithSubquery.WhereExists(QueryOver.Of<CompletedTask>()
.Where(x => x.id == tAlias.id) //not sure how you need to link Task to CompletedTask
.Where(x => x.Student.StudentId == settings.StudentId)
.Select(x => x.id)) //exists requires some kind of projection (i.e. select clause)
.List<Task>();
or if you only want the completedtask then just...
Task taskAlias = null;
session.QueryOver<CompletedTask>()
.JoinAlias(x => x.Task, () => taskAlias)
.WhereRestrictionsOn(() => taskAlias.Course.Id).IsIn(courseIds)
.Where(x => x.Student.StudentId == settings.StudentId)
.List<CompletedTask>();
or look into setting up a student filter on the Task.CompletedTasks collection. I've never used this feature before. I believe you have to enable the filter and set the student parameter before you run the query. Then your Task object would only contain completedTasks by that student...
http://nhibernate.info/doc/nh/en/index.html#filters

How to take this sql query and turn it into nhibernate query

I am trying take this sql query and make it into an nhibernate HQL query. I am using nhibernate 3 and Fluent Nhibernate 1.2
SELECT dbo.Tasks.CourseId, dbo.CoursePermissions.BackgroundColor, dbo.Tasks.DueDate, dbo.Tasks.TaskName, dbo.Tasks.TaskId
FROM dbo.Courses INNER JOIN
dbo.Tasks ON dbo.Courses.CourseId = dbo.Tasks.CourseId INNER JOIN
dbo.CoursePermissions ON dbo.Courses.CourseId = dbo.CoursePermissions.CourseId
WHERE (dbo.Tasks.CourseId = 1)
I would have liked to use linq but I don't think nhibernate supports linq joins yet so I guess I am stuck with using HQL(unless someone knows a better way).
I guess I can use QueryOver or the other ways nhibernate does queries so whatever works the best. I still don't understand the difference between all the ways as if I could do everything in linq I would.
However I have no clue on how to write my query.
Thanks
Edit
I now have this(changed a bit)
Course cAlias = null;
Task tAlias = null;
CoursePermission cpAlias = null;
var result = session.QueryOver<Task>(() => tAlias)
.JoinAlias(() => tAlias.Course, () => cAlias)
.JoinAlias(() => cAlias.CoursePermissions, () => cpAlias)
.Where(Restrictions.In(Projections.Property(() => cAlias.Id), courseIds))
.And(x => x.DueDate >= startDate)
.And(x => x.DueDate <= endDate)
.Select( Projections.Property(() => cAlias.Id),
Projections.Property(() => cpAlias.BackgroundColor),
Projections.Property(() => tAlias.DueDate),
Projections.Property(() => tAlias.TaskName),
Projections.Property(() => tAlias.TaskId))
.List<object[]>();
I know want to map it to
public class TaskAppointments
{
public int Id { get; set; }
public string BackgroundColor { get; set; }
public DateTime DueDate { get; set; }
public int TaskId { get; set; }
public string TaskName { get; set; }
}
How do I do this. If this was a linq method I would do
.Select(new TaskAppointments { TaskId = Projections.Property(() => tAlias.TaskId)})
but it says it can't convert it to an int.
Edit2
This is what I came up with
Course cAlias = null;
Task tAlias = null;
CoursePermission cpAlias = null;
TaskAppointments taskAppointments = null;
List<TaskAppointments> result = session.QueryOver<Task>(() => tAlias)
.JoinAlias(() => tAlias.Course, () => cAlias)
.JoinAlias(() => cAlias.CoursePermissions, () => cpAlias)
.Where(Restrictions.In(Projections.Property(() => cAlias.Id), courseIds))
.And(x => x.DueDate >= startDate)
.And(x => x.DueDate <= endDate)
.SelectList(list =>
list.SelectGroup(x => x.TaskId).WithAlias(() => taskAppointments.TaskId)
.SelectGroup(() => cpAlias.BackgroundColor).WithAlias(() => taskAppointments.BackgroundColor)
.SelectGroup(x => x.DueDate).WithAlias(() => taskAppointments.DueDate)
.SelectGroup(x => x.TaskName).WithAlias(() => taskAppointments.TaskName)
)
.TransformUsing(Transformers.AliasToBean<TaskAppointments>())
.List<TaskAppointments>().ToList();
Without mappings I assume that you have the following relationships: Courses -> Tasks (1:n) and Courses -> CoursePermissions (1:n)
I also assumed that you do not want the complete objects but only certain properties, so I used projections.
QueryOver version:
// the aliases are required here, so that we can reference the entities properly
Courses cAlias = null;
Tasks tAlias = null;
CoursePermissions cpAlias = null;
var result = session.QueryOver<Courses>(() => cAlias)
.JoinAlias(() => cAlias.Tasks, () => tAlias)
.JoinAlias(() => cAlias.CoursePermissions, () => cpAlias)
.Where(() => cAlias.CourseId == 1)
.Select(Projections.Property(() => cAlias.CourseId),
Projections.Property(() => cpAlias.BackgroundColor),
Projections.Property(() => tAlias.DueDate),
Projections.Property(() => tAlias.TaskName),
Projections.Property(() => tAlias.TaskId))
.List<object[]>();
Edit start
If you need to do a WHERE IN clause, you can do this:
List<int> courseIdList = new List<int>() { 1, 2 };
var result = session.QueryOver<Courses>(() => cAlias)
.JoinAlias(() => cAlias.Tasks, () => tAlias)
.JoinAlias(() => cAlias.CoursePermissions, () => cpAlias)
.Where(Restrictions.In(Projections.Property(() => cAlias.CourseId), courseIdList))
.Select(...)
.List<object[]>();
Edit end
Edit 2 start
If you want to transform it into a DTO:
// AliasToBeanResultTransformer is in namespace NHibernate.Transform
// we have to use .As("...") for the transformer to find the correct property-names
var result = ...
.Select(Projections.Property(() => cAlias.CourseId).As("CourseId"),
Projections.Property(() => cpAlias.BackgroundColor).As("BackgroundColor"),
Projections.Property(() => tAlias.DueDate).As("DueDate"),
Projections.Property(() => tAlias.TaskName).As("TaskName"),
Projections.Property(() => tAlias.TaskId).As("TaskId"))
.TransformUsing(new AliasToBeanResultTransformer(typeof(TaskAppointments)))
.List<TaskAppointments>();
Edit 2 end
HQL version:
string hql = "select c.CourseId, cp.BackgroundColor, t.DueDate, t.TaskName, t.TaskId"
+ " from Courses as c inner join c.Tasks as t inner join c.CoursePermissions as cp"
+ " where c.CourseId = 1";
var result2 = session.CreateQuery(hql)
.List<object[]>();
Be aware that this will result in a cartesian product, so for each Course you will get Tasks.Count + CoursePermissions.Count rows.