Aspose Linq Reporting

Dear Sir/Madame,

I cannot find a single example of using Linq Reporting with a custom implementation of IDataReader and IDataRecord. Specifically, I would like an example that followed the skeleton:

public class MyDataReader : IDataReader
{
  // What goes here?
}


public class MyDataRecord : IDataRecord
{
  // What goes here?
  // MyDataRecord should point to instances of MyDataReader where
  //     repeating groups are required. And, recursively, the MyDataReader
  //     records (which should be of type MyDataRecord) should be able
  //     to point to more deeply nested MyDataReaders.


  // Of course, all of this could be avoided if there were callbacks such as
  //  you have implemented for Merge (e.g. IMailMergeDataSource,
  //   IMailMergeDataSourceRoot, and IFieldMergingCallback)
}


public class MyTestClass
{
  public void TestMethod()
  {

    Document doc = new Document("asp-top-rep.docx");
    doc.MailMerge.FieldMergingCallback = new HandleFieldMerge();
    ReportingEngine re = new ReportingEngine();


    MyDataRecord ds = new MyDataRecord();
    re.BuildReport(doc, ds, "ds");
            
    doc.Save("asp-top-rep-out.docx");
  }
}

The associated template should support a table with a repeating group, such as:
*******Start of Test Template

This is the aspose reporting test document.

Name:<<[ds.FirstName]>> <<[ds.LastName]>>

A table will go under here.

Header 1 Header 2 Header 3
<<foreach [t in ds.TestEnum]>><<[t.ColFieldOne]>> <<[t.ColFieldTwo]>> <<[t.ColFieldThree]>><</foreach>>

*******End of Test Template

The result of running the code above should be population of the test fields. I have gotten the example to work for the simple cases, ds.FirstName and ds.LastName. However, I cannot get a repeating group to work. The proper construction of IDataReader and IDataRecord implementations to suit your requirements is a mystery, and I cannot find any documentation. I have tried numerous variations (guesses), but this approach is terribly counterproductive. It would be far more useful if you could just provide sample code to satisfy your use case.

Thank you.

Hi Joshua,

Thanks for your inquiry. Please read about IDataReader and IDataRecord interfaces from following msdn links.
https://learn.microsoft.com/en-us/dotnet/api/system.data.idatareader?redirectedfrom=MSDN&view=net-7.0
https://learn.microsoft.com/en-us/dotnet/api/system.data.idatarecord?redirectedfrom=MSDN&view=net-7.0

Working with IDataReader Implementors

LINQ Reporting Engine enables you to treat IDataReader implementors as enumerations of IDataRecord implementors in template expressions. That is, you can use IDataReader implementors in template expressions in the same way as DataTable objects. See “Working with DataTable” for more information.

However, you can not use IDataReader implementors in template expressions in conjunction with enumeration operations that require a caching of enumeration items. The examples of such operations are grouping and sorting. To work around this restriction, use DataTable objects instead.

Working with IDataRecord Implementors

LINQ Reporting Engine enables you to access field values of a particular IDataRecord implementor by field names using the “.” operator in template expressions. To use this feature, one of the following conditions must be met:

  • The IDataRecord implementor represents an iteration variable upon enumerating an IDataReader implementor (see “Outputting Sequential Data” for more information).
  • The IDataRecord implementor does not implement the IDataReader interface.

The following example shows, how to use this feature. Given that r is an IDataRecord implementor that has a field named “Name”, you can access the field’s value using the following syntax.

r.Name

Could you please share some more detail about your test case? Perhaps, there is another way to achieve what you need. We will then provide you more information about your query.

