Reo.Core.Reports 8.0.786

dotnet add package Reo.Core.Reports --version 8.0.786
                    
NuGet\Install-Package Reo.Core.Reports -Version 8.0.786
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Reo.Core.Reports" Version="8.0.786" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Reo.Core.Reports" Version="8.0.786" />
                    
Directory.Packages.props
<PackageReference Include="Reo.Core.Reports" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Reo.Core.Reports --version 8.0.786
                    
#r "nuget: Reo.Core.Reports, 8.0.786"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Reo.Core.Reports@8.0.786
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Reo.Core.Reports&version=8.0.786
                    
Install as a Cake Addin
#tool nuget:?package=Reo.Core.Reports&version=8.0.786
                    
Install as a Cake Tool

Reo.Core.Reports

Библиотека для создания отчетов в формате Excel (XLSX).

Ремарки

  • На текущий момент в разработке, возможны ломающие изменения.
  • У большинства методов исчерпывающая документация и/или множество перегрузок. Внимательно смотрите в IntellySense.
  • Не все методы расписаны в данном документе.
  • Механизм призван покрывать самые частые кейсы и запросы, возможно для ваших конкретных нужд еще не написаны хелперы - экстеншен-методы, это повод их написать.

Использование

  1. Добавить в DI

    .AddReportGeneratorDefaults()
    
  2. Необходимо создать класс, наследующийся от ReportGeneratorBase<TReportParameters, TDataFilter> (или StaticTemplateReportGeneratorBase<TReportParameters, TDataFilter>, в случае наличия шаблона в виде файла), где

    • TReportParameters - параметры генерации отчета
    • TDataFilter - фильтр/объект, влияющий на получение данных для отчета
  3. Определить TemplateFileName - имя файла шаблона отчета (например, "ReportTemplate.xlsx").
    Файл должен находиться в папке Templates в корне запускаемого проекта.
    Все что в шаблоне будет скопировано в результирующий документ.

  4. Определить метод SetupReport. Внутри него настраивается вся генерация отчета.

  5. Создание отчета разбито по страницам, добавить и сконфигурировать новую страницу можно через AddPage.
    На каждую страницу можно вывести данные одного типа. Тип и источник данных определяется первым параметром метода.

    • Если вам нужна какая-то сложная логика - создайте класс, реализующий IReportGeneratorDataProvider<TReport, TReportParameters>
    • Можно передать лямбда-выражение в качестве источника данных
    • Можно передать список объектов
    • Можно передать лямбда-выражение в качестве источника данных и достать данные из TReportParameters

    Второй параметр - конфигуратор страницы.

  6. Зарегистрировать ваш генератор в DI, например:

    .AddSingleton<IReportGenerator<ActivitiesMnoReportParameters, EmptyLimitableFilter>, ActivitiesMnoReportGenerator>()
    
  7. Использовать:

     var defaultReportRequest = new DefaultReportRequest<ActivitiesMnoReportParameters, EmptyLimitableFilter>
     {
         Parameters = new(entity),
         Filter = EmptyLimitableFilter.Instance
     };
     var exported = await activitiesMnoReportGenerator.ExportAsync(defaultReportRequest, token);
     return new(exported.Content, "application/vnd.ms-excel")
     {
         FileDownloadName = exported.FileName
     };
    

Конфигурация страницы

Настройки страницы

Настройки страницы определяются методами Setup*. Можно настроить

  • шапку страницы - SetupHeader
  • строку начала данных - SetupDataArea
  • работы с "ИТОГО" - SetupTotal*
  • содержимое конкретной ячейки - SetupCell
// Вывод хедера на A1
c.SetupHeader((parameters, time) => $"Мероприятия по Дорожной карте {parameters.RoadMap.RegistryNumber} "
	+ $"на {time.CurrentDate.ToString(FormatConstants.DateOnlyFormat)}");

// Начало строк с данными с 3ей строки
c.SetupDataArea(3);

Столбцы данных

