Mail Merge for Parent Child objects

Hello there,

I am having a doubt wrt Mail merge using an Object collection that has some hierarchical data.

Assuming I have a parent object called Property with its child object as Buyers.
Also, I have a template that has placeholders/merge fields for both Property attrributes as well as its child: Buyers’ attributes.

How should I execute merge feature in this case?

Will it be a good approach to execute mail merge twice as:

//For Parent Object
PropertyDataSource mmPropertyDataSource = new PropertyDataSource(new List());
objTemplateDocument.MailMerge.Execute(mmPropertyDataSource);

//For Child Object
PropertyBuyersDataSource mmPropertyBuyersDataSource = new PropertyBuyersDataSource(new List());
objTemplateDocument.MailMerge.Execute(mmPropertyBuyersDataSource);

Could you please suggest any other efficient approach?

Thank you!

Hi

Thanks for your request. No, your code is not correct, if you need to fill the document with data, which has parent-child relationships. In this case, you should use IMailMergeDataSource.GetChildDataSource.

http://www.aspose.com/documentation/.net-components/aspose.words-for-.net-and-java/aspose.words.reporting.imailmergedatasource.getchilddatasource.html

For example, see the following code:

List properties= new List();

for(int i=0; i<10; i++)

{

Property prop = new Property(string.Format(“property_{0}”, i));

// Add few buyers.

for(int j=0; j<5; j++)

{

prop.Buyers.Add(new Buyer(string.Format(“firstName_{0}-{1}”, i, j),

string.Format(“lastName_{0}-{1}”, i, j)));

}

properties.Add(prop);

}

// Create datasource.

PropertiesDataSource ds = new PropertiesDataSource(properties);

// Open template.

Document doc = new Document(@“Test001\in.doc”);

// Execute mail merge with regions.

doc.MailMerge.ExecuteWithRegions(ds);

// Save output document.

doc.Save(@“Test001\out.doc”);

====================================================================

///

/// Properties datasource.

///

private class PropertiesDataSource : IMailMergeDataSource

{

public PropertiesDataSource(List properties)

{

mProperties = properties;

// When the data source is initialized, it must be positioned before the first record.

mRecordIndex = -1;

}

public bool MoveNext()

{

if (!IsEof)

mRecordIndex++;

return (!IsEof);

}

public bool GetValue(string fieldName, out object fieldValue)

{

switch (fieldName)

{

case “Name”:

fieldValue = mProperties[mRecordIndex].Name;

return true;

default:

fieldValue = null;

return false;

}

}

public IMailMergeDataSource GetChildDataSource(string tableName)

{

switch (tableName)

{

case “Buyers”:

return new BuyersDataSource(mProperties[mRecordIndex].Buyers);

default:

return null;

}

}

public string TableName

{

get { return “Properties”; }

}

private bool IsEof

{

get { return (mRecordIndex >= mProperties.Count); }

}

private int mRecordIndex;

private List mProperties;

}

///

/// Buyers datasource.

///

private class BuyersDataSource : IMailMergeDataSource

{

public BuyersDataSource(List buyers)

{

mBuyers = buyers;

// When the data source is initialized, it must be positioned before the first record.

mRecordIndex = -1;

}

public bool MoveNext()

{

if (!IsEof)

mRecordIndex++;

return (!IsEof);

}

public bool GetValue(string fieldName, out object fieldValue)

{

switch (fieldName)

{

case “FirstName”:

fieldValue = mBuyers[mRecordIndex].FirstName;

return true;

case “LastName”:

fieldValue = mBuyers[mRecordIndex].LastName;

return true;

default:

fieldValue = null;

return false;

}

}

public IMailMergeDataSource GetChildDataSource(string tableName)

{

return null;

}

public string TableName

{

get { return “Buyers”; }

}

private bool IsEof

{

get { return (mRecordIndex >= mBuyers.Count); }

}

private int mRecordIndex;

private List mBuyers;

}

=============================================================================

// These are dummy classes.

private class Property

{

public Property(string name)

{

mName = name;

mBuyers = new List();

}

public string Name

{

get { return mName; }

}

public List Buyers

{

get { return mBuyers; }

}

private string mName;

private List mBuyers;

}

private class Buyer

{

public Buyer(string firstName, string lastName)

{

mFirstName = firstName;

mLastName = lastName;

}

public string FirstName

{

get { return mFirstName; }

}

public string LastName

{

get { return mLastName; }

}

private string mFirstName;

private string mLastName;

}

Hope this helps.

Best regards.

Hello Alexey,

Thanks for your reply, but above code is not working fine for me as I do not have any regions defined in source document. (I do feel that I could have explained the scenerio a little more in my first post.)

My requirement is that source document is dotx/docx file without any regions which acts as a template.
Now when doing mail merge, I want to create different documents for each buyer in a property.

So essentially if I have say 2 properties, one with m no. of buyers and other with n no. of buyers.
So my resultant mail merge document will have m+n no. of pages, considering the template is of only one page.

It’s like a scenerio where I want to send letter to all prospective buyers for a property.
So, I don’t think it will fit in ExecuteWithRegions().

Please guide.

Thank you!

PS: I have attached a sample template. Placeholders starting with ‘A’ are for Buyer and those with ‘P’ are for Property

Hi

Thank you for additional information. In this case, I think, you can use the technique, I suggested you in the following thread:

In your case, the first object is Property and the second object is Buyer.

Best regards.

Is the Code above still valid?

I tried using it, changing ExecuteWithRegions to Execute but the GetChildDataSource is never get called and is therefore empty.

My docx as well as the progamm.cs is attached.

any ideas?

Hi,

Thanks for your inquiry. the problem occurs because there is no nested mail merge region in your template document. The Aspose.Words mail merge engine invokes IMailMergeDataSource.GetChildDataSource method when it encounters a beginning of a nested mail merge region.

When the Aspose.Words mail merge engines populates a mail merge region with data and encounters the beginning of a nested mail merge region in the form of MERGEFIELD TableStart:TableName, it invokes GetChildDataSource on the current data source object. Your implementation needs to return a new data source object that will provide access to the child records of the current parent record. Aspose.Words will use the returned data source to populate the nested mail merge region.

Below are the rules that the implementation of GetChildDataSource must follow.

If the table that is represented by this data source object has a related child (detail) table with the specified name, then your implementation needs to return a new IMailMergeDataSource object that will provide access to the child records of the current record. An example of this is Orders / OrderDetails relationship. Let’s assume that the current IMailMergeDataSource object represents the Orders table and it has a current order record. Next, Aspose.Words encounters “MERGEFIELD TableStart:OrderDetails” in the document and invokes GetChildDataSource. You need to create and return a IMailMergeDataSource object that will allow Aspose.Words to access the OrderDetails record for the current order.

If this data source object does not have a relation to the table with the specified name, then you need to return a IMailMergeDataSource object that will provide access to all records of the specified table.

If a table with the specified name does not exist, your implementation should return null.

I hope, this helps.

Best regards,