I need to know if it is possible to use your Linq Reporting in a similar fashion to your normal MailMerge. I can use IMailMergeDataSourceRoot, IMailMergeDataSource, and IFieldMergingCallback with MailMerge. I know I can’t use these with Ling Reporting.
However, those interfaces were designed to serve a purpose – to allow your merge to ping my data source at the last possible second, dynamically (not requiring me to have a static, reflectable, pre-populated data source). I want to do the same thing with Linq Reporting, but those interfaces are not available. I got it partially working with IDataRecord and IDataReader, but Linq Reporting chokes, and throws a null-pointer exception. I need to see sample code using LinqReporting together with IDataRecord and IDataReader. I want to dynamically build my IDataRecord and IDataReader implementations just before BuildReport(). I want to build them such that I can return each merge field as it is needed (like when using IMailMergeDataSourceRoor, IMailMergeDataSource, and IFieldMergingCallback

Hi Joshua,

Please note that the following example (involving custom types) allows to achieve exactly what you want i.e. recursively and lazily access data having dynamic structure:

Document document = new Document(MyDir + @"template2.docx");
ReportingEngine engine = new ReportingEngine();
engine.BuildReport(document, new DynamicEntity(Guid.NewGuid()), "root");
document.Save(MyDir + @"result2.docx");
public interface IPropertyProvider
{
    T this[string propertyName] { get; }
}
public class DynamicEntity : IPropertyProvider<string>
{
    /// 
    /// Gets a property value by its name.
    /// 
    public string this[string propertyName]
    {
        get
        {
            // In this example, we simply return a property name as its value.
            // In a real-life application, a real property value should be returned.
            // This value can be cached using for example a Dictionary, or fetched
            // every time the property is requested.
            return propertyName + " Value";
        }
    }
    /// 
    /// Provides access to individual related instances 
    /// by their names.
    /// 
    public IPropertyProvider<DynamicEntity> Entities
    {
        get { return mEntities; }
    }
    /// 
    /// Provides access to enumerations of related instances 
    /// by their names.
    /// 
    public IPropertyProvider<IEnumerable<DynamicEntity>> Children
    {
        get { return mChildren; }
    }
    private class ReferencedEntities : IPropertyProvider<DynamicEntity>
    {
        public DynamicEntity this[string propertyName]
        {
            get
            {
                // In this example, we simply return the root entity.
                // In a real-life application, a DynamicEntity instance corresponding 
                // to propertyName for the given root entity should be returned.
                // This instance can be cached using for example a Dictionary, 
                // or fetched every time the referenced entity is requested.
                return mRootEntity;
            }
        }
        internal ReferencedEntities(DynamicEntity rootEntity)
        {
            // The reference to the root entity allows to access fields of the root entity
            // (such as an identifier) in the above indexer for a real-life application.
            mRootEntity = rootEntity;
        }
        private readonly DynamicEntity mRootEntity;
    }
    private class ChildEntities : IPropertyProvider<IEnumerable<DynamicEntity>>
    {
        public IEnumerable<DynamicEntity> this[string propertyName]
        {
            get
            {
                // In this example, we simply return the root entity three times.
                // In a real-life application, an enumeration of DynamicEntity instances 
                // corresponding to propertyName for the given root entity should be returned.
                // This enumeration can be cached using for example a Dictionary, 
                // or fetched every time the child entities are requested.
                yield return mRootEntity;
                yield return mRootEntity;
                yield return mRootEntity;
            }
        }
        internal ChildEntities(DynamicEntity rootEntity)
        {
            // The reference to the root entity allows to access fields of the root entity
            // (such as an identifier) in the above indexer for a real-life application.
            mRootEntity = rootEntity;
        }
        private readonly DynamicEntity mRootEntity;
    }
    internal DynamicEntity(Guid id)
    {
        // In this example, we use Guid to represent an entity identifier.
        // In a real-life application, the identifier can be of any type or even missing.
        mId = id;
        // In this example, we simply initialize fields in the constructor.
        // In a real-life application, these fields can be initialized lazily
        // at the corresponding properties, if needed.
        mEntities = new ReferencedEntities(this);
        mChildren = new ChildEntities(this);
    }
    private readonly Guid mId;
    private readonly ReferencedEntities mEntities;
    private readonly ChildEntities mChildren;
}

The input/output documents “Template2.docx” and “result2.docx” which are used in this example are attached with this post.

Hope, this helps.

Best regards,

Awais,

Thank you very much for your excellent reply. I really appreciate your effort in producing such a thoughtful answer. I will continue working on my prototype and I hope I can receive such thorough replies in the future. You have saved me countless hours of experimentation.

Thank you,
Josh

Hello,

In your Example #1 implementing IDataReader above, how are children of ExampleEntity supposed to be returned if they are implemented? For example, if ExampleEntity had an array property “public ExampleEntityChildren[] Children { get; }”, how would these children be returned using the IDataReader interface so that they could be referenced in a <> construct in the associated Word template? This also relates to another open thread I created: Aspose.Words Linq Reporting - Using IDataRecord and IDataReader with hierarchical data

Hi Josh,

Thanks for your inquiry. Please follow your other thread for further proceedings.

Best regards,