Столбцы страницы добавляются последовательно методами Add*. Существует 4 основных вида столбца:

  • Info - любая текстовая информация, названия, например наименование региона.
    Столбец не попадет в "ИТОГО".
  • NumericValue - любые числовые значения.
    Столбец попадет в "ИТОГО".
  • NonNumericValue - не числовые значения, нужно передать лямбду о том, как вывести множество таких значений в " ИТОГО".
    Столбец попадет в "ИТОГО".
  • Const - константные строки. (например для экселевских формул в нотации R1C1).
    Столбец не попадет в "ИТОГО".

Большинство методов Add принимает в себя Func<TReport, object> в котором можно выбрать значение какого свойства выведется в столбец.

c.AddInfoColumn(activity => activity.FederalBudgetPlan);
c.AddInfoColumn(activity => activity.FederalBudgetFact);

Любой метод Add* всегда добавляет вам столбец в отчет.

Есть набор методов перегрузок (и можно сделать свои), которые как-то более конкретно работают с нужным вам типом данных.
Например, метод для вывода наименования enum'а: c.AddEnumDisplayName(activity => activity.Confirmation);.

Итого

.SetupTotalType позволяет настроить "ИТОГО" для страницы.

  • None - не выводить "ИТОГО", по умолчанию
  • Country - выводить "ИТОГО" по РФ (т.е. суммировать все записи)
  • ByFederalDistrict - выводить "ИТОГО" по федеральным округам (и по стране)

.SetupTotalRowsDataPredicate позволяет настроить фильтр для того, какие строки будут включены в подсчет "ИТОГО".

Присоединяемые свойства

Зачастую модель нужно обогащать из каких-либо источников, что бы потом вывести это в отчет. Например, в модели есть RegionId, а в отчете нужно вывести RegionName.
Да, это можно сделать, создав отдельную вьюху, и обогатив ее отдельно руками на этапе получения данных, но это не удобно:

  • Нужно руками создавать отдельный класс, и маппить в его
  • Нужно каждый раз одинаковый писать код получения и обогащения данных
  • Нельзя унести в кор одну, проверенную реализацию
  • Написать подобную штуку для потокового экспорта - нетривиальная задача

Механизм AttachedProperties призван решить эту проблему. К каждой экспортируемой строке можно "присоединить" любой C# объект любого типа, а потом обращаться к нему сколько угодно раз.

Для "присоединения" нужно сделать

builder.AttachPropertyKeyed
	// или
builder.AttachPropertyBulk
	// или
builder.AttachedProperties.AddAttachedProperty(providerTemplate)

Есть заготовленные методы для Region, Area, FederalDistrict.

Любой метод Attach* всегда прикрепляет вам объект.

Использовать в столбцах можно через

c.AddAttached<RegionDoc>(doc => doc.Name)

Для часто используемых случаев уже есть методы расширения:

c.AddRegionCode();
c.AddRegionName();
c.AddAreaName();
c.AddFederalDistrict();
// etc

Вышеперечисленные методы расширения автоматически добавляют необходимое присоединяемое свойство к вашим документам (т.е. не нужно писать c.AttachRegionDocs(), etc) в случае, если экспортируемый документ реализует интерфейс, содержащий нужный идентификатор, например IHasRegion для RegionDoc, IHasArea для AreaDoc, итд.
Если же не реализует, но у вас там все равно где-то есть, например, RegionId, то у AttachRegionDocs (и подобных методов) есть перегрузка с лямбдой, позволяющей указать, какое свойство считать за Id.

Форматтеры

Все методы Add* принимают лямбду, в которой можно вернуть object, а не строку.
Это позволяет принимать решение о форматировании конкретных типов на уровне фреймворка, а не на уровне юзера.

На каждом из уровней (дефолты в DI на всё приложение, оверрайды на отчет, оверрайды на страницу) можно переопределить/добавить реализации IValueFillFormatter для кастомного форматирования конкретных типов.

