Reo.Core.Reports 8.0.789

dotnet add package Reo.Core.Reports --version 8.0.789
                    
NuGet\Install-Package Reo.Core.Reports -Version 8.0.789
                    
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.789" />
                    
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.789" />
                    
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.789
                    
#r "nuget: Reo.Core.Reports, 8.0.789"
                    
#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.789
                    
#: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.789
                    
Install as a Cake Addin
#tool nuget:?package=Reo.Core.Reports&version=8.0.789
                    
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.789 138 12/25/2025
8.0.788 142 12/25/2025
8.0.787 144 12/25/2025
8.0.786 152 12/24/2025
8.0.785 155 12/24/2025
8.0.784 155 12/24/2025
8.0.783 156 12/24/2025
8.0.782 157 12/24/2025
8.0.781 154 12/23/2025
8.0.780 147 12/23/2025
8.0.779 151 12/23/2025
8.0.778 150 12/23/2025
8.0.777 153 12/23/2025
8.0.776 153 12/23/2025
8.0.775 153 12/23/2025
8.0.771 149 12/23/2025
8.0.770 155 12/23/2025
8.0.769 162 12/22/2025
8.0.768 161 12/22/2025
8.0.766 211 12/19/2025
8.0.765 207 12/19/2025
8.0.764 205 12/19/2025
8.0.763 212 12/19/2025
8.0.762 217 12/19/2025
8.0.761 220 12/19/2025
8.0.760 267 12/17/2025
8.0.759 254 12/16/2025
8.0.758 260 12/16/2025
8.0.757 238 12/15/2025
8.0.755 227 12/15/2025
8.0.749 234 12/15/2025
8.0.748 213 12/15/2025
8.0.747 104 12/12/2025
8.0.746 108 12/12/2025
8.0.745 105 12/12/2025
8.0.744 109 12/12/2025
8.0.743 405 12/11/2025
8.0.742 419 12/10/2025
8.0.741 422 12/10/2025
8.0.740 427 12/9/2025
8.0.739 405 12/8/2025
8.0.738 402 12/8/2025
8.0.737 406 12/8/2025
8.0.736 174 12/5/2025
8.0.735 187 12/4/2025
8.0.734 180 12/4/2025
8.0.733 184 12/4/2025
8.0.732 651 12/3/2025
8.0.725 565 12/1/2025
8.0.724 475 12/1/2025
8.0.721 133 11/28/2025
8.0.720 133 11/28/2025
8.0.719 174 11/27/2025
8.0.718 164 11/27/2025
8.0.713 166 11/27/2025
8.0.712 165 11/27/2025
8.0.709 168 11/27/2025
8.0.708 166 11/27/2025
8.0.707 171 11/27/2025
8.0.706 167 11/27/2025
8.0.705 165 11/27/2025
8.0.704 172 11/27/2025
8.0.703 169 11/26/2025
8.0.701 162 11/26/2025
8.0.700 169 11/26/2025
8.0.699 170 11/26/2025
8.0.697 166 11/26/2025
8.0.692 180 11/24/2025
8.0.691 310 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 389 11/18/2025
8.0.682 387 11/18/2025
8.0.681 321 11/17/2025
8.0.680 322 11/17/2025
8.0.679 301 11/17/2025
8.0.678 270 11/13/2025
8.0.677 278 11/11/2025
8.0.676 274 11/11/2025
8.0.675 281 11/11/2025
8.0.674 271 11/11/2025
8.0.673 258 11/10/2025
8.0.672 258 11/10/2025
8.0.671 119 11/7/2025
8.0.670 123 11/7/2025
8.0.669 124 11/7/2025
8.0.668 126 11/7/2025
8.0.667 160 11/7/2025
8.0.666 167 11/7/2025
8.0.665 183 11/6/2025
8.0.664 180 11/6/2025
8.0.663 175 11/6/2025
8.0.662 187 11/6/2025
8.0.661 181 11/5/2025
8.0.660 188 11/5/2025
8.0.659 121 11/1/2025
8.0.658 152 10/31/2025
8.0.657 186 10/30/2025
8.0.656 184 10/30/2025
8.0.655 184 10/29/2025