How to Union or Concat self made structure

Hi,
I have a problem because I can’t execute an UNION (or a Concat function) between 2 generic collection (System.Collections.Generic.IEnumerable`1[Cj]).

I have to create a unique structure with 3 static elements (volEntree, volSortie, volBypass) and a dynamic list of elements issus from : Query.GetDescendants(Entity,“Weir”).
The structure must contains a name and a value and then, with this structure, I want to build a bar chart like this :

I need to hold the elements in a unique structure in order to keep the proportionality on the chart.

So, I build this code (GetScalarIndicatorData and GetDataSamples return Time Series : Time and Value) :

<<var [volEntree = ScalarIndicatoDataSource.GetScalarIndicatorData("ComputeWasteWaterTreatmentPlantIncomingVolume","1.00:00:00")]>>

<<var [volSortie = ScalarIndicatoDataSource.GetScalarIndicatorData("ComputeWasteWaterTreatmentPlantTreatedVolume","1.00:00:00")]>>

<<var [volBypass = ScalarIndicatoDataSource.GetScalarIndicatorData("ComputeWasteWaterTreatmentPlantBypassVolume","1.00:00:00")]>>

<<var [categoriesBarGraphVolumes = new string[] { “Volume en entrée”, “Volume en sortie”, “Volume By-pass” }]>>

<<var [valuesBarGraphVolumes = new int[] {(int)volEntree.Sum(x => x.Value), (int)volSortie.Sum(x => x.Value), (int) volBypass.Sum(x => x.Value)}]>>

<<var [seq = new int[] { 0, 1, 2 }]>>

<<var [barGraphVolumes1 = seq.Select(i => new { nomSerie = categoriesBarGraphVolumes.Skip(i).First(), valSerie = valuesBarGraphVolumes.Skip(i).First() })]>>

<<var [barGraphVolumes2 = Query.GetDescendants(Entity,"Weir").Select(do => new { nomSerie = do[“Name”], valSerie = Query.GetDataSamples(do,"Flow.HourlyVolumeTimeSeries").Sum(x => x?.Value)})]>>

<<var [barGraphVolumes = barGraphVolumes1.Union(barGraphVolumes2)]>>

Do you have any ideas on how I could get around this or how I could manage to do this?
Thank you.

@deid

Hello,

Thank you for reaching out. Your request has been received and will be processed soon.

If you have any questions specifically about Aspose libraries (e.g., working with Word, PDF, Excel, PowerPoint, Email, OCR, etc.), please let us know and we’ll be happy to assist.

Best regards,
Aspose Support Team

I build this in a Word Template and I can work only at template level.

@deid

Union / Concat of a Custom Structure in a Word template (LINQ Reporting Engine)

The LINQ Reporting Engine works only on the data that you pass to the template.
If you need to combine two collections (or a collection with a single object) you have to do the union/concat in your code before you hand the data to the engine.

Below is a concise, step‑by‑step example that shows how to:

  1. Build two custom collections.
  2. Use Concat / Union (or AddRange) to merge them.
  3. Pass the merged collection to the template.
  4. Use the standard <#= #> tags in the Word file to iterate over the result.

:one: Define the custom structure

public class Employee
{
    public int    Id   { get; set; }
    public string Name { get; set; }
    public string Dept { get; set; }
}

:two: Create the source collections

var permanentStaff = new List<Employee>
{
    new Employee { Id = 1, Name = "John", Dept = "Finance" },
    new Employee { Id = 2, Name = "Anna", Dept = "HR" }
};

var temporaryStaff = new List<Employee>
{
    new Employee { Id = 3, Name = "Mike", Dept = "IT" },
    new Employee { Id = 4, Name = "Sara", Dept = "Marketing" }
};

:three: Merge the collections

// Option 1 – simple concatenation (preserves order, duplicates allowed)
IEnumerable<Employee> allStaff = permanentStaff.Concat(temporaryStaff);

// Option 2 – union (removes duplicates based on the default comparer)
// For a proper union you may need to implement IEqualityComparer<Employee>
IEnumerable<Employee> allStaff = permanentStaff.Union(temporaryStaff);

// Option 3 – add temporary items to the first list (mutates the list)
permanentStaff.AddRange(temporaryStaff);
IEnumerable<Employee> allStaff = permanentStaff;

Note – The LINQ Reporting Engine can work with any IEnumerable<T>.
It does not provide its own Union/Concat operators inside the template.

:four: Pass the merged data to the engine

using Aspose.Words;
using Aspose.Words.Reporting;

// Load the template
var doc = new Document("EmployeeReportTemplate.docx");

// Prepare the data source – the name (`AllStaff`) must match what you use in the template
var data = new { AllStaff = allStaff };

// Fill the template
doc.Report(data);

// Save the result
doc.Save("EmployeeReportResult.docx");

:five: Template side – iterate over the merged collection

In the Word template place a Repeating Section (or a table row) and use the following tags:

Template element Tag syntax
Begin loop <# foreach (var emp in AllStaff) { #>
Output a field <#= emp.Name #>
End loop <# } #>

Example (inside a table row):

<# foreach (var emp in AllStaff) { #>
| <#= emp.Id #> | <#= emp.Name #> | <#= emp.Dept #> |
<# } #>

When the report is generated, the engine will render one row for each Employee in the merged collection.


:books: Useful Links


TL;DR

  • You cannot call Union/Concat inside the Word template.
  • Perform the merge in your C# (or VB) code using standard LINQ (Concat, Union, AddRange, etc.).
  • Pass the resulting IEnumerable<T> to the engine and iterate over it with normal <# … #> tags.

If you need any further clarification—e.g., implementing a custom IEqualityComparer<T> for a true Union—just let me know!

As explained, I can only work at template level, so I need a workaround to do this, but I’ve no idea. Please I need some human advice.

@deid We will investigate the issue and get back to you soon. For now, could you please attach your actual template, sample date and expected output, you would like to get? This will help us to better understand your requirements.

@deid

Thanks for waiting. The case is quite tricky, but finally the working prototype is found. Please consider the following template:

<<var [items = new int[] { 3, 4, 5 }]>>
<<var [firstItem = items.Take(1)] // Assuming that at least one item is present>>
<<var [allItems = firstItem.Concat(firstItem.Concat(firstItem.Concat(items)))]>>
<<foreach [i in allItems]>>
<<[IndexOf() == 0 ? "staticItem0" : IndexOf() == 1 ? "staticItem1" : IndexOf() == 2 ? "staticItem2" : "dynamicItem" + IndexOf()]>>
<<[IndexOf() == 0 ? 0 : IndexOf() == 1 ? 1 : IndexOf() == 2 ? 2 : i]>>
<</foreach>>

When a report is built upon the template, it looks like that:

staticItem0
0
staticItem1
1
staticItem2
2
dynamicItem3
3
dynamicItem4
4
dynamicItem5
5

For a chart, a similar approach can be applied. For example, the following foreach, x, and y tags can be used:

<<foreach [i in allItems]>>
<<x [IndexOf() == 0 ? "staticItem0" : IndexOf() == 1 ? "staticItem1" : IndexOf() == 2 ? "staticItem2" : "dynamicItem" + IndexOf()]>>
<<y [IndexOf() == 0 ? 0 : IndexOf() == 1 ? 1 : IndexOf() == 2 ? 2 : i]>>

Just use your collection instead of items and access actual members of its elements, and it should work.

1 Like

@ivan.lyagin Thanks so much for your help. I apply this in my template. have a nice day

1 Like