Document Solutions for Excel, .NET Edition | Document Solutions
Templates / Data Source Binding
In This Topic
    Data Source Binding
    In This Topic

    Once the template layout is prepared in Excel including bound fields, expressions, formula and sheet name fields, these fields need to be bound to a data source. You can add a data source using the AddDataSource method and bind the data with template using the ProcessTemplate method. This will populate the data from datasource in the template fields to generate the Excel report.

    Also, you can use multiple data sources or multiple data tables within a data source and populate data through them. The syntax requires you to define the object of the data source followed by the data field. For example, the below template layout merges data from two data sources, the employee information from one data table and Department information from another table.

    Multiple data sources

    DsExcel supports the below data sources while using templates:

    DataTable

    A single table which has collection of rows and columns from any type of database.

     

    Template syntax

    [Alias of data source].[Column name]

    For example:

    {{ds.ID}}

    {{ds.Name}}

    Bind DataTable datasource

    C#
    Copy Code
    var datasource = new System.Data.DataTable();
     datasource.Columns.Add(new DataColumn("ID", typeof(Int32)));
     datasource.Columns.Add(new DataColumn("Name", typeof(string)));
     datasource.Columns.Add(new DataColumn("Score", typeof(Int32)));
     datasource.Columns.Add(new DataColumn("Team", typeof(string)));
     
     
     ...//Init data
     
     
     //Add data source
     workbook.AddDataSource("ds", datasource);

     

    DataSet

    A collection of one or more DataTables.

     

    Template syntax

    [Alias of data source].[Table name].[Column name]

    For example:

    {{ds.Table1.ID}}

    {{ds.Table2.Team}}

     

    Bind DataSet datasource

    C#
    Copy Code
    var dTable1 = new System.Data.DataTable();
    var dTable2 = new System.Data.DataTable();
     
     
     ...//Init data
     
     
     var datasource = new System.Data.DataSet();
     datasource.Tables.Add(team1);
     datasource.Tables.Add(team2);
     
     
     //Add data source
     workbook.AddDataSource("ds", datasource);

    Custom Object

    A user-defined object from user code or serialized object of JSON String/File/XML, etc. DsExcel Template supports any data source that can be serialized as a custom object.

     

    Template syntax

    [Alias of data source].[Field name]

    or

    [Alias of data source].[Property name]

    For example:

    {{ds.Records.Area}}

    {{{ds.Records.Product}}

     

    Bind Custom Object datasource

    C#
    Copy Code
    var datasource = new SalesData
                 {
                     Records = new List<SalesRecord>()
                 };
     
                 var record1 = new SalesRecord
                 {
                     Area = "NorthChina",
                     Salesman = "Hellen",
                     Product = "Apple",
                     ProductType = "Fruit",
                     Sales = 120
                 };
                 datasource.Records.Add(record1);
     
                 var record2 = new SalesRecord
                 {
                     Area = "NorthChina",
                     Salesman = "Hellen",
                     Product = "Banana",
                     ProductType = "Fruit",
                     Sales = 143
                 };
                 datasource.Records.Add(record2);
     
     
     ...//Init data
     
     
     //Add data source
     workbook.AddDataSource("ds", datasource);

    Custom Data Table

    A user-defined custom table is a collection of rows and columns from any type of database. DsExcel provides ITableDataSource interface to simplify creating custom data sources and handle them more efficiently. The following table lists all the members of ITableDataSource interface:

    Members Description
    GetValue Gets the value based on the row and column index.
    GetRowCount Gets the row count.
    GetColumnCount Gets the column count.
    GetColumnName Gets the column name based on the column index.
    GetColumnIndex Gets the column index based on the column name.

    Template syntax

    [Alias of data source].[Field name]

    or

    [Alias of data source].[Property name]

    For example:

    {{customer.name}}

    {{product.name}}

    Refer to the following example code to create a custom data table from a JSON stream using a class that transforms JSON stream into a structured table:

    C#
    Copy Code
    class Program
    {
        static void Main(string[] args)
        {
            // Initialize Workbook.
            var workbook = new Workbook();
    
            // Open template file.
            workbook.Open("ComplexMultiDataSource.xlsx");
    
            // Create table data sources from JSON.
            Stream order_json = new FileStream("order.json", FileMode.Open);
            Stream customer_json = new FileStream("customer.json", FileMode.Open);
            Stream product_json = new FileStream("product.json", FileMode.Open);
            JsonTable order = new JsonTable(order_json);
            JsonTable customer = new JsonTable(customer_json);
            JsonTable product = new JsonTable(product_json);
    
            // Add data sources for template.
            workbook.AddDataSource("order", order);
            workbook.AddDataSource("customer", customer);
            workbook.AddDataSource("product", product);
    
            // Process the template.
            workbook.ProcessTemplate();
    
            // Set column width.
            workbook.Worksheets[0].Range["A:F"].ColumnWidth = 16;
    
            // Save to an Excel file.
            workbook.Save("CustomDataTable.xlsx");
    
        }
    }
    
    /*
    The class is only part of the sample, not part of the product.
    So it is not recommended to use this class in a production environment.
    Please implement your own class that meets the requirements of the product.
    */
    // Create a class to transform a JSON stream into a table and inherit ITableDataSource.
    internal class JsonTable : ITableDataSource
    {
        private readonly List<JsonElement> _jsonArray;
        private readonly Dictionary<int, string> _columnsMap;
        private readonly Dictionary<string, int> _reversedColumnsMap;
    
        public JsonTable(Stream inputStream)
        {
            string jsonContent;
            try
            {
                jsonContent = ConvertToString(inputStream);
            }
            catch (IOException e)
            {
                throw new Exception(e.Message, e);
            }
    
            _jsonArray = ToJsonArray(JsonDocument.Parse(jsonContent).RootElement.EnumerateArray());
    
            JsonElement jsonObject = _jsonArray[0];
    
            _columnsMap = new Dictionary<int, string>();
            _reversedColumnsMap = new Dictionary<string, int>();
            foreach (var property in jsonObject.EnumerateObject())
            {
                _columnsMap.Add(_columnsMap.Count, property.Name);
                _reversedColumnsMap.Add(property.Name, _reversedColumnsMap.Count);
            }
        }
    
        private List<JsonElement> ToJsonArray(ArrayEnumerator enumerator)
        {
            var jsonArray = new List<JsonElement>();
            while (enumerator.MoveNext())
            {
                jsonArray.Add(enumerator.Current);
            }
            return jsonArray;
        }
    
        private string ConvertToString(Stream inputStream)
        {
            using (StreamReader reader = new StreamReader(inputStream, Encoding.UTF8))
            {
                return reader.ReadToEnd();
            }
        }
    
        // Get column count from data source.
        public int GetColumnCount()
        {
            return _columnsMap.Count;
        }
    
        // Get column index from data source.
        public int GetColumnIndex(string columnName)
        {
            if (_reversedColumnsMap.TryGetValue(columnName, out int res))
            {
                return res;
            }
    
            return -1;
        }
    
        // Get column name from data source.
        public string GetColumnName(int column)
        {
            if (_columnsMap.TryGetValue(column, out string res))
            {
                return res;
            }
    
            return null;
        }
    
        // Get row count from data source.
        public int GetRowCount()
        {
            return _jsonArray.Count;
        }
    
        // Get value from data source.
        public object GetValue(int row, int column)
        {
            JsonElement jsonElement = _jsonArray[row].GetProperty(this.GetColumnName(column));
    
            switch (jsonElement.ValueKind)
            {
                case JsonValueKind.String:
                    return jsonElement.GetString();
                case JsonValueKind.Number:
                    return jsonElement.GetDouble();
                case JsonValueKind.True:
                    return true;
                case JsonValueKind.False:
                    return false;
                case JsonValueKind.Null:
                case JsonValueKind.Undefined:
                case JsonValueKind.Object:
                case JsonValueKind.Array:
                default:
                    return null;
            }
        }
    }

    You can also view the demo to learn more about Custom Data Table.

    JSON

    DsExcel allows you to create a new instance of JsonDataSource class as a custom object. Hence, users with json as their data source can directly fetch data from json file and construct a JsonDataSource from the json text and then use the JsonDataSource for the template.

    This eradicates the need to create a mapping class to fetch the data from Json and user can directly use a field or member of the json as given in template syntax below:

    Template Syntax

    [Alias of data source].[Field name]

    For example:

    {{ds.student.family.father.name}}

    {{ds.student.family.father.occupation}}

    {{ds.student.family.mother.name}}

    Sample JSON for Reference

    {
    
      "student": [
    
        {
    
          "name": "Jane",
    
          "address": "101, Halford Avenue, Fremont, CA",
    
          "family": [
    
            {
    
              "father": {
    
                "name": "Patrick James",
    
                "occupation": "Surgeon"
    
              },
    
              "mother": {
    
                "name": "Diana James",
    
                "occupation": "Surgeon"
    
              }
    
            },
    
            {
    
              "father": {
    
                "name": "father James",
    
                "occupation": "doctor"
    
              },
    
              "mother": {
    
                "name": "mother James",
    
                "occupation": "teacher"
    
              }
    
            }
    
          ]
    
        },
    
        {
    
          "name": "Mark",
    
          "address": "101, Halford Avenue, Fremont, CA",
    
          "family": [
    
            {
    
              "father": {
    
                "name": "Jonathan Williams",
    
                "occupation": "Product Engineer"
    
              },
    
              "mother": {
    
                "name": "Joanna Williams",
    
                "occupation": "Surgeon"
              }
    
            }
    
          ]
    
        }
    
      ]
    
    }
                    
    

    Bind JSON datasource 

    C#
    Copy Code
      // Load json text
      var jsonText = File.OpenText("Template_FamilyInfo.json").ReadToEnd();
    
      // Create a JsonDataSource
      var datasource = new JsonDataSource(jsonText);
    
      // Add data source
      workbook.AddDataSource("ds", datasource);

    Variable

    A user-defined variable in code

     

    Template Syntax

    [Alias of data source]

    For example:

    {{cName}}

    {{count}}

    {{owner}}

     

    Bind Variable datasource

    C#
    Copy Code
    var className = "Class 3";
     var count = 500;
     
     //Add data source
     workbook.AddDataSource("cName", datasource);
     workbook.AddDataSource("count", count);
     workbook.AddDataSource("owner", "Hunter Liu");

    Array or List

    A user-defined array or list in code

     

    Template syntax

    1. Array or List of base type variable(string, int , double, etc.)

    [Alias of data source]

     

    2. Array or List of custom object

    [Alias of data source].[Field name]

    or

    [Alias of data source].[Property name]

    For example:

    {{p.Name}}

    {{p.Age}}

    {{countries}}

    {{numbers}}

     

    Bind Array or List datasource

    C#
    Copy Code
    int[] numbers = new int[] { 10, 12, 8, 15};
     List<string> countries = new List<string>() { "USA", "Japan", "UK", "China" };
     
     List<Person> peoples = new List<Person>();
     
     Person p1 = new Person();
     p1.Name = "Helen";
     p1.Age = 12;
     peoples.Add(p1);
     
     Person p2 = new Person();
     p2.Name = "Jack";
     p2.Age = 23;
     peoples.Add(p2);
     
     Person p3 = new Person();
     p3.Name = "Fancy";
     p3.Age = 25;
     peoples.Add(p3);
     
     workbook.AddDataSource("p", peoples);
     workbook.AddDataSource("countries", countries);
     workbook.AddDataSource("numbers", numbers);

    Cancel Template Processing

    DsExcel allows you to cancel ProcessTemplate method by using an overload of ProcessTemplate method that takes a parameter of CancellationToken type. The cancellation is thread-safe, i.e., the cancellation request is sent by a thread that does not own the workbook. You can use the following methods of CancellationTokenSource to send signals to CancellationToken:

    Methods Description
    Cancel Cancels the method immediately.
    CancelAfter Cancels the method after specific delay time.
    Dispose Releases all resources used by current instance.

    The overload of ProcessTemplate method cancels the process when the following conditions are met:

    Note: You must decide whether to accept the partially expanded template or revert to the previous state. If you want to revert to the previous state, you must serialize the workbook before calling the ProcessTemplate method and then deserialize after canceling the operation.

    Refer to the following example code to cancel template processing on request or after a timeout is reached:

    C#
    Copy Code
    // Create a new workbook.
    var workbook = new GrapeCity.Documents.Excel.Workbook();
    
    // Load template file from resource.
    workbook.Open("Template_SalesDataGroup_DataTable.xlsx");
    
    Console.WriteLine("Creating test data.");
    
    // Add data to DataTable.
    var datasource = new System.Data.DataTable();
    datasource.Columns.Add(new DataColumn("Area", typeof(string)));
    datasource.Columns.Add(new DataColumn("City", typeof(string)));
    datasource.Columns.Add(new DataColumn("Category", typeof(string)));
    datasource.Columns.Add(new DataColumn("Name", typeof(string)));
    datasource.Columns.Add(new DataColumn("Revenue", typeof(double)));
    
    var areas = new[] { "North America", "South America" };
    var cities = new[] { "Chicago", "New York", "Santiago", "Quito", "Fremont", "Buenos Aires", "Medillin", "Minnesota" };
    var categories = new[] { "Consumer Electronics", "Mobile" };
    var category1NamePrefixes = new[] { "Bose ", "Canon ", "Haier ", "IFB ", "Mi ", "Sennheiser " };
    var category2NamePrefixes = new[] { "iPhone ", "OnePlus ", "Redmi ", "Samsung " };
    
    Random rand = new Random();
    var rows = datasource.Rows;
    
    // You can increase the loop count if the demo is too fast on your computer.
    for (var i = 0; i < 50000; i++)
    {
        var area = areas[rand.Next(0, areas.Length)];
        var city = cities[rand.Next(0, cities.Length)];
        var categoryId = rand.Next(0, categories.Length);
        var category = categories[categoryId];
        var names = (categoryId == 0) ? category1NamePrefixes : category2NamePrefixes;
        var name = names[rand.Next(0, names.Length)] + rand.Next(10, 10000).ToString();
        var revenue = rand.Next(10000, 100000);
        rows.Add(area, city, category, name, revenue);
    }
    
    // Add template global settings.
    workbook.Names.Add("TemplateOptions.KeepLineSize", "true");
    
    // Add data source.
    workbook.AddDataSource("ds", datasource);
    
    // Cancel data source binding when cancel key is pressed or timeout is reached.
    using (CancellationTokenSource cancellation = new CancellationTokenSource())
    {
        void cancelHandler(object sender, ConsoleCancelEventArgs e)
        {
            // Exit the process.
            e.Cancel = true;
    
            // Prevent entering cancelHandler when ProcessTemplate is completed or canceled.
    #if NETCOREAPP3_0_OR_GREATER
            Console.CancelKeyPress -= cancelHandler;
    #endif
            // Cancel when cancel key is pressed.
            cancellation.Cancel();
        };
        Console.CancelKeyPress += cancelHandler;
    
        // Cancel when timeout is reached.
        cancellation.CancelAfter(TimeSpan.FromSeconds(10));
        Console.WriteLine("Start ProcessTemplate.");
        try
        {
            workbook.ProcessTemplate(cancellation.Token);
            Console.WriteLine("ProcessTemplate finished.");
        }
        catch (OperationCanceledException ex) when (ex.CancellationToken == cancellation.Token)
        {
            Console.WriteLine("ProcessTemplate was canceled.");
        }
    
        // Prevent entering cancelHandler when ProcessTemplate is completed or canceled.
    #if NETCOREAPP3_0_OR_GREATER
        Console.CancelKeyPress -= cancelHandler;
    #endif
    }
    
    // Save the workbook.
    workbook.Save("CancelTemplateProcessing.xlsx");