Mail Merge with mutiple tables from different datasources having same properties

Hi,

I was planning to use the Mail Merge from Aspose.Words. I have referred the below link:

https://docs.aspose.com/words/java/nested-mail-merge-with-regions/

What I understood was, the Merge Fields like «Price» or «Quantity» or etc. are binding from the given data source. And this was done by the below statement:

doc.MailMerge.ExecuteWithRegions(pizzaDs); ==> What this will do is, it fills the Price and Quantity to word document wherever «Price» or «Quantity» are available.

Now, I have two data sources like pizzaDs and pizzaDs1; And two merge field tables in word document; I wanted to fill Table1 with pizzaDs and Table2 with pizzaDs1.

Note: pizzaDs and pizzaDs1 both are having similar columns, but pizzaDs1 having few more extra columns.

Please suggest…

Hi Srinu,

Thanks for your inquiry. The doc.MailMerge.ExecuteWithRegions method operates only on a specific portion of your document defined by two special fields i.e. TableStart and TableEnd fields:
https://docs.aspose.com/words/net/types-of-mail-merge-operations/

If your document has two separate mail merge regions, no matter they contain fields with same name or not, you need to call doc.MailMerge.ExecuteWithRegions method twice i.e. one for pizzaDs and second time for pizzaDs1. Alternatively, if you have two DataTables in a DataSet, Aspose.Words will accommodate both mail merge regions in a single doc.MailMerge.ExecuteWithRegions method call. I hope, this helps.

Best regards,

Hi hafeez,

Thanks for your response. I did make it working for multiple data sources. But I’m facing an issue on doing MailMerge where one class is having the reference to another. Here is an example:

class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime Date { get; set; }
    public **Location** Loc { get; set; }
}

public class **Location**
{
    public int Left { get; set; }
    public int Right { get; set; }
}
Id Name Location
«TableStart:MultipleRecords» «Id» «Name» «Left»
«TableEnd:MultipleRecords»

And filling the mail merge fields using below snippet:

var source = new MyMailMergeDataSource(list, "MultipleRecords"); // list has 10 records
doc.MailMerge.ExecuteWithRegions(source);

But unfortunately the «Left» field from last column is not binding. Can you help me out on binding the last column values which are coming from the another class Location?

Hi Srinu,

Thanks for your inquiry. To ensure a timely and accurate response, please attach the following resources here for testing:

  • Your input Word document.
  • Aspose.Words generated output document (.docx file) which shows the undesired behavior.
  • Please create a standalone (runnable) console application that helps us reproduce your problem on our end and attach it here for testing.

As soon as you get these pieces of information ready, we’ll start investigation into your issues and provide you more information.

Best regards,

Hi,

Here is the sample attached example with input and output files. Could you please provide your solution ASAP as we need it urgently.

You can observe in output.docx file, the fields from last two columns (Left column and Right column) are not loaded with data.

Thanks,

Hi Srinu,

Thanks for the additional information. You can change the code like below to achieve this:

public bool GetValue(string fieldName, out object fieldValue)
{
    object obj = mEnumerator.Current;
    Type curentRecordType = obj.GetType();
    PropertyInfo property;
    if (fieldName == "Left" || fieldName == "Right")
    {
        property = curentRecordType.GetProperty("Loc");
        if (fieldName == "Left")
            fieldValue = ((Location)property.GetValue(obj, null)).Left;
        else
            fieldValue = ((Location)property.GetValue(obj, null)).Right;
        return true;
    }
    else
    {
        property = curentRecordType.GetProperty(fieldName);
        if (property != null)
        {
            fieldValue = property.GetValue(obj, null);
            return true;
        }
    }
    fieldValue = null;
    return false;
}

I hope, this helps.

Best regards,

Hi Hafeez,

Thanks for your response. I understood, but there are few questions on this:

if (fieldName == "Left" || fieldName == "Right")
{
    property = curentRecordType.GetProperty("Loc");
    if (fieldName == "Left")
        fieldValue = ((Location)property.GetValue(obj, null)).Left;
    else
        fieldValue = ((Location)property.GetValue(obj, null)).Right;
    return true;
}
  • The above code is a static. I mean the above code will work only if I do mail merge using Employee objects. I have huge number of classes in my entity framework and entities are related with each other based on relationships between database tables. Ex: Employee is related to Location, and Location is further related to Department, and Department is further related to Finance, and soon. what is the best way to do mail merge from Employee to last entity?

Thanks,

Hi,

Thanks for your inquiry. Please do the following changes in your code to achieve this:

public class MyMailMergeDataSource : IMailMergeDataSource
{
    private readonly IEnumerator mEnumerator;
    private readonly string mTableName;
    public MyMailMergeDataSource(IEnumerable data)
    {
        mEnumerator = data.GetEnumerator();
    }
    public MyMailMergeDataSource(IEnumerable data, string tableName)
    {
        mEnumerator = data.GetEnumerator();
        mTableName = tableName;
    }
    public object GetFieldValue(object obj, string fieldName)
    {
        foreach (PropertyInfo prop in obj.GetType().GetProperties())
        {
            if (prop.Name.Equals(fieldName))
            {
                return prop.GetValue(obj, null);
            }
            object value = prop.GetValue(obj, null);
            Type type = value.GetType();
            if (value != null && !(type.IsPrimitive || type.Equals(typeof(string)) || type.Equals(typeof(DateTime))))
            {
                return GetFieldValue(value, fieldName);
            }
        }
        return null;
    }
    public bool GetValue(string fieldName, out object fieldValue)
    {
        object obj = mEnumerator.Current;
        fieldValue = GetFieldValue(obj, fieldName);
        return (fieldValue != null) ? true : false;
    }
    public bool MoveNext()
    {
        return mEnumerator.MoveNext();
    }
    public string TableName
    {
        get { return mTableName; }
    }
    public IMailMergeDataSource GetChildDataSource(string tableName)
    {
        return null;
    }
}

I hope, this helps.

Best regards,

Hi Hafeez,

Thanks for your response. This works pretty well and thanks for that. But technically there is an issue on configuring Mail Merge fields in word template. I have attached the example here:

In the attached sample code, the property Name is available in both Department and Employee classes. How do I create the Merge fields in my Input.docx which should have both Employee name and Department Name in the same table?

Hi Srinu,

Thanks for your inquiry. I think, in your template document, you can change the name of Employee’s Name merge field as “Employee.Name” and name of Department’s Name merge field as “Department.Name” (please see attached modified template). After that, please try executing the following code:

...
...
public object GetFieldValue(object obj, string fieldName)
{
    foreach (PropertyInfo prop in obj.GetType().GetProperties())
    {
        if (fieldName.Contains(".") && (obj.GetType().Name + "." + prop.Name).Equals(fieldName))
            return prop.GetValue(obj, null);
        if (prop.Name.Equals(fieldName))
            return prop.GetValue(obj, null);
        object value = prop.GetValue(obj, null);
        Type type = value.GetType();
        if (value != null && !(type.IsPrimitive || type.Equals(typeof(string)) || type.Equals(typeof(DateTime))))
        {
            return GetFieldValue(value, fieldName);
        }
    }
    return null;
}
public bool GetValue(string fieldName, out object fieldValue)
{
    object obj = mEnumerator.Current;
    fieldValue = GetFieldValue(obj, fieldName);
    return (fieldValue != null) ? true : false;
}
...
...

I hope, this helps.

Best regards,

Hi Hafeez,

Thanks a lot! You Aspose guys are giving great support.

Once again, thanks for your support.