InsertDocument performance Issue

Hello,
I have a report that is similar to a parent-child report.
I have anywhere from 200-500 master rows with about 3-5 columns per row in the report. For each master row I call the InsertDocument() function to insert a new document with a table filled with the child records (this can have -N- number of rows and columns).
The problem is that the performance is really bad when you call InsertDocument() this many times. Is there anything in this method that can be modified to increase performance?
BTW, I am cloning the Sub Documents and NOT opening them from file each time.
Thanks,
Andrew

Can someone please help me with this problem…I have not got any response yet and this functionality is very important to us.

Hi
Thanks for your request and sorry for delay, I will investigate this issue and provide you more information within a hour or two. Could you also attach your documents for testing?
Best regards.

Hi,
I can’t attach the docs because of sensitive data. But if you have trouble finding a problem, I can show you an example of how I’m creating the report.
Thanks,
Andrew

Hi
Thank you. It would be great if you show me your code.
Best regards.

Hello,
Here is my code. It is long, but the reports are complex in nature. I’ve also attached a short example of how the output should look. The problem is that when you try to insertDocument of child rows for hundreds of parent rows, it’s very slow.
You’ll notice towards the end where I create the sub reports and call your InsertDocument() function at a specified bookmark
Thank you for your efforts, your help is very much appreciated.
Andy

Hi
Maybe, it is better to insert only part of the document. For example see the following code and attached templates. (I copied only table from the sub document)

public void Test141()
{
    // Create dummy datasource.
    // First create main table structure
    DataTable mainTable = new DataTable("MainTable");
    mainTable.Columns.Add("FirstName");
    mainTable.Columns.Add("LastName");
    mainTable.Columns.Add("Details", typeof(DataTable));
    // Create details table structure
    DataTable orderTable = new DataTable("Order");
    orderTable.Columns.Add("Name");
    orderTable.Columns.Add("Quantity");
    orderTable.Columns.Add("Price");
    // Now let's fill both tables with data
    Random rnd = new Random();
    for (int i = 0; i < 10; i++)
    {
        orderTable.Rows.Clear();
        int count = rnd.Next(20);
        for (int j = 0; j < count; j++)
        {
            orderTable.Rows.Add(new object[] { "Some product name", rnd.Next(100).ToString(), rnd.Next(100).ToString() });
        }
        mainTable.Rows.Add(new object[] { "Alexey", "Noskov", orderTable });
    }
    // Add MergeField event habdler
    _mainDoc.MailMerge.MergeField += new MergeFieldEventHandler(MailMerge_InsertSubTable);
    // Execute mail merge
    _mainDoc.MailMerge.ExecuteWithRegions(mainTable);
    // Save output document
    _mainDoc.Save(@"Test141\out.doc");
}
void MailMerge_InsertSubTable(object sender, MergeFieldEventArgs e)
{
    if (e.FieldName == "Details")
    {
        // Clone sub document
        Document doc = (Document)_subDoc.Clone(true);
        // Execute mail merge
        doc.MailMerge.ExecuteWithRegions((DataTable)e.FieldValue);
        // Get table form the document
        Table tab = doc.FirstSection.Body.Tables[0];
        // Import this table to main document
        Node dstTable = e.Document.ImportNode(tab, true, ImportFormatMode.KeepSourceFormatting);
        // Insert this table into the main document
        // first we should get paragraph where mergefield is placed
        Node par = e.Field.Start.GetAncestor(NodeType.Paragraph);
        // Insert table before this paragraph
        par.ParentNode.InsertBefore(dstTable, par);
        // set text of mergefield
        e.Text = string.Empty;
    }
}
private Document _mainDoc = new Document(@"Test141\main.doc");
private Document _subDoc = new Document(@"Test141\sub.doc");

Best regards.

Hi,
Thanks for you reply.
In my code, I do not use Merge Fields. I create the report entirely dynamically.
Can you show me a better way to create my report using dynamic merge fields etc,. because I do not understand your code above with the Merge data in it.
Unfortunately, I can’t create my reports in Word first because they are purely dynamic columns etc,.
Thanks for your attention to this matter,
Andy

When I try to execute your code, I get the same problem as before using my Data Source with a large amount of records.
Line 1021 below seems to be very problematic.
I guess if nested tables were supported this would all work easily and fast :frowning:
Please HELP…we really need this functionality.
Here is the error:
Server Error in ‘/Reporting_Test’ Application.
There are too many styles in the document.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: There are too many styles in the document.

Source Error:

Line 1019: Dim tab As Aspose.Words.Tables.Table = dstDoc.FirstSection.Body.Tables(0)
Line 1020: 'Import this table to main document
Line 1021: Dim dstTable As Node = bldr.Document.ImportNode(tab, True, ImportFormatMode.KeepSourceFormatting)
Line 1022: 'Insert this table into the main document
Line 1023: 'first we should get paragraph where mergefield is placed

Hi
Thanks for your request. Please try using ImportFormatMode.UseDestinationStyles. Maybe this could help.
Best regards.

Hello,
This still does not help. It doesn’t produce the error, but the report never generates because of it’s very slow performance.
I know that the problem is caused by the ImportNode because when i generate this report without importing/inserting document… it only takes a few seconds.
Please advise.
Thanks,
Andy

