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
<PackageReference Include="Reo.Core.Reports" Version="8.0.786" />
<PackageVersion Include="Reo.Core.Reports" Version="8.0.786" />
<PackageReference Include="Reo.Core.Reports" />
paket add Reo.Core.Reports --version 8.0.786
#r "nuget: Reo.Core.Reports, 8.0.786"
#:package Reo.Core.Reports@8.0.786
#addin nuget:?package=Reo.Core.Reports&version=8.0.786
#tool nuget:?package=Reo.Core.Reports&version=8.0.786
Reo.Core.Reports
Библиотека для создания отчетов в формате Excel (XLSX).
Ремарки
- На текущий момент в разработке, возможны ломающие изменения.
- У большинства методов исчерпывающая документация и/или множество перегрузок. Внимательно смотрите в IntellySense.
- Не все методы расписаны в данном документе.
- Механизм призван покрывать самые частые кейсы и запросы, возможно для ваших конкретных нужд еще не написаны хелперы - экстеншен-методы, это повод их написать.
Использование
Добавить в DI
.AddReportGeneratorDefaults()Необходимо создать класс, наследующийся от
ReportGeneratorBase<TReportParameters, TDataFilter>(илиStaticTemplateReportGeneratorBase<TReportParameters, TDataFilter>, в случае наличия шаблона в виде файла), гдеTReportParameters- параметры генерации отчетаTDataFilter- фильтр/объект, влияющий на получение данных для отчета
Определить
TemplateFileName- имя файла шаблона отчета (например, "ReportTemplate.xlsx").
Файл должен находиться в папкеTemplatesв корне запускаемого проекта.
Все что в шаблоне будет скопировано в результирующий документ.Определить метод
SetupReport. Внутри него настраивается вся генерация отчета.Создание отчета разбито по страницам, добавить и сконфигурировать новую страницу можно через
AddPage.
На каждую страницу можно вывести данные одного типа. Тип и источник данных определяется первым параметром метода.- Если вам нужна какая-то сложная логика - создайте класс, реализующий
IReportGeneratorDataProvider<TReport, TReportParameters> - Можно передать лямбда-выражение в качестве источника данных
- Можно передать список объектов
- Можно передать лямбда-выражение в качестве источника данных и достать данные из
TReportParameters
Второй параметр - конфигуратор страницы.
- Если вам нужна какая-то сложная логика - создайте класс, реализующий
Зарегистрировать ваш генератор в DI, например:
.AddSingleton<IReportGenerator<ActivitiesMnoReportParameters, EmptyLimitableFilter>, ActivitiesMnoReportGenerator>()Использовать:
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 - форматирование значений
Форматтеры можно добавлять на трех уровнях:
На уровне приложения (через DI)
services.AddReportGeneratorDefaults(options => { options.Formatters.Add(new MyCustomFormatter()); });На уровне отчета (в
SetupReport)protected override void SetupReport(ReportBuilder<TReportParameters, TDataFilter> builder) { builder.AddFormatter(new MyCustomFormatter()); builder.AddPage(/* ... */); }На уровне страницы
builder.AddPage(dataProvider, pageBuilder => { pageBuilder.Formatters.Add(new MyCustomFormatter()); pageBuilder.AddInfoColumn(/* ... */); });
| Product | Versions 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. |
-
net9.0
- AspNetCore.HealthChecks.Elasticsearch (>= 9.0.0)
- AspNetCore.HealthChecks.Redis (>= 9.0.0)
- AspNetCore.HealthChecks.UI.Client (>= 9.0.0)
- ClosedXML (>= 0.105.0)
- DocumentFormat.OpenXml (>= 3.3.0)
- FluentValidation (>= 11.11.0)
- FluentValidation.AspNetCore (>= 11.3.1)
- Flurl (>= 4.0.0)
- Flurl.Http (>= 4.0.2)
- Flurl.Http.Newtonsoft (>= 0.9.1)
- Humanizer (>= 3.0.1)
- Humanizer.Core (>= 3.0.1)
- Humanizer.Core.ru (>= 3.0.1)
- IdentityModel (>= 7.0.0)
- JetBrains.Annotations (>= 2025.2.4)
- LinkDotNet.StringBuilder (>= 3.2.0)
- LinqKit.Core (>= 1.2.9)
- MassTransit (>= 8.5.7 && < 9.0.0)
- MassTransit.RabbitMQ (>= 8.5.7 && < 9.0.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 8.0.22)
- Microsoft.AspNetCore.Authentication.OpenIdConnect (>= 8.0.22)
- Microsoft.AspNetCore.Mvc.NewtonsoftJson (>= 8.0.22)
- Microsoft.Bcl.AsyncInterfaces (>= 9.0.11)
- Microsoft.CSharp (>= 4.7.0)
- Microsoft.EntityFrameworkCore (>= 9.0.11)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Caching.Memory (>= 10.0.1)
- Microsoft.Extensions.Caching.StackExchangeRedis (>= 9.0.11)
- Microsoft.Extensions.Configuration (>= 10.0.1)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.1)
- Microsoft.Extensions.DependencyInjection (>= 10.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Http.Polly (>= 10.0.1)
- Microsoft.Extensions.Logging (>= 10.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Options (>= 10.0.1)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.1)
- NEST (>= 7.17.5)
- NEST.JsonNetSerializer (>= 7.17.5)
- Newtonsoft.Json (>= 13.0.4)
- Polly (>= 8.5.0)
- RabbitMQ.Client (>= 7.2.0)
- RedLock.net (>= 2.3.2)
- Reo.Core.Elastic (>= 8.0.786)
- Reo.Core.IdentityModel (>= 8.0.786)
- stbychkov.AutoLoggerMessage (>= 1.0.15)
- Swashbuckle.AspNetCore (>= 10.1.0)
- System.Collections.Immutable (>= 10.0.1)
- System.ComponentModel.Annotations (>= 5.0.0)
- System.IO.Abstractions (>= 22.1.0)
- System.IO.Pipelines (>= 10.0.1)
- System.Linq.Async (>= 7.0.0)
- System.Text.Encodings.Web (>= 10.0.1)
- System.Text.Json (>= 10.0.1)
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 |