1

レポートを自動的に生成するライブラリに取り組んでいます。レポートは詳細にはわかりませんが、レポートが呼び出される方法は Oracle データベースで設定されます。各レポートには独自のクラスがあります。各クラスには、DataTable を返すデータベース クエリがあります。

レポート生成プロセスはうまく機能しますが、レポートごとにレポート インデックス ファイルを作成する必要があります。構成はデータベースにもあり、次の形式になっています。

REPORT_ID / QUERY / ELEMENT_NAME

   12 GROUPBY    Reference    
   12 GROUPBY    Internal_Seq 
   12 COUNT      Item_Count   
   12 MIN        Process_Date 
   12 MAX        Job_Id       
   12 SUM        Paid_Amount

レポートごとに構成が異なるため、データテーブルの内容を動的に照会する必要があります。そのとき、ScottGu のブログHelp with Dynamic Linq Queryの情報を見て、必要な場所にたどり着きました。必要に応じてコードを少し変更しましたが、これが私が持っているものです。

    public void GetIndexData(DataTable p_Table)
    {
        string location = Utilities.GetLocation(this.GetType(), MethodBase.GetCurrentMethod());
        m_log.Info(location + "Starting...");

        try
        {
            //Create the datatable to contain the fields/aggregates from the datasource
            if (m_Fields.Count > 0)
            {
                List<ArchiveField> groupFields = new List<ArchiveField>();
                List<ArchiveField> aggreFields = new List<ArchiveField>();

                foreach (ArchiveField _field in m_Fields)
                {
                    if (_field.Query == "GROUPBY")
                    {
                        groupFields.Add(_field);
                    }
                    else
                    {
                        aggreFields.Add(_field);
                    }
                }

                //Build the GroupBy parameters using an anonymous type
                List<string> fields = new List<string>();
                object[] param = new object[groupFields.Count];
                int counter = 0;
                foreach (ArchiveField _field in groupFields)
                {
                    fields.Add("get_Item(@" + counter.ToString() + ") as " + _field.Name);
                    param[counter] = _field.Name;
                    counter++;
                }
                var query = p_Table.AsEnumerable().AsQueryable().GroupBy("New(" + string.Join(",", fields.ToArray()) + ")", "it", param);
                var groupType = query.ElementType;

                //Build the Select parameters using Dynamic Lambda invocation
                fields = new List<string>();
                param = new object[aggreFields.Count];
                counter = 0;
                foreach (ArchiveField _field in aggreFields)
                {
                    fields.Add("@" + counter.ToString() + "(it) as " + _field.Name);
                    param[counter] = GetGroupByExpression(groupType, _field.Name, typeof(long), _field.Query);
                    counter++;
                }
                foreach (ArchiveField _field in groupFields)
                {
                    fields.Add("it.Key." + _field.Name + " as " + _field.Name);
                }
                query = query.Select("New(" + string.Join(",", fields.ToArray()) + ")", param);

                //Convert the IQueryable to a datatable
                PropertyInfo[] _props = query.ElementType.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);

                DataTable dt = new DataTable();
                foreach (PropertyInfo p in _props)
                {
                    dt.Columns.Add(p.Name, p.PropertyType);
                }

                foreach (var l in query)
                {
                    var temp = l;
                    dt.Rows.Add(_props.Select((PropertyInfo p) => p.GetValue(temp, null)).ToArray());
                }                    

                m_DataSource = dt;
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            m_log.Info(location + "Ending...");
        }
    }

    private LambdaExpression GetGroupByExpression(Type p_groupType, string p_ColumnName, Type p_ColumnType, string p_Aggregate)
    {
        MethodInfo fieldMethod = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(string) });
        fieldMethod = fieldMethod.MakeGenericMethod(p_ColumnType);

        ConstantExpression colParam = Expression.Constant(p_ColumnName, typeof(string));
        ParameterExpression rowParam = Expression.Parameter(typeof(DataRow), "r");
        MethodCallExpression fieldMethodCall = Expression.Call(fieldMethod, rowParam, colParam);

        var columnExpression = Expression.Lambda(fieldMethodCall, rowParam);
        ParameterExpression groupParam = Expression.Parameter(p_groupType, "g");

        MethodInfo aggrMethod = null;
        MethodCallExpression aggrMethodCall = null;
        if (p_Aggregate.ToUpper() == "COUNT")
        {
            aggrMethod = typeof(Enumerable).GetMethods().Single(m => m.Name.ToUpper() == p_Aggregate.ToUpper() & m.IsStatic & m.GetParameters().Length == 1);
            aggrMethod = aggrMethod.MakeGenericMethod(typeof(DataRow));

            aggrMethodCall = Expression.Call(aggrMethod, groupParam);
        }
        else
        {
            aggrMethod = typeof(Enumerable).GetMethods().Single(m => m.Name.ToUpper() == p_Aggregate.ToUpper() & m.ReturnType.Equals(p_ColumnType) & m.IsGenericMethod);
            aggrMethod = aggrMethod.MakeGenericMethod(typeof(DataRow));

            aggrMethodCall = Expression.Call(aggrMethod, groupParam, columnExpression);
        }

        return Expression.Lambda(aggrMethodCall, groupParam);
    }

コードでは、m_DataSource はクラスで DataTable として定義されており、最終結果が含まれている必要があります。

これは、Microsoft の System.Linq.Dynamic クラスからの抜粋です。

    public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (keySelector == null) throw new ArgumentNullException("keySelector");
        if (elementSelector == null) throw new ArgumentNullException("elementSelector");
        LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values);
        LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values);
        return source.Provider.CreateQuery(
            Expression.Call(
                typeof(Queryable), "GroupBy",
                new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type },
                source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda)));
    }

    public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (keySelector == null) throw new ArgumentNullException("keySelector");
        if (elementSelector == null) throw new ArgumentNullException("elementSelector");
        LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, values);
        LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, values);
        return source.Provider.CreateQuery(
            Expression.Call(
                typeof(Queryable), "GroupBy",
                new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type },
                source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda)));
    }

私のコードは、クエリオブジェクトの要素を見つけようとするたびに、「指定されたキャストは無効です」というエラーで、この部分で失敗します。

                foreach (var l in query)

次のことを知っているので、これに対する解決策が必要です。

  1. Framework 3.5 で作業する必要があります (これを変更する機会はありません)。
  2. レポート クラスは遅延バインディングによって呼び出されるため、すべての呼び出しは動的であり、使用する集計を事前に知ることはできません (構成はレポートごとに変更されます)。
  3. サードパーティ ライブラリの数を制限する必要があります。外部からの影響を受けずにコード内ですべてを実行できるのであれば、それは良いことです。
  4. データはレポート クラスから取得されるため、集計を使用してセカンダリ クエリを生成するためのデータベースへのアクセス権がありません。

データテーブルに必要な値を取得するために何が欠けていますか? そもそも元の例に欠陥があり、必要なデータ行を列挙できるキャストが見つからないのでしょうか?

4

0 に答える 0