عرض جدول باستخدام Entity Framework

هناك عدد من المهام عندما يكون من الأكثر ملاءمة تصدير البيانات من قواعد البيانات إلى مستند PDF دون استخدام مخطط تحويل HTML إلى PDF الشائع مؤخرًا.

ستوضح لك هذه المقالة كيفية إنشاء مستند PDF باستخدام Aspose.PDF for .NET.

أساسيات توليد PDF باستخدام Aspose.PDF

واحدة من أهم الفئات في Aspose.PDF هي فئة Document. هذه الفئة هي محرك عرض PDF. لعرض هيكل PDF، تستخدم مكتبة Aspose.PDF نموذج Document-Page، حيث:

  • Document - يحتوي على خصائص مستند PDF بما في ذلك مجموعة الصفحات.
  • Page - يحتوي على خصائص صفحة معينة ومجموعات متنوعة من العناصر المرتبطة بهذه الصفحة.

لذلك، لإنشاء مستند PDF باستخدام Aspose.PDF، يجب عليك اتباع هذه الخطوات:

  1. إنشاء كائن Document.
  2. إضافة الصفحة (كائن Page) لكائن Document.
  3. إنشاء كائنات توضع على الصفحة (مثل جزء نص، جدول، إلخ).
  4. إضافة العناصر التي تم إنشاؤها إلى المجموعة المقابلة على الصفحة (في حالتنا ستكون مجموعة الفقرات).
  5. حفظ المستند كملف PDF.

المشكلة الأكثر شيوعًا هي إخراج البيانات في تنسيق جدول. تُستخدم فئة Table لمعالجة الجداول. تمنحنا هذه الفئة القدرة على إنشاء جداول ووضعها في المستند، باستخدام Rows و Cells. لذا، لإنشاء الجدول، تحتاج إلى إضافة العدد المطلوب من الصفوف وملئها بالعدد المناسب من الخلايا.

المثال التالي ينشئ الجدول 4x10.

عند تهيئة كائن Table، تم استخدام إعدادات الحد الأدنى للواجهة:

  • ColumnWidths - عرض الأعمدة (بشكل افتراضي).
  • DefaultCellPadding - الحقول الافتراضية لخلايا الجدول.
  • Border - سمات إطار الجدول (النمط، السماكة، اللون).
  • DefaultCellBorder - سمات إطار الخلية (النمط، السماكة، اللون).

نتيجة لذلك، نحصل على الجدول 4x10 مع أعمدة ذات عرض متساوي.

جدول 4x10

تصدير البيانات من كائنات ADO.NET

توفر فئة Table طرقًا للتفاعل مع مصادر بيانات ADO.NET - ImportDataTable و ImportDataView. تستورد الطريقة الأولى البيانات من DataTable، والثانية من DataView. مفترضين أن هذه الكائنات ليست مريحة جدًا للعمل في قالب MVC، سنقتصر على مثال موجز. في هذا المثال (السطر 50)، يتم استدعاء طريقة ImportDataTable وتستقبل كمعلمات مثيل DataTable وإعدادات إضافية مثل علامة الرأس والموقع الأولي (صفوف/أعمدة) لإخراج البيانات.

تصدير البيانات من Entity Framework

الأكثر صلة بـ .NET الحديثة هو استيراد البيانات من أطر ORM. في هذه الحالة، من الجيد توسيع فئة Table مع طرق إضافية لاستيراد البيانات من قائمة بسيطة أو من البيانات المجمعة. دعونا نقدم مثالاً لأحد أشهر أطر ORM - Entity Framework.

public static class PdfHelper
{
    private static void ImportEntityList<TSource>(this Aspose.Pdf.Table table, IList<TSource> data)
    {
        var headRow = table.Rows.Add();

        var props = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var prop in props)
        {
            headRow.Cells.Add(prop.GetCustomAttribute(typeof(DisplayAttribute)) is DisplayAttribute dd ? dd.Name : prop.Name);
        }