Hi
Thanks for your request. As I can see from your document in the sub document you just build table using DocumentBuilder and then insert sub document at the bookmark. Maybe you can just move DocumentBuidler cursor to the bookmark and build Table in the main document. In this case you don’t need InsertDocument method at all.
Best regards.

Hi,
The problem is though, if I build the table in the main document, I would be building a table within a table (nested tables). The bookmark is located within a tablecell in the master document. This behaviour isn’t supported correct?
Please advise.
thanks,
Andy

Hi
Thank you for additional information. Please try using the following technique:

// Open document and create DocumentBuilder
Document doc = new Document(@"Test143\in.doc");
DocumentBuilder builder = new DocumentBuilder(doc);
// Build new Table at any location in the document
Table tab = builder.StartTable();
for (int i = 0; i < 10; i++)
{
    for (int j = 0; j < 5; j++)
    {
        builder.InsertCell();
        builder.Write("Here is some text");
    }
    builder.EndRow();
}
builder.EndTable();
// Move DocumentBuilder cursor to the bookmark
builder.MoveToBookmark("mybk");
Node currentPar = builder.CurrentNode.GetAncestor(NodeType.Paragraph);
// Insert table
currentPar.ParentNode.InsertBefore(tab, currentPar);
// Save output document
doc.Save(@"Test143\out.doc");

I build table somewhere in the document, at the beginning for instance. And then insert it at the bookmark. I also attached input and output documents.
Best regards.

Hello,
I will try your solution and let you know if something like this will work for my scenario.
Thank you,
Andy

Hi,
How can I build the table before or after the master table? The Sub Table that I am building is specific to the Parent Row, in other words, below is the scenario I’m trying to accomplish:

Dim myDataset as new Dataset()
Dim myTable as new DataTable("Table0") ' Master Table
'Fill myTable with 200 or so master records
Dim myTable2 as new DataTable("Table1") 'Child Table 1
'Fill myTable2 with 2000 or so detail records (10 for each row)
'Create a data relation between the two tables
Add the tables to the dataset
Builder.StartTable()
'*****Write out the Master Header Columns*****
Builder.RowFormat.HeadingFormat=True
Builder.Font.Bold=True
Builder.CellFormat.Width=Builder.PageSetup.PageWidth/ds.Tables(0).Columns.Count
For Each Column as DataColumn in Dataset.Tables(0).Columns
Builder.InsertCell()
Builder.Write(Column.ColumnName)
Next
Builder.Font.Bold=False
Builder.RowFormat.HeadingFormat=False
'*****Write out the data*****
For Each ParentRow as DataRow in Dataset.Tables(0).Rows
'Output Parent Row
For Each Column as DataColumn in DataSet.Tables(0).Columns
Builder.InsertCell(ParentRow(Column.Ordinal))
Next
Builder.EndRow
'Create a new row to insert our child data for the Parent Row
'Make the cell as wide as the entire table
Builder.CellFormat.Width=Builder.PageSetup.PageWidth
Builder.InsertCell
For Each ChildRow as DataRow in ParentRow.GetChildRows(DataRelation)
'Output Multiple Child Rows In their Own Table
'I need to add these records to their own table, but we can't just create 'a table here because that would be considered nested tables...correct?
'How can I accomplish this since when you try to use InsertDocument() 'and build table for this many Master/Detail records it has serious 'performance issues?????
Next
'Finished adding the Child Records for Given Parent Row, so now we need to 
'end the table row
Builder.EndRow
Next
Builder.EndTable

Hi
Thank you for additional information. I think you can use two DocumentBuilder objects in this case. The first will be used to build main table and the second will be used to build sub table. Please see the following code, for instance.

'Create empty document and two DocumentBuilder
Dim doc As Document = New Document()
Dim builder_main As DocumentBuilder = New DocumentBuilder(doc)
Dim builder_sub As DocumentBuilder = New DocumentBuilder(doc)
'start building main table
Dim mainTable As Table = builder_main.StartTable()
For itemIdx As Integer = 1 To 5
builder_main.CellFormat.Width = 100
For i As Integer = 1 To 5
builder_main.InsertCell()
builder_main.Write("some tet here")
Next
builder_main.EndRow()
Dim detailCell As Cell = builder_main.InsertCell()
detailCell.CellFormat.Width = 500
'Now we can build sub table
Dim subTable As Table = builder_sub.StartTable()
For i As Integer = 1 To 10
For j As Integer = 1 To 8
builder_sub.InsertCell()
builder_sub.Write("this is text in the sub table")
Next
builder_sub.EndRow()
Next
builder_sub.EndTable()
'Insert sub table into the main table
detailCell.AppendChild(subTable)
builder_main.EndRow()
Next
builder_main.EndTable()
'Save output document
doc.Save("C:\Temp\out.doc")

Hope this could help you.
Best regards.

Thank you for all your help. Your support is great!!!
This was the idea I needed and I believe everything works now.
Much appreciated.
Andy

Hi,
Another question related to the sub table added. Is it possible to have a Header Row that repeats on each page for this table? My sub tables sometimes spill onto the next page.
I tried using DocumentBuilder2.RowFormat.HeadingFormat=True, but with no success.
Could you please advise me if their is a solution.
Thank you for your prompt attention.

Hi
Thanks for your inquiry. Unfortunately, “Repeat as header row on each page” option does not work for nested tables. This is a restriction of MS Word. You can try to build such table using MS Word to make sure.
Best regards.