Skip to content

NH-3435 - Ability to select the root entity in a criteria projection #302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions src/NHibernate.Test/Criteria/ProjectionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.SqlCommand;
using NUnit.Framework;

namespace NHibernate.Test.Criteria
Expand Down Expand Up @@ -347,6 +348,82 @@ public void UseNotEqPropertyWithProjection()
}
}

[Test]
public void UseRootProjection()
{
//NH-3435
using (ISession session = sessions.OpenSession())
using (ITransaction tx = session.BeginTransaction())
{
Course course = new Course();
course.CourseCode = "HIB";
course.Description = "Hibernate Training";
session.Save(course);

Student gavin = new Student();
gavin.Name = "Gavin King";
gavin.StudentNumber = 667;
session.Save(gavin);

Enrolment enrolment = new Enrolment();
enrolment.Course = course;
enrolment.CourseCode = course.CourseCode;
enrolment.Semester = 1;
enrolment.Year = 1999;
enrolment.Student = gavin;
enrolment.StudentNumber = gavin.StudentNumber;
gavin.Enrolments.Add(enrolment);
session.Save(enrolment);

session.Flush();

Student g = session.CreateCriteria(typeof(Student))
.Add(Expression.IdEq(gavin.StudentNumber))
.SetFetchMode("Enrolments", FetchMode.Join)
.SetProjection(Projections.RootEntity())
.UniqueResult<Student>();

Assert.AreSame(gavin, g);
}
}

[Test]
public void UseEntityProjection()
{
//NH-3435
using (ISession session = sessions.OpenSession())
using (ITransaction tx = session.BeginTransaction())
{
Course course = new Course();
course.CourseCode = "HIB";
course.Description = "Hibernate Training";
session.Save(course);

Student gavin = new Student();
gavin.Name = "Gavin King";
gavin.StudentNumber = 667;
session.Save(gavin);

Enrolment enrolment = new Enrolment();
enrolment.Course = course;
enrolment.CourseCode = course.CourseCode;
enrolment.Semester = 1;
enrolment.Year = 1999;
enrolment.Student = gavin;
enrolment.StudentNumber = gavin.StudentNumber;
gavin.Enrolments.Add(enrolment);
session.Save(enrolment);

session.Flush();

Student g = session.CreateCriteria(typeof(Enrolment))
.Add(Expression.And(Expression.Eq("StudentNumber", gavin.StudentNumber), Expression.Eq("CourseCode", course.CourseCode)))
.CreateAlias("Student", "s", JoinType.InnerJoin)
.SetProjection(Projections.Entity<Student>("s"))
.UniqueResult<Student>();

Assert.AreSame(gavin, g);
}
}
}
}
127 changes: 127 additions & 0 deletions src/NHibernate/Criterion/BaseEntityProjection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Engine;
using NHibernate.Persister.Entity;
using NHibernate.SqlCommand;
using NHibernate.Type;

namespace NHibernate.Criterion
{
[Serializable]
public abstract class BaseEntityProjection : IProjection
{
private string alias = null;
private string[] columnAliases = null;
private bool lazy = true;

protected BaseEntityProjection(System.Type rootEntity, string alias)
{
this.RootEntity = rootEntity;
this.alias = alias;
}

protected System.Type RootEntity
{
get;
private set;
}

public BaseEntityProjection SetLazy(bool lazy)
{
this.lazy = lazy;

return (this);
}

string[] IProjection.Aliases
{
get
{
return (this.columnAliases.ToArray());
}
}

TypedValue[] IProjection.GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
throw new NotImplementedException();
}

IType[] IProjection.GetTypes(string alias, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return (new IType[] { criteriaQuery.GetType(criteria, alias) });
}

private void SetFields(ICriteria criteria)
{
if (this.RootEntity == null)
{
this.RootEntity = criteria.GetRootEntityTypeIfAvailable();
}

if (this.alias == null)
{
this.alias = criteria.Alias;
}
}

IType[] IProjection.GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
this.SetFields(criteria);

return (new IType[] { new ManyToOneType(this.RootEntity.FullName, this.lazy) });
}