        foreach (var item in data)
        {
            // Add row to table
            var row = table.Rows.Add();
            // Add table cells
            foreach (var t in props)
            {
                var dataItem = t.GetValue(item, null);
                if (t.GetCustomAttribute(typeof(DataTypeAttribute)) is DataTypeAttribute dataType)
                    switch (dataType.DataType)
                    {

                        case DataType.Currency:
                            row.Cells.Add(string.Format("{0:C}", dataItem));
                            break;
                        case DataType.Date:
                            var dateTime = (DateTime)dataItem;
                            if (t.GetCustomAttribute(typeof(DisplayFormatAttribute)) is DisplayFormatAttribute df)
                            {
                                row.Cells.Add(string.IsNullOrEmpty(df.DataFormatString)
                                    ? dateTime.ToShortDateString()
                                    : string.Format(df.DataFormatString, dateTime));
                            }
                            break;
                        default:
                            row.Cells.Add(dataItem.ToString());
                            break;
                    }
                else
                {
                    row.Cells.Add(dataItem.ToString());
                }
            }
        }
    }

    private static void ImportGroupedData<TKey, TValue>(this Aspose.Pdf.Table table, IEnumerable<Models.GroupViewModel<TKey, TValue>> groupedData)
    {
        var headRow = table.Rows.Add();
        var props = typeof(TValue).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var prop in props)
        {
            headRow.Cells.Add(prop.GetCustomAttribute(typeof(DisplayAttribute)) is DisplayAttribute dd ? dd.Name : prop.Name);
        }

        foreach (var group in groupedData)
        {
            // Add group row to table
            var row = table.Rows.Add();
            var cell = row.Cells.Add(group.Key.ToString());
            cell.ColSpan = props.Length;
            cell.BackgroundColor = Aspose.Pdf.Color.DarkGray;
            cell.DefaultCellTextState.ForegroundColor = Aspose.Pdf.Color.White;

            foreach (var item in group.Values)
            {
                // Add data row to table
                var dataRow = table.Rows.Add();
                // Add cells
                foreach (var t in props)
                {
                    var dataItem = t.GetValue(item, null);

                    if (t.GetCustomAttribute(typeof(DataTypeAttribute)) is DataTypeAttribute dataType)
                        switch (dataType.DataType)
                        {
                            case DataType.Currency:
                                dataRow.Cells.Add(string.Format("{0:C}", dataItem));
                                break;
                            case DataType.Date:
                                var dateTime = (DateTime)dataItem;
                                if (t.GetCustomAttribute(typeof(DisplayFormatAttribute)) is DisplayFormatAttribute df)
                                {
                                    dataRow.Cells.Add(string.IsNullOrEmpty(df.DataFormatString)
                                        ? dateTime.ToShortDateString()
                                        : string.Format(df.DataFormatString, dateTime));
                                }
                                break;
                            default:
                                dataRow.Cells.Add(dataItem.ToString());
                                break;
                        }
                    else
                    {
                        dataRow.Cells.Add(dataItem.ToString());
                    }
                }
            }
        }
    }
}

تُستخدم سمات Data Annotations غالبًا لوصف النماذج وتساعدنا في إنشاء الجدول. لذلك، تم اختيار خوارزمية توليد الجدول التالية لـ ImportEntityList:

  • الأسطر 12-18: بناء صف رأس وإضافة خلايا رأس وفقًا للقانون “إذا كانت DisplayAttribute موجودة، فخذ قيمتها وإلا خذ اسم الخاصية”.
  • الأسطر 50-53: بناء صفوف البيانات وإضافة خلايا الصف وفقًا للقانون “إذا كانت السمة DataTypeAttribute محددة، فإننا نتحقق مما إذا كنا بحاجة إلى إجراء إعدادات تصميم إضافية لها، وإلا فقط نقوم بتحويل البيانات إلى سلسلة وإضافتها إلى الخلية؛”.

في هذا المثال، تم إجراء تخصيصات إضافية لـ DataType.Currency (الأسطر 32-34) و DataType.Date (الأسطر 35-43)، ولكن يمكنك إضافة أخرى إذا لزم الأمر. خوارزمية طريقة ImportGroupedData مشابهة تقريبًا للأخرى السابقة. يتم استخدام فئة GroupViewModel الإضافية، لتخزين البيانات المجمعة.

using System.Collections.Generic;
public class GroupViewModel<K,T>
{
    public K Key;
    public IEnumerable<T> Values;
}

نظرًا لأننا نعالج المجموعات، نقوم أولاً بإنشاء سطر لقيمة المفتاح (الأسطر 66-71)، وبعد ذلك - الأسطر الخاصة بهذه المجموعة.