3

これが可能かどうかはわかりませんが、とにかく説明します。

単一の射影で完全に機能するクエリで単純な算術演算を行うためのカスタム射影があります。

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 "+";
            }
        }
    }
}

以下のように QueryOver 構文を使用して、別の内部からこのプロジェクションを使用しようとしています。

public override IList<DaySummaryDetail> Execute()
{
    // ReSharper disable PossibleNullReferenceException
    var calculated = Custom.Projections.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
}

生成されたクエリは正しいです:

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)]

私が抱えている問題は、次の例外が発生することです。

NHibernate.Exceptions.GenericADOException : クエリを実行できませんでした [ 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 ] 名前: cp0 - 値: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_

スタックトレース:

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()

クエリからわかるように、エイリアシングは 2 つの値 (y0_ と y1_) を使用していますが、何らかの形で y2_ を探しています。これは、ここで処理できないネストされたプロジェクションを使用しているためだと思いますか、それともプロジェクションを間違って実装または呼び出したのですか?

ヘルプや代替の提案は素晴らしいでしょう。これは HQL などを使用してさまざまな方法で実行できることを認識していますが、特に QueryOver の使用に興味がありました。

前もって感謝します

4

1 に答える 1

1

男子校の間違い。

プロジェクションの GetTypes メソッドは、プロジェクションの結果に含まれる型を返す必要があります。関連するフィールドの型を返していました。

次のようになります。

public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
{
    return new IType[] { NHibernateUtil.Int32 };
}

これが誰かの時間を節約することを願っています

于 2011-12-20T08:05:35.803 に答える