bool IProjection.IsAggregate
{
get { return (false); }
}

bool IProjection.IsGrouped
{
get { return (false); }
}

SqlString IProjection.ToGroupSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
{
throw new NotImplementedException();
}

SqlString IProjection.ToSqlString(ICriteria criteria, int position, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> enabledFilters)
{
this.SetFields(criteria);

SqlStringBuilder builder = new SqlStringBuilder();
AbstractEntityPersister persister = criteriaQuery.Factory.TryGetEntityPersister(this.RootEntity.FullName) as AbstractEntityPersister;
ICriteria subcriteria = criteria.GetCriteriaByAlias(this.alias);

this.columnAliases = persister.GetIdentifierAliases(string.Empty);

string[] columnNames = persister.GetPropertyColumnNames(persister.IdentifierPropertyName).Select(x => string.Concat(criteriaQuery.GetSQLAlias(subcriteria, persister.IdentifierPropertyName), ".", criteriaQuery.Factory.Dialect.QuoteForColumnName(x))).ToArray();

for (int i = 0; i < columnNames.Length; ++i)
{
builder.Add(String.Format("{0} as {1}", columnNames[i], this.columnAliases[i]));

if (i < columnNames.Length - 1)
{
builder.Add(", ");
}
}

return (builder.ToSqlString());
}

public string[] GetColumnAliases(int position, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return ((this as IProjection).Aliases);
}

public string[] GetColumnAliases(string alias, int position, ICriteria criteria, ICriteriaQuery criteriaQuery)
{
return ((this as IProjection).Aliases);
}
}
}
20 changes: 20 additions & 0 deletions src/NHibernate/Criterion/EntityProjection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace NHibernate.Criterion
{
[Serializable]
public class EntityProjection : BaseEntityProjection
{
public EntityProjection(System.Type rootEntity, String alias) : base(rootEntity, alias)
{
}
}

[Serializable]
public class EntityProjection<T> : EntityProjection
{
public EntityProjection(String alias) : base(typeof(T), alias)
{
}
}
}
25 changes: 25 additions & 0 deletions src/NHibernate/Criterion/Projections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ namespace NHibernate.Criterion
/// </summary>
public static class Projections
{
/// <summary>
/// Return the root entity.
/// </summary>
/// <returns>The root entity.</returns>
public static IProjection RootEntity()
{
return (new RootEntityProjection());
}

/// <summary>
/// Return an aliased entity.
/// </summary>
/// <param name="type">The type of the entity.</param>
/// <param name="alias">The alias of the entity.</param>
/// <returns>A projection of the entity.</returns>
public static IProjection Entity(System.Type type, string alias)
{
return (new EntityProjection(type, alias));
}

public static IProjection Entity<T>(string alias)
{
return (new EntityProjection<T>(alias));
}

/// <summary>
/// Create a distinct projection from a projection
/// </summary>
Expand Down
12 changes: 12 additions & 0 deletions src/NHibernate/Criterion/RootEntityProjection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace NHibernate.Criterion
{
[Serializable]
public class RootEntityProjection : BaseEntityProjection
{
public RootEntityProjection() : base(null, null)
{
}
}
}
3 changes: 3 additions & 0 deletions src/NHibernate/NHibernate.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@
<Compile Include="Connection\IConnectionProvider.cs" />
<Compile Include="Connection\UserSuppliedConnectionProvider.cs" />
<Compile Include="Dialect\BitwiseFunctionOperation.cs" />
<Compile Include="Criterion\BaseEntityProjection.cs" />
<Compile Include="Criterion\EntityProjection.cs" />
<Compile Include="Criterion\RootEntityProjection.cs" />
<Compile Include="Dialect\DB2Dialect.cs" />
<Compile Include="Dialect\Dialect.cs" />
<Compile Include="Dialect\FirebirdDialect.cs" />
Expand Down