Not sure this is even possible but I'll explain anyway.

I have a custom projection for doing simple arithmetic in a query
which works perfectly in a single projection.

    namespace Custom.Projections
    {
        using System.Collections.Generic;
        using System.Text;
        using global::NHibernate;
        using global::NHibernate.Criterion;
        using global::NHibernate.SqlCommand;
        using global::NHibernate.Type;

        /// <summary>
        /// Represents a CalculatedProjection
        /// </summary>
        public class CalculatedProjection : SimpleProjection
        {
            private readonly CalculationType calculationType;
            private readonly IProjection firstProjection;
            private readonly IProjection secondProjection;
            private readonly string firstPropertyName;
            private readonly string secondPropertyName;

            /// <summary>
            /// Initializes a new instance of the CalculatedProjection
class
            /// </summary>
            /// <param name="type">The type of calculation</param>
            /// <param name="firstPropertyName">The name of the first
property</param>
            /// <param name="secondPropertyName">The name of the
second property</param>
            protected internal CalculatedProjection(CalculationType
type, string firstPropertyName, string secondPropertyName)
            {
                this.calculationType = type;
                this.firstPropertyName = firstPropertyName;
                this.secondPropertyName = secondPropertyName;
                System.Diagnostics.Debugger.Launch();
            }

            /// <summary>
            /// Initializes a new instance of the CalculatedProjection
class
            /// </summary>
            /// <param name="type">The type of calculation</param>
            /// <param name="firstProjection">The first projection</
param>
            /// <param name="secondProjection">The second projection</
param>
            protected internal CalculatedProjection(CalculationType
type, IProjection firstProjection, IProjection secondProjection)
            {
                this.calculationType = type;
                this.firstProjection = firstProjection;
                this.secondProjection = secondProjection;
            }

            /// <summary>
            /// The type of calculation
            /// </summary>
            public enum CalculationType
            {
                /// <summary>
                /// Addition +
                /// </summary>
                Addition,

                /// <summary>
                /// Subtraction -
                /// </summary>
                Subtraction,

                /// <summary>
                /// Division /
                /// </summary>
                Division,

                /// <summary>
                /// Division *
                /// </summary>
                Multiplication,
            }

            /// <summary>
            /// Gets a value indicating whether the projection is
grouped
            /// </summary>
            public override bool IsGrouped
            {
                get { return false; }
            }

            /// <summary>
            /// Gets a value indicating whether IsAggregate.
            /// </summary>
            public override bool IsAggregate
            {
                get { return false; }
            }

            /// <summary>
            /// Converts the calculation into a string
            /// </summary>
            /// <returns>The string representation of the calculation</
returns>
            public override string ToString()
            {
                var firstValue = this.firstProjection != null ?
this.firstProjection.ToString() : this.firstPropertyName;
                var secondValue = this.secondProjection != null ?
this.secondProjection.ToString() : this.secondPropertyName;

                return "(" + firstValue +
TypeToString(this.calculationType) + secondValue + ")";
            }

            /// <summary>
            /// Gets the types involved in the query
            /// </summary>
            /// <param name="criteria">The current criteria</param>
            /// <param name="criteriaQuery">The criteria query</param>
            /// <returns>An array of IType</returns>
            public override IType[] GetTypes(ICriteria criteria,
ICriteriaQuery criteriaQuery)
            {
                var types = new List<IType>();

                if (this.firstProjection != null)
                {
 
types.AddRange(this.firstProjection.GetTypes(criteria,
criteriaQuery));
                }
                else
                {
                    types.Add(criteriaQuery.GetType(criteria,
this.firstPropertyName));
                }

                if (this.secondProjection != null)
                {
 
types.AddRange(this.secondProjection.GetTypes(criteria,
criteriaQuery));
                }
                else
                {
                    types.Add(criteriaQuery.GetType(criteria,
this.secondPropertyName));
                }

                return types.ToArray();
            }

            /// <summary>
            /// Converts the objects to an sql string representation
            /// </summary>
            /// <param name="criteria">The criteria being used in the
query</param>
            /// <param name="loc">The location in the query</param>
            /// <param name="criteriaQuery">The criteria query</param>
            /// <param name="enabledFilters">List of enabled filters</
param>
            /// <returns>The calculation as an sql string</returns>
            public override SqlString ToSqlString(ICriteria criteria,
int loc, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter>
enabledFilters)
            {
                string first, second;

                if ((this.firstProjection != null) &&
(this.secondProjection != null))
                {
                    first =
global::NHibernate.Util.StringHelper.RemoveAsAliasesFromSql(this.firstProjection.ToSqlString(criteria,
loc, criteriaQuery, enabledFilters)).ToString();
                    second =
global::NHibernate.Util.StringHelper.RemoveAsAliasesFromSql(this.secondProjection.ToSqlString(criteria,
loc, criteriaQuery, enabledFilters)).ToString();
                }
                else
                {
                    first = criteriaQuery.GetColumn(criteria,
this.firstPropertyName);
                    second = criteriaQuery.GetColumn(criteria,
this.secondPropertyName);
                }

                return new SqlString(new object[] { "(", first,
TypeToString(this.calculationType), second, ") as y", loc.ToString(),
"_" });
            }

            /// <summary>
            /// Converts the objects to an sql string representation
            /// </summary>
            /// <param name="criteria">The criteria being used in the
query</param>
            /// <param name="criteriaQuery">The criteria query</param>
            /// <param name="enabledFilters">List of enabled filters</
param>
            /// <returns>The calculation as an sql string</returns>
            public override SqlString ToGroupSqlString(ICriteria
criteria, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter>
enabledFilters)
            {
                var sb = new StringBuilder();

                return new SqlString(new object[] { sb.ToString() });
            }

            /// <summary>
            /// Returns the string symbol of calculation type
            /// </summary>
            /// <param name="type">The type to use</param>
            /// <returns>The string representation</returns>
            private static string TypeToString(CalculationType type)
            {
                switch (type)
                {
                    case CalculationType.Addition: return "+";
                    case CalculationType.Subtraction: return "-";
                    case CalculationType.Multiplication: return "*";
                    case CalculationType.Division: return "/";
                    default: return "+";
                }
            }
        }
    }

