I am working on a reporting service in which mail merge with Aspose is a major functionality.
Because I have a no. of custom objects against which mail merge(Execute(), ExecuteWithRegions()) is run, IMailMergeDataSource needs to be implemented for each custom object that I have.
In future, more custom objects will be added to the list.o, I was wondering if there is a generic way of getting MailMergeDataSource for my custom objects so that I don’t need to implement IMailMergeDataSource for each custom object?
Thanks for yu rreqeust. Sure, you can use reflection to achieve this. Please see the following code for example:
///
/// A custom mail merge data source that you implement to allow Aspose.Words
/// to mail merge data from LINQ query results into Microsoft Word documents.
///
public class MailMergeDataSource: IMailMergeDataSource
{
///
/// Creates new instance of a custom mail merge data source
///
/// Data returned from a LINQ query
public MailMergeDataSource(IEnumerable data)
{
mEnumerator = data.GetEnumerator();
}
///
/// Creates new instance of a custom mail merge data source
///
/// Data returned from a LINQ query
/// Name of the data source is only used when you perform mail merge with regions.
/// If you would like to use simple mail merge then use constructor with one parameter.
public MailMergeDataSource(IEnumerable data, string tableName)
{
mEnumerator = data.GetEnumerator();
// Name of the data source is needed when you perform mail merge with regions
mTableName = tableName;
}
///
/// Aspose.Words call this to get a value for every data field.
///
public bool GetValue(string fieldName, out object fieldValue)
{
// Get type of current record
Type curentRecordType = mCurrentObject.GetType();
// Use reflection to get property by name and its value
PropertyInfo property = curentRecordType.GetProperty(fieldName);
if (property != null)
{
fieldValue = property.GetValue(mCurrentObject, null).ToString();
return true;
}
else
{
// A field with this name was not found,
// return false to the Aspose.Words mail merge engine.
fieldValue = null;
return false;
}
}
///
/// Moves to the next record in the collection.
///
public bool MoveNext()
{
// Move enumerator to next record
bool hasNexeRecord = mEnumerator.MoveNext();
if (hasNexeRecord)
{
mCurrentObject = mEnumerator.Current;
}
return hasNexeRecord;
}
///
/// The name of the data source. Used by Aspose.Words only when executing mail merge with repeatable regions.
///
public string TableName
{
get
{
return mTableName;
}
}
private IEnumerator mEnumerator;
private object mCurrentObject;
private string mTableName = string.Empty;
}
One more query in addition to above -
The object i use for Mail Merge has further child objects, I need to map merge fields for these child object properties as well.
For exp. ‘Client’ object has child object ‘Address’.
If I want to access StreetNumber/PostCode or any other address related info., I need to access it through objClient.Address.PostCode and so on.
FYI, In source document, I have merge fields like ‘APostCode’, ‘AStreetNumber’ etc.
Could you please guide how can I map fields for such properties?
Thanks for your inquiry. I think, in this case, you should have set of rules where engine should look for the appropriate property. But the main idea is the same is I described.
As I already mentioned above, you should use MappedDataFields to map between merge field names in the template and names in your datasource.
Best regards,