Реализации по умолчанию лежат в ReportGeneratorOptions, и включают в себя:

  • ObjectFillFormatter - вызовет .ToString на объекте
  • CollectionsFillFormatter - раскладывает коллекцию на строку вида "1, 2, 3". "-" если нет элементов. Каждый элемент внутри так-же будет отформатирован нужным для него форматтером. Т.е. например коллекция дат выведется как "dd.MM.yyyy, dd.MM.yyyy, dd.MM.yyyy"
  • DateOnlyFillFormatter - форматирует дату как dd.MM.yyyy

Поиск по коллекции происходит от последнего к первому, поэтому кастомные форматтеры нужно добавлять в конец списка.

Расширяемость

Каждый элемент фреймворка можно расширить, зарегистрировав свои реализации.
Для понимания как оно устроено смотрите в уже существующие реализации.

Основы

Шаблоны и сущности

В большинстве случаев все сущности разделены на 2 части: IBlaBlaTemplate - описывает "как" что-то делать. IBlaBla - что-то делает на основе шаблоны.

IBlaBlaTemplate можно создать где угодно, и передать в него какую-то конфигурацию (например, селектор значения, значение при null или что угодно).

Реализация IBlaBla вынесена в отдельную сущность, потому что настоящая реализация будет резолвится из DI.
Это осознанное решение, что бы внутри этой сущности можно было бы просто использовать заинжекченные из DI сервисы.

Дженерики

Когда вы реализуете любой интерфейс, то всегда пробрасывайте все дженерики в ваш тип напрямую, т.е. например:

//                              TTemplate, TReport, TReportParameters
public class HeaderColumnFiller<TTemplate, TReport, TReportParameters> 
	: IColumnFiller<TTemplate, TReport, TReportParameters>

Но при этом используйте констрейнты для ограничения TTemplate, что бы действительно получить доступ к нему в коде:

public class HeaderColumnFiller<TTemplate, TReport, TReportParameters> : IColumnFiller<TTemplate, TReport, TReportParameters>
//                ->  HeaderColumnFillerTemplate
    where TTemplate : HeaderColumnFillerTemplate<TReport, TReportParameters>

IColumnFiller - заполнители столбцов

Каждый раз, когда вы вызываете .Add под капотом в конфигуратор добавляется шаблон филлера - IColumnFillerTemplate.

IColumnFillerTemplate - описывает "как" генерить столбец. IColumnFiller - заполняет столбец.

В случае, когда вам не нужно инжектить сервисы из DI, то ваш филлер может реализовывать и IColumnFiller и IColumnFillerTemplate одновременно.

public class DisplayColumnFiller<TTemplate, TReport, TReportParameters>
	: IColumnFillerTemplate<TReport>, 
	  IColumnFiller<TTemplate, TReport, TReportParameters>
	where TTemplate : IColumnFillerTemplate<TReport>

Полный пример создания филлера:

/// <inheritdoc cref="IColumnFillerTemplate{TReport}" />
public class FederalDistrictFillerTemplate<TReport> : IColumnFillerTemplate<TReport>
	where TReport : IHasRegion
{
	/// <inheritdoc />
	public ColumnType Type => ColumnType.Info;

	/// <inheritdoc />
	public Type ReturnType => typeof(string);
}

/// <inheritdoc cref="IColumnFiller{TTemplate, TReport}" />
public class FederalDistrictFiller<TTemplate, TReport, TReportParameters>(IFederalDistrictElasticReader federalDistrictElasticReader, IMemoryCache memoryCache)
	: IColumnFiller<TTemplate, TReport, TReportParameters>
	where TTemplate : FederalDistrictFillerTemplate<TReport>
	where TReport : IHasRegion
{

	/// <inheritdoc />
	public async ValueTask<object?> GetValueAsync(
		TTemplate template,
		IReadOnlyList<TReport> allReports,
		TReport report,
		TReportParameters parameters,
		AttachedPropertiesContainer attachedProperties,
		CancellationToken token)
	{

		var federalDistrictsByRegion = await GetFederalDistrictsByRegionsAsync(token);

		return federalDistrictsByRegion.GetValueOrDefault(report.RegionId, string.Empty);
	}
	
	/// <inheritdoc />
	public ValueTask<object> GetTotalValueAsync(
		TTemplate template,
		IReadOnlyList<TReport> reports,
		TReportParameters parameters,
		CancellationToken token) => throw new NotSupportedException();
	
	private async Task<Dictionary<int, string>> GetFederalDistrictsByRegionsAsync(CancellationToken token)
	{
		var dictionary = await memoryCache.GetOrCreateAsync(
			nameof(FederalDistrictFiller<FederalDistrictFillerTemplate<IHasRegion>, IHasRegion, TReportParameters>),
			async entry =>
			{
				var federalDistrictDocs = await federalDistrictElasticReader.GetAllAsync(token);

				var federalDistrictsByRegion = federalDistrictDocs
					.SelectMany(doc => doc.Regions.Select(dto => (federalDistrict: doc, region: dto)))
					.ToDictionary(tuple => tuple.region.Id, tuple => tuple.federalDistrict.Abbreviation);

				entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(3);

				return federalDistrictsByRegion;
			});

		return dictionary!;
	}
}

P.S. Хоть пример полностью валидный и будет работать, не используйте этот код. В данном случае лучше использовать AttachedProperties, через которые и сделана текущая реализация FederalDistrictFiller.

После создания нужно зарегистрировать филлер в DI:

// Можно использовать другие лайфтаймы
.TryAddSingleton(typeof(IColumnFiller<,,>), typeof(FederalDistrictFiller<,,>));
// Однако лучше использовать экстеншен метод, который сделает все за вас
.TryAddReportGeneratorColumnFillerSingleton(typeof(HeaderColumnFiller<,,>));

Это полный пример создания филлера, обратите внимание, что нигде в коде выведения конкретных дженериков не будет.
Явной связи между филлером и темплейтом тоже нигде не указывается.

IAttachedPropertyProvider - присоединяемые свойства

Каждый раз, когда вы вызываете .Attach под капотом в builder.AttachedProperties добавляется шаблон филлера.

Все остальное аналогично IColumnFiller'ам. Кроме

Регистрация в DI:

.TryAddReportGeneratorAttachedPropertyProviderScoped(typeof(AreaDocAttachedPropertyProvider<,>));

В Add* методах, которые добавляют столбцы, использующие присоединяемые свойства (например AddRegionCode) можно сделать проверку на наличие нужного интерфейса для автоматического вызова Attach* метода.

/// <summary>
/// Настройка столбца с информацией по коду региона
/// </summary>
/// <param name="reportPageBuilder"> Сервис настроек отчета </param>
public static void AddRegionCode<TReport, TReportParameters>(this ReportPageBuilder<TReport, TReportParameters> reportPageBuilder)
{
	// Вызовет AttachRegionDocs, если                                             TReport реализует IHasRegion
	reportPageBuilder.AttachOrThrow<TReport, TReportParameters, RegionDoc>(typeof(TReport),  typeof(IHasRegion),
		nameof(AttachRegionDocs));

	reportPageBuilder.SetupColumn(new RegionCodeFiller<IColumnFillerTemplate<TReport>, TReport, TReportParameters>());
}

IValueFillFormatter - форматирование значений

Форматтеры можно добавлять на трех уровнях:

  1. На уровне приложения (через DI)

     services.AddReportGeneratorDefaults(options =>
     {
        options.Formatters.Add(new MyCustomFormatter());
     });
    
  2. На уровне отчета (в SetupReport)

     protected override void SetupReport(ReportBuilder<TReportParameters, TDataFilter> builder)
     {
        builder.AddFormatter(new MyCustomFormatter());
    
        builder.AddPage(/* ... */);
     }
    
  3. На уровне страницы

     builder.AddPage(dataProvider, pageBuilder =>
     {
        pageBuilder.Formatters.Add(new MyCustomFormatter());
    
        pageBuilder.AddInfoColumn(/* ... */);
     });
    
Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
8.0.786 0 12/24/2025
8.0.785 0 12/24/2025
8.0.784 0 12/24/2025
8.0.783 1 12/24/2025
8.0.782 4 12/24/2025
8.0.781 31 12/23/2025
8.0.780 24 12/23/2025
8.0.779 28 12/23/2025
8.0.778 27 12/23/2025
8.0.777 30 12/23/2025
8.0.776 30 12/23/2025
8.0.775 30 12/23/2025
8.0.771 27 12/23/2025
8.0.770 33 12/23/2025
8.0.769 41 12/22/2025
8.0.768 40 12/22/2025
8.0.766 171 12/19/2025
8.0.765 167 12/19/2025
8.0.764 165 12/19/2025
8.0.763 172 12/19/2025
8.0.762 177 12/19/2025
8.0.761 180 12/19/2025
8.0.760 266 12/17/2025
8.0.759 253 12/16/2025
8.0.758 259 12/16/2025
8.0.757 237 12/15/2025
8.0.755 226 12/15/2025
8.0.749 233 12/15/2025
8.0.748 212 12/15/2025
8.0.747 103 12/12/2025
8.0.746 107 12/12/2025
8.0.745 103 12/12/2025
8.0.744 106 12/12/2025
8.0.743 403 12/11/2025
8.0.742 415 12/10/2025
8.0.741 419 12/10/2025
8.0.740 424 12/9/2025
8.0.739 404 12/8/2025
8.0.738 401 12/8/2025
8.0.737 405 12/8/2025
8.0.736 171 12/5/2025
8.0.735 185 12/4/2025
8.0.734 178 12/4/2025
8.0.733 182 12/4/2025
8.0.732 650 12/3/2025
8.0.725 564 12/1/2025
8.0.724 474 12/1/2025
8.0.721 131 11/28/2025
8.0.720 131 11/28/2025
8.0.719 172 11/27/2025
8.0.718 164 11/27/2025
8.0.713 164 11/27/2025
8.0.712 165 11/27/2025
8.0.709 166 11/27/2025
8.0.708 165 11/27/2025
8.0.707 170 11/27/2025
8.0.706 166 11/27/2025
8.0.705 165 11/27/2025
8.0.704 171 11/27/2025
8.0.703 167 11/26/2025
8.0.701 161 11/26/2025
8.0.700 168 11/26/2025
8.0.699 168 11/26/2025
8.0.697 166 11/26/2025
8.0.692 178 11/24/2025
8.0.691 309 11/21/2025
8.0.690 382 11/20/2025
8.0.689 389 11/20/2025
8.0.686 390 11/18/2025
8.0.685 388 11/18/2025
8.0.682 385 11/18/2025
8.0.681 319 11/17/2025
8.0.680 321 11/17/2025
8.0.679 300 11/17/2025
8.0.678 269 11/13/2025
8.0.677 278 11/11/2025
8.0.676 274 11/11/2025
8.0.675 279 11/11/2025
8.0.674 271 11/11/2025
8.0.673 258 11/10/2025
8.0.672 255 11/10/2025
8.0.671 117 11/7/2025
8.0.670 122 11/7/2025
8.0.669 124 11/7/2025
8.0.668 126 11/7/2025
8.0.667 158 11/7/2025
8.0.666 166 11/7/2025
8.0.665 183 11/6/2025
8.0.664 178 11/6/2025
8.0.663 174 11/6/2025
8.0.662 185 11/6/2025
8.0.661 180 11/5/2025
8.0.660 186 11/5/2025
8.0.659 120 11/1/2025
8.0.658 152 10/31/2025
8.0.657 184 10/30/2025
8.0.656 184 10/30/2025
8.0.655 183 10/29/2025