I am now trying to use this projection from within another using the
QueryOver syntax as below:

    public override IList<DaySummaryDetail> Execute()
    {
        // ReSharper disable PossibleNullReferenceException
        var calculated =
WheatleyProjections.Calculated(CalculatedProjection.CalculationType.Subtraction,
"Duration.EndTime", "Duration.StartTime");

        DaySummaryDetail detail = null;

        return Session.QueryOver<WorkingDay>()
            .SelectList(list => list
            .Select(Projections.Group<WorkingDay>(x =>
x.Date).WithAlias(() => detail.Date))
            .Select(Projections.Sum(calculated)).WithAlias(() =>
detail.TotalMinutes))
            .Where(x => x.Person.Id == 1)
            .TransformUsing(Transformers.AliasToBean<DaySummaryDetail>())
            .List<DaySummaryDetail>();

        // ReSharper restore PossibleNullReferenceException
    }

The query generated is correct:

    SELECT
        this_.DATE_TIME as y0_,
        sum((this_.END_MINS-this_.START_MINS)) as y1_
    FROM
        WORKINGDAY this_
    WHERE
        this_.PERSON_ID = @p0
    GROUP BY
        this_.DATE_TIME;
    @p0 = 1 [Type: Int64 (0)]

The issue I have is that I get the following exception:

NHibernate.Exceptions.GenericADOException : could not execute query
[ SELECT this_.DATE_TIME as y0_, sum((this_.END_MINS-
this_.START_MINS)) as y1_ FROM WORKINGDAY this_ WHERE this_.PERSON_ID
= @p0 GROUP BY this_.DATE_TIME ]
  Name:cp0 - Value:8977
[SQL: SELECT this_.DATE_TIME as y0_, sum((this_.END_MINS-
this_.START_MINS)) as y1_ FROM WORKINGDAY this_ WHERE this_.PERSON_ID
= @p0 GROUP BY this_.DATE_TIME]
  ----> System.IndexOutOfRangeException : y2_

StackTrace:

    at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name)
    at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String
name)
    at
NHibernate.Loader.Criteria.CriteriaLoader.GetResultColumnOrRow(Object[]
row, IResultTransformer customResultTransformer, IDataReader rs,
ISessionImplementor session)
    at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session,
QueryParameters queryParameters, Boolean returnProxies)
    at
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor
session, QueryParameters queryParameters, Boolean returnProxies)
    at NHibernate.Loader.Loader.DoList(ISessionImplementor session,
QueryParameters queryParameters)
    at
NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor
session, QueryParameters queryParameters)
    at
NHibernate.Loader.Criteria.CriteriaLoader.List(ISessionImplementor
session)
    at NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList
results)
    at NHibernate.Impl.CriteriaImpl.List(IList results)
    at NHibernate.Impl.CriteriaImpl.List()


As you can see from the the query the aliasing is using two values
(y0_ and y1_) yet somehow it is looking for y2_.
I assume this is because I am using a nested projection here which it
can not deal with OR is it that I have implemented or called the
projection incorrectly?

Any help or alternate suggestions would be great. I realise this can
be done in a number of ways using HQL etc. but was particularly
interested in using QueryOver

Thanks in advance

-- 
You received this message because you are subscribed to the Google Groups 
"nhusers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/nhusers?hl=en.

Reply via email to