XML-based Crystal Report not updating child object

2019-07-27 22:49发布

I am working on an application that requires several types of Crystal Reports, "standard" ones that hit a database table, update connection string and refresh the report, done. The other type of Crystal Report is based on a regular (slightly complex) class object, or POCO object.

The problem I'm facing is that the datasource of the report is based on a class we've created, it has properties that are also classes we've created. When I refresh the report, the data in the object is updated, but the data in the child property objects are not updated. They remain as the values set when I created the report.

A bit of background, the environment is C# on VS2010 with Crystal Reports 2011.

In order to create the report, I created an object and populated every property with relevant data, including child objects, I then exported the object to XML and outputted it to a file. I then created a new report and added a datasource of the type "ADO.NET (XML)".

All the "tables" appeared fine and I was able to make and add links, design and preview the report as you would normally.

When it came to runtime testing, I started with the code from this StackOverflow question:

.NET - Convert Generic Collection to DataTable

in order to convert the list of my objects into a DataTable and assign it as a datasource for the report. As mentioned previously, it works for the first level, but not child properties.

I've modified that code to create a new DataTable when a child property is one of our created classes and not simply a CLR data type, but that just leaves me with an empty report now. That code is below:

public static class CollectionExtensions
{
    /// <summary>Converts to.</summary>
    /// <typeparam name="T">The type value</typeparam>
    /// <param name="list">The list value.</param>
    /// <returns>The data table.</returns>
    public static DataTable ConvertTo<T>(this IList<T> list)
    {
        var entityType = typeof(T);
        var table = CreateTable(entityType);

        foreach (var item in list)
        {
            var row = ConvertToRow(table, item, entityType);
            table.Rows.Add(row);
        }

        return table;
    }

    /// <summary>
    /// Converts to table.
    /// </summary>
    /// <param name="table">The table.</param>
    /// <param name="item">The item value.</param>
    /// <param name="type">The type value.</param>
    /// <returns>returns a data table</returns>
    public static DataRow ConvertToRow(DataTable table, object item, Type type)
    {
        var properties = TypeDescriptor.GetProperties(type);

        var row = table.NewRow();

        foreach (PropertyDescriptor prop in properties)
        {
            if (prop.PropertyType.IsAssignableFrom(typeof(AbstractEntity)))
            {
                var subTable = CreateTable(prop.PropertyType);

                if (prop.GetValue(item) != null)
                {
                    var subRow = ConvertToRow(subTable, prop.GetValue(item), prop.PropertyType);
                    subTable.Rows.Add(subRow);
                }

                row[prop.Name] = subTable;
            }
            else
            {
                row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
            }
        }

        return row;
    }

    /// <summary> Creates the table. </summary>
    /// <param name="type">The type value.</param>
    /// <returns>The datatable</returns>
    public static DataTable CreateTable(Type type)
    {
        var table = new DataTable(type.Name);
        var properties = TypeDescriptor.GetProperties(type);

        foreach (PropertyDescriptor prop in properties)
        {
            if (prop.PropertyType.IsAssignableFrom(typeof(AbstractEntity)))
            {
                table.Columns.Add(prop.Name, typeof(DataTable));
            }
            else
            {
                table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            }
        }

        return table;
    }
}

I think the problem is that I've got a disconnect between how I'm creating the report and how the data is being applied at runtime when the datasource is being updated.

But never having done an XML-based Crystal Report before, I'm not exactly sure how to resolve this. My questions to the experts out there are:

  1. Am I on the right track, for creating the report in the first place?
  2. Given how I'm creating the report, am I on the right track for how I'm updating the report?
  3. Is there a better way to achieve this same result? For a multi-level object being the datasource for an XML-based Crystal Report.

1条回答
Bombasti
2楼-- · 2019-07-27 23:01

The solution was to serialize the object to a MemoryStream and to deserialize it back into a DataSet and the Crystal Report was able to update all tables fine (that I've tested so far).

I used the following code in my test harness application and this resolved the issue:

        var xmlDocument = new XmlDocument();
        var serializer = new XmlSerializer(typeof(MyObjectClass));
        using (var stream = new MemoryStream())
        {
            serializer.Serialize(stream, myObjectInstance);
            stream.Flush();
            stream.Seek(0, SeekOrigin.Begin);
            xmlDocument.Load(stream);
        }

        var data = new DataSet();
        var context = new XmlParserContext(null, new XmlNamespaceManager(new NameTable()), null, XmlSpace.None);
        var reader = new XmlTextReader(xmlDocument.OuterXml, XmlNodeType.Document, context);
        data.ReadXml(reader);

        var report = new ReportDocument();
        report.Load(@"C:\Reports\TestReport.rpt", OpenReportMethod.OpenReportByTempCopy);

        report.SetDataSource(data);

        this.crystalReportsViewer.ViewerCore.ReportSource = report;

I hope it helps someone else out there.

查看更多
登录 后发表回答