۱۳۹۰/۰۸/۲۳

تهيه گزارشات Crosstab به كمك LINQ - قسمت دوم


اگر به قسمت اول «تهيه گزارشات Crosstab به كمك LINQ» دقت كرده باشيد، يك مشكل كوچك دارد و آن هم لزوم مشخص سازي دقيق ستون‌هايي است كه مي‌خواهيم در گزارش ظاهر شوند. مثلا دقيقا مشخص كنيم كه نام واحد چيست يا دقيقا روز را مشخص كنيم. اين مورد براي گزارش‌هاي كوچك مشكلي ندارد؛ ولي اگر همان مثال دوم را در نظر گرفته و بازه را كمي بيشتر كنيم، مثلا يك ماه، آن وقت بايد حداقل 30 بار بنويسيم Day1IsPresent تا ... Day30IsPresent و يا اگر بازه‌ي گزارشگيري به اختيار كاربر باشد آن وقت چه بايد كرد؟ مثلا يكبار 7 روز پايان ماه را انتخاب كند، يكبار 14 روز را، شايد يك بار هم مثلا 90 روز را مد نظر داشته باشد (تعداد ستون‌ها متغير باشد يا به عبارتي Dynamic Crosstab نياز است ايجاد شود).
براي حل اين مساله، مي‌توان از متد الحاقي زير از سايت extensionmethod.net كمك گرفت:

using System;
using System.Collections.Generic;
using System.Linq;

namespace PivotExtensions
{
    public static class Ext
    {
        public static Dictionary<TKey1, Dictionary<TKey2, TValue>>
                        Pivot<TSource, TKey1, TKey2, TValue>
                        (
                            this IEnumerable<TSource> source,
                            Func<TSource, TKey1> key1Selector,
                            Func<TSource, TKey2> key2Selector,
                            Func<IEnumerable<TSource>, TValue> aggregate
                        )
        {
            return source.GroupBy(key1Selector)
                         .Select(
                            key1Group => new
                                {
                                    Key = key1Group.Key,
                                    Value = key1Group.GroupBy(key2Selector)
                                         .Select(
                                            key2Group => new
                                               {
                                                   K = key2Group.Key,
                                                   V = aggregate(key2Group)
                                               })
                                         .ToDictionary(e => e.K, o => o.V)
                                })
                         .ToDictionary(e => e.Key, o => o.Value);
        }
    }
}

در اين متد:
key1Selector مشخص كننده ستون‌هاي ثابت و مشخص سمت راست يا چپ (بر اساس جهت صفحه) گزارش است. در سيستم‌هاي مختلف اين ستون‌ها نام‌هايي مانند keyColumn ، leftColumn و Row Heading ممكن است داشته باشند.
key2Selector ستون‌هاي پوياي گزارش را تشكيل مي‌دهد. در ساير سيستم‌ها اين پارامتر، pivotNameColumn ،VariableColumn ، topField و يا Column Heading هم ناميده مي‌شود.
Aggregate در اينجا مشخص مي‌كند كه مقادير ستون‌هاي پوياي ياد شده چگونه بايد محاسبه شوند.

با توجه به اين متد، براي نمونه جهت حل مثال اول قسمت قبل خواهيم داشت:

var list = ExpenseDataSource.ExpensesDataSource();
var pivotList = list.Pivot(
                x =>
                    new
                    {
                        x.Date.Year,
                        x.Date.Month
                    },
                  x1 => x1.Department,
                  x2 => x2.Sum(x => x.Expenses));

با خروجي


فايل LINQPad آن از اينجا قابل دريافت است.


و براي حل مثال دوم قسمت قبل مي‌توان نوشت:

var list2 = StudentsStatDataSource.CreateWeeklyReportDataSource();
var lst = list2.Pivot(
                x =>
                    new 
                    {
                        x.Id,
                        x.Name
                    },
              x1 => "Day " + x1.Date.Day,
              x2 => x2.First().IsPresent);

با خروجي


فايل LINQPad آن از اينجا قابل دريافت است.