Rendre un tableau avec Entity Framework

Il existe un certain nombre de tâches pour lesquelles il est plus pratique d’exporter des données des bases de données vers un document PDF sans utiliser le schéma de conversion HTML vers PDF récemment populaire.

Cet article vous montrera comment générer un document PDF en utilisant le Aspose.PDF for .NET.

Notions de base sur la génération de PDF avec Aspose.PDF

L’une des classes les plus importantes dans Aspose.PDF est la classe Document. Cette classe est un moteur de rendu PDF. Pour présenter une structure PDF, la bibliothèque Aspose.PDF utilise le modèle Document-Page, où :

  • Document - contient les propriétés du document PDF, y compris la collection de pages.
  • Page - contient les propriétés d’une page spécifique et diverses collections d’éléments associés à cette page.

Par conséquent, pour créer un document PDF avec Aspose.PDF, vous devez suivre ces étapes :

  1. Créer l’objet Document.
  2. Ajouter la page (l’objet Page) pour l’objet Document.
  3. Créer des objets qui sont placés sur la page (par exemple, fragment de texte, tableau, etc.).
  4. Ajouter les éléments créés à la collection correspondante sur la page (dans notre cas, ce sera une collection de paragraphes).
  5. Enregistrer le document en tant que fichier PDF.

Le problème le plus courant est la sortie de données au format tableau. La classe Table est utilisée pour traiter les tableaux. Cette classe nous donne la possibilité de créer des tableaux et de les placer dans le document, en utilisant Rows et Cells. Ainsi, pour créer le tableau, vous devez ajouter le nombre requis de lignes et les remplir avec le nombre approprié de cellules.

L’exemple suivant crée le tableau 4x10.

Lors de l’initialisation de l’objet Table, les paramètres de base minimaux ont été utilisés :

  • ColumnWidths - largeur des colonnes (par défaut).
  • DefaultCellPadding - les champs par défaut pour la cellule du tableau.
  • Border - attributs du cadre du tableau (style, épaisseur, couleur).
  • DefaultCellBorder - attributs du cadre de la cellule (style, épaisseur, couleur).

En conséquence, nous obtenons le tableau 4x10 avec des colonnes de largeur égale.

Tableau 4x10

Exportation de données à partir d’objets ADO.NET

La classe Table fournit des méthodes pour interagir avec des sources de données ADO.NET - ImportDataTable et ImportDataView. La première méthode importe des données à partir du DataTable, la seconde à partir du DataView. En supposant que ces objets ne sont pas très pratiques pour travailler dans le modèle MVC, nous nous limiterons à un bref exemple. Dans cet exemple (ligne 50), la méthode ImportDataTable est appelée et reçoit en paramètres une instance de DataTable et des paramètres supplémentaires comme le drapeau d’en-tête et la position initiale (lignes/colonnes) pour la sortie des données.

Exportation de données à partir de l’Entity Framework

Plus pertinent pour le .NET moderne est l’importation de données à partir de frameworks ORM. Dans ce cas, il est judicieux d’étendre la classe Table avec des méthodes d’extension pour importer des données à partir d’une liste simple ou à partir de données groupées. Donnons un exemple pour l’un des ORM les plus populaires - 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());
                    }
                }
            }
        }
    }
}

Les attributs Data Annotations sont souvent utilisés pour décrire les modèles et nous aider à créer le tableau. Par conséquent, l’algorithme de génération de tableau suivant a été choisi pour ImportEntityList :

  • lignes 12-18 : construire une ligne d’en-tête et ajouter des cellules d’en-tête selon la règle “Si l’attribut DisplayAttribute est présent, alors prenez sa valeur sinon prenez le nom de la propriété”.
  • lignes 50-53 : construire les lignes de données et ajouter des cellules de ligne selon la règle “Si l’attribut DataTypeAttribute est défini, alors nous vérifions si nous devons faire des réglages de conception supplémentaires pour cela, sinon il suffit de convertir les données en chaîne et de les ajouter à la cellule ;”.

Dans cet exemple, des personnalisations supplémentaires ont été faites pour DataType.Currency (lignes 32-34) et DataType.Date (lignes 35-43), mais vous pouvez en ajouter d’autres si nécessaire. L’algorithme de la méthode ImportGroupedData est presque le même que le précédent. Une classe GroupViewModel supplémentaire est utilisée pour stocker les données groupées.

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

Puisque nous traitons des groupes, nous générons d’abord une ligne pour la valeur clé (lignes 66-71), et après cela - les lignes de ce groupe.