Mail Merge with mutiple tables yielding strange results

Dear sirs or madams,

Setup:
C#, .NET-Framework console-application (for demo purposes; usually using this inside an ASP.NET application)

Scenario:
I’m currently trying to MailMerge a couple of tables using asposeWordsDocument.MailMerge.ExecuteWithRegions(dataset); and I’m having multiple tables inside the dataset with different TableNames, but all of them declare the same columnNames (e.g.

  • tableNames: “AllProducts”, “RedProducts”, “GreenProducts”, …
  • columnNames: “Name”, “PackagingUnit”, “PricePerUnit”), because all of them refer to a List of items of the same class.

As long as all of these tables have at least one data-item, everything is fine. But if one of the tables does not contain any content (header-declarations don’t matter; just content-values), stuff breaks. I’m struggeling with two issues:

  1. The console-app declares a couple of products and MailMerges these products into different tables depending on their color (e.g. products with color == Green will be placed inside the “TableStart:GreenProducts”-mailmerge-region). For I did not define any “Blue”-products, I would assume, that the mailmerge-region “TableStart:BlueProducts” does not receive any content - but it does! There will be (as far as I’ve tested) exactly one entry placed inside the “TableStart:BlueProducts”-region that comes from a different table. Is there any issue with my code or is this an unexpected behaviour inside the MailMerge-Engine?

  2. This might or might not be an issue resulting from issue 1), because this issue does not seem to be apparent, if all tables do contain values. This time the html-bold-tags, which I’ve wrapped around the values of the Product.Name (before putting them into the data-set; c.f. line 65 in Program.cs) do not render properly inside the “TableStart:YellowProducts”-region.

  • Yes, I know, that I could have simply formatted the first columns of the tables inside the word-template to be bold, but that’s besides the point, since the actual use-case is quite a bit more complicated than that and it requires me to be able to conditionally use the bold-format (which works flawlessly as long as all the tables do contains any product/content).
  • Then again: If this issue resolves itself by not having issue 1), I’m totally fine with that.

Please find attached a demo-solution including a WordTemplate.docx-file, which is the template for the MailMerge and a Sample_WordResult.docx-file, which I created by (and renamed after) running the console-application. Note: You probably need to modify 3 file-paths, that I’ve declared at the top of Program.cs for the app to run properly.
Aspose_Mergefields_MultipleTables.zip (43.4 KB)

Thanks in advance for any information you can provide on these issues.

Kind regards,
Matthias Heinz

@M.Heinz The problem sits in the IFieldMergingCallback implementation. You are using DocumentBuilder.MoveToMergeField method passing merge field name into the method. But since multiple regions have the same field names DocumentBuilder simply moves to the first occurrence of the field with specified name. In your case you should use DocumentBuilder.MoveToField method instead. Please see the following modified code:

void IFieldMergingCallback.FieldMerging(FieldMergingArgs args)
{
    if (args.FieldValue != null)
    {
        var mField = args.Field as Aspose.Words.Fields.FieldMergeField;
        var textBefore = mField.TextBefore;
        var textAfter = mField.TextAfter;

        DocumentBuilder db = new DocumentBuilder(args.Document);
        // Move to the field and remove it since we will use DocumentBuilder to insert the value.
        db.MoveToField(args.Field, true);
        args.Field.Remove();

        var value = args.FieldValue.ToString();
        if (!String.IsNullOrEmpty(value) && (!String.IsNullOrEmpty(textBefore) || !String.IsNullOrEmpty(textAfter)))
            value = String.Format("{0}{1}{2}", textBefore, value, textAfter);

        if (value.Contains("</"))
            db.InsertHtml(value.Replace("\r", "").Replace("\n", ""), true);
        else
            db.Write(value);
    }
}

Thanks for your quick and precise response. This fix does indeed resolve all of my issues.

Hi @alexey.noskov,

I’ve got one more question regarding the position of the call to “args.Field.Remove();”. When this line is positioned like in your example, there appears to happen a crash with some word-documents and not others…

I’ve been handed about 5 documents each reproducing/not reproducing this crash - which I unfortunately am not allowed to share. Besides some of them containing TextBefore/TextAfter, I cannot find any relevant differences; on the other hand, the working and the crashing ones do contain samples indluding and excluding those bonus texts before/after… So unfortunately I cannot narrow it down for you.

Stacktrace WebUI
Exception of type 'System.Web.HttpUnhandledException' was thrown.
at System.Web.UI.Page.HandleError(Exception e)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest()
at System.Web.UI.Page.ProcessRequest(HttpContext context)
at ASP.default_aspx.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
System.NullReferenceException: Object reference not set to an instance of an object.
at Aspose.Words.DocumentBuilder.6b82ec9lndgnvy8yve7f2ezrj4y8lxf8​ (Int32 )
at    .(    , Int32 )
at Aspose.Words.DocumentBuilder.(String )
at Aspose.Words.DocumentBuilder.(String )
at Aspose.Words.DocumentBuilder.(String , Boolean )
at [MyProject].HandleSpecialMergeFields.Aspose.Words.MailMerging.IFieldMergingCallback.FieldMerging(FieldMergingArgs args) in [MyProject]
at Aspose.Words.MailMerging.MailMerge.(FieldMergingArgs )
at    .(FieldMergeField , FieldMergingArgs& )
at Aspose.Words.Fields.FieldMergeField.(    , MailMerge )
at Aspose.Words.Fields.FieldMergeField.86q6acmbfp2kjwdcs2q6rs3uwmqt2zc9​  ()
at  ​ .(Field )
at  ​ . ( ​  )
at  ​ .( ​  )
at  ​ .( ​  )
at  ​ .( ​  ,  ​  )
at  ​ .(IList`1 )
at    .(IList`1 ,  ​  )
at    . ()
at    .(​   , ​  [] , Boolean )
at Aspose.Words.MailMerging.MailMerge.(    )
at [MyProject]
at [MyProject]
at [MyProject]
at System.Web.UI.WebControls.Button.OnClick(EventArgs e)
at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.HandleError(Exception e)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest()
at System.Web.UI.Page.ProcessRequest(HttpContext context)
at ASP.default_aspx.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Details when debugging
System.NullReferenceException: 'Object reference not set to an instance of an object.'

System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=Aspose.Words
  StackTrace:
   at Aspose.Words.DocumentBuilder.6b82ec9lndgnvy8yve7f2ezrj4y8lxf8​ (Int32 )

  This exception was originally thrown at this call stack:
    Aspose.Words.DocumentBuilder.   .6b82ec9lndgnvy8yve7f2ezrj4y8lxf8​ (int)

However, if I move the line “args.Field.Remove();” just outside the last “else”-block, then I don’t experience any crash.

Is there any risk by moving this line to the point after the insertion of the content and/or is there any bug on your end?

Kind regards,
Matthias Heinz

@M.Heinz it is safe to move args.Field.Remove() to the end of the if (args.FieldValue != null) condition. In the code DocumentBuilder’s cursor is placed after the field, so content you are inserting also is placed after the field.

Perfect - and thanks again for your quick response.