Tisa.XrmApp
2026.9.10.310
dotnet add package Tisa.XrmApp --version 2026.9.10.310
NuGet\Install-Package Tisa.XrmApp -Version 2026.9.10.310
<PackageReference Include="Tisa.XrmApp" Version="2026.9.10.310" />
<PackageVersion Include="Tisa.XrmApp" Version="2026.9.10.310" />
<PackageReference Include="Tisa.XrmApp" />
paket add Tisa.XrmApp --version 2026.9.10.310
#r "nuget: Tisa.XrmApp, 2026.9.10.310"
#:package Tisa.XrmApp@2026.9.10.310
#addin nuget:?package=Tisa.XrmApp&version=2026.9.10.310
#tool nuget:?package=Tisa.XrmApp&version=2026.9.10.310
Tisa.XrmApp
Базовый пакет для разработки приложений, работающих с Microsoft Dynamics 365/XRM. Предоставляет набор базовых классов, интерфейсов и компонентов для быстрой разработки приложений, интегрирующихся с Dynamics 365.
Возможности
- Поддержка .NET 8.0, .NET 9.0 и .NET 10.0
- Автоматическая регистрация сервисов через Service Installers
- JWT и API Key аутентификация
- Авторизация на основе разрешений
- Интеграция с Tisa.XrmCore для работы с Dynamics 365
- CQRS паттерн с Mediator
- Валидация запросов с FluentValidation
- Логирование с Serilog
- Обработка ошибок API
- Настройка Swagger/OpenAPI
- Интеграция с Tisa.Infrastructure, Tisa.Authorization, Tisa.Common
Установка
dotnet add package Tisa.XrmApp
Основные компоненты
Service Installer - Автоматическая регистрация сервисов
Пакет предоставляет метод расширения для автоматической регистрации всех Service Installers из сборки.
Базовая настройка приложения
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Tisa.XrmApp;
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Регистрация всех сервисов XRM приложения
builder.Services.AddXrmApplication(builder.Configuration);
var app = builder.Build();
app.Run();
}
}
Аутентификация
Пакет предоставляет два типа аутентификации: JWT и API Key.
JWT аутентификация
JWT аутентификация настраивается автоматически через AuthenticationServiceInstaller. Настройка выполняется через конфигурацию:
{
"Jwt": {
"SecretKey": "your-secret-key",
"Issuer": "your-issuer",
"Audience": "your-audience",
"ExpirationMinutes": 60
}
}
API Key аутентификация
API Key аутентификация позволяет использовать токены из Dynamics 365 для аутентификации запросов. Токен может передаваться:
- В заголовке
x-api-key - В параметре запроса
Token - В маршруте как параметр
Token
// Пример использования в контроллере
[ApiController]
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = AuthScheme.ApiKey)]
public class SecureController : ControllerBase
{
[HttpGet]
public IActionResult GetData()
{
return Ok("Данные доступны");
}
}
Настройка токенов в конфигурации
{
"Tokens": {
"your-token-value": "EntityType:EntityId:EntityName"
}
}
Авторизация
Авторизация настраивается через AuthorizationServiceInstaller и интегрируется с Tisa.Authorization.
Использование разрешений
using Tisa.Authorization.Attributes;
using Tisa.Authorization.Abstractions;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
[HasPermission(Permission.UserRead)]
public IActionResult GetUsers()
{
return Ok();
}
}
API настройки
ApiServiceInstaller настраивает базовые параметры API, включая поведение валидации моделей.
Настройка поведения API
// Автоматически настраивается через ApiServiceInstaller
// Можно переопределить в appsettings.json:
{
"ApiBehaviorOptions": {
"InvalidModelStateResponseFactory": {
// Настройки обработки ошибок валидации
}
}
}
CQRS и Messaging
Пакет интегрируется с Mediator для реализации паттерна CQRS.
Создание команды
using Tisa.Infrastructure.Messaging;
using Tisa.Common.Primitives;
public record CreateContactCommand(string FirstName, string LastName, string Email)
: ICommand<ContactDto>;
public class CreateContactCommandHandler : ICommandHandler<CreateContactCommand, ContactDto>
{
private readonly IXCoreService _xrmService;
public CreateContactCommandHandler(IXCoreService xrmService)
{
_xrmService = xrmService;
}
public async ValueTask<Result<ContactDto>> Handle(
CreateContactCommand command,
CancellationToken cancellationToken)
{
// Создание контакта в Dynamics 365
var contact = new Entity("contact");
contact["firstname"] = command.FirstName;
contact["lastname"] = command.LastName;
contact["emailaddress1"] = command.Email;
var contactId = await _xrmService.CreateAsync(contact, cancellationToken);
return Result.Success(new ContactDto(contactId, command.FirstName, command.LastName));
}
}
Создание запроса
using Tisa.Infrastructure.Messaging;
using Tisa.Common.Primitives;
public record GetContactQuery(Guid ContactId) : IQuery<ContactDto>;
public class GetContactQueryHandler : IQueryHandler<GetContactQuery, ContactDto>
{
private readonly IXCoreService _xrmService;
public GetContactQueryHandler(IXCoreService xrmService)
{
_xrmService = xrmService;
}
public async ValueTask<Result<ContactDto>> Handle(
GetContactQuery query,
CancellationToken cancellationToken)
{
var contact = await _xrmService.RetrieveAsync(
"contact",
query.ContactId,
cancellationToken);
if (contact == null)
{
return Result.Failure<ContactDto>("Контакт не найден");
}
var dto = new ContactDto(
contact.Id,
contact.GetAttributeValue<string>("firstname"),
contact.GetAttributeValue<string>("lastname")
);
return Result.Success(dto);
}
}
Валидация запросов
Валидация выполняется автоматически через ValidationBehavior при использовании Mediator.
Создание валидатора
using FluentValidation;
using Tisa.Infrastructure.Messaging;
public class CreateContactCommandValidator : AbstractValidator<CreateContactCommand>
{
public CreateContactCommandValidator()
{
RuleFor(x => x.FirstName)
.NotEmpty().WithMessage("Имя обязательно")
.MaximumLength(50).WithMessage("Имя не должно превышать 50 символов");
RuleFor(x => x.LastName)
.NotEmpty().WithMessage("Фамилия обязательна")
.MaximumLength(50).WithMessage("Фамилия не должна превышать 50 символов");
RuleFor(x => x.Email)
.NotEmpty().WithMessage("Email обязателен")
.EmailAddress().WithMessage("Неверный формат email");
}
}
Логирование
Логирование настраивается через LoggingServiceInstaller с использованием Serilog.
Настройка логирования
Логирование настраивается автоматически. Логи записываются в:
- Общий файл:
logs/R365_.txt - Файлы по токенам:
logs/{TokenName}/R365_.txt
Использование логирования
using Serilog;
public class ContactService
{
public void ProcessContact(Contact contact)
{
Log.Information("Обработка контакта {ContactId}", contact.Id);
// Логика обработки
}
}
Обработка ошибок
Пакет предоставляет стандартизированную обработку ошибок API.
Использование ApiErrors
using Tisa.XrmApp.Errors;
using Tisa.Common.Errors;
public class ContactService
{
public Result<Contact> GetContact(Guid id)
{
if (id == Guid.Empty)
{
return Result.Failure<Contact>(
ApiErrors.UnProcessableRequest.Message
);
}
// Логика получения контакта
}
}
ApiErrorResponse
using Tisa.XrmApp.Errors;
using Tisa.Common.Errors;
public class ValidationErrorResponse : ApiErrorResponse
{
public ValidationErrorResponse(IReadOnlyCollection<Error> errors)
: base(errors)
{
}
}
Работа с Dynamics 365
Пакет интегрируется с Tisa.XrmCore для работы с Dynamics 365.
Использование IXCoreService
using Tisa.XrmCore.Abstractions;
public class ContactService
{
private readonly IXCoreService _xrmService;
public ContactService(IXCoreService xrmService)
{
_xrmService = xrmService;
}
public async Task<Guid> CreateContactAsync(
string firstName,
string lastName,
CancellationToken cancellationToken)
{
var contact = new Entity("contact");
contact["firstname"] = firstName;
contact["lastname"] = lastName;
return await _xrmService.CreateAsync(contact, cancellationToken);
}
public async Task<Entity> GetContactAsync(
Guid contactId,
CancellationToken cancellationToken)
{
return await _xrmService.RetrieveAsync(
"contact",
contactId,
cancellationToken);
}
public async Task<bool> ValidateTokenAsync(string token)
{
return _xrmService.ValidateToken($"Tokens:{token}");
}
}
Middleware
Пакет автоматически регистрирует все классы, реализующие IMiddleware.
Создание кастомного Middleware
using Microsoft.AspNetCore.Http;
public class CustomMiddleware : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
// Логика до обработки запроса
await next(context);
// Логика после обработки запроса
}
}
Swagger/OpenAPI
Пакет поддерживает настройку Swagger через NSwag.
Добавление кастомных заголовков в Swagger
using Tisa.XrmApp.Headers;
using NSwag.Generation.AspNetCore;
public void ConfigureServices(IServiceCollection services)
{
services.AddOpenApiDocument(document =>
{
document.OperationProcessors.Add(new AddCustomHeader());
});
}
Структура проекта
Authentication/- Компоненты аутентификации (API Key)Common/- Общие компонентыErrors/- Обработка ошибок API (ApiErrors, ApiErrorResponse)Events/- Обработчики событийHeaders/- Работа с HTTP заголовками (Swagger)Middleware/- Middleware компонентыServiceInstallers/- Установщики сервисовApi/- Настройка APIAuthentication/- Настройка аутентификацииAuthorization/- Настройка авторизацииCommon/- Общие сервисы приложенияLogging/- Настройка логированияMessaging/- Настройка CQRS/MediatorMiddleware/- Регистрация middleware
Services/- Базовые сервисыShared/- Общие ресурсы
Полный пример настройки приложения
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Tisa.XrmApp;
using Tisa.Infrastructure.Extensions;
using FluentValidation;
using System.Reflection;
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Регистрация сервисов XRM приложения
builder.Services.AddXrmApplication(builder.Configuration);
// Регистрация контроллеров
builder.Services.AddControllers();
// Регистрация валидаторов
builder.Services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
// Регистрация Mediator
builder.Services.AddMediator(options =>
{
options.ServiceLifetime = ServiceLifetime.Scoped;
});
// Настройка Swagger
builder.Services.AddOpenApiDocument();
var app = builder.Build();
// Настройка middleware
app.UseGlobalExceptionHandler();
app.UseLogContextEnrichment();
app.UseSerilogRequestLogger();
app.UseAuthentication();
app.UseAuthorization();
app.UseOpenApi();
app.UseSwaggerUi();
app.MapControllers();
app.Run();
}
}
Конфигурация приложения
Пример конфигурации appsettings.json:
{
"Jwt": {
"SecretKey": "your-secret-key-here",
"Issuer": "https://your-issuer.com",
"Audience": "https://your-audience.com",
"ExpirationMinutes": 60
},
"Tokens": {
"token-1": "contact:guid-1:Contact Name 1",
"token-2": "account:guid-2:Account Name 2"
},
"XrmCore": {
"ConnectionString": "your-connection-string",
"OrganizationServiceUrl": "https://your-org.crm.dynamics.com"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Tisa": "Debug"
}
}
}
Требования
- .NET 8.0, .NET 9.0 или .NET 10.0
- Tisa.XrmCore
- Tisa.Infrastructure
- Tisa.Authorization
- Tisa.Common
- FluentValidation 12.1.0
- Mediator.Abstractions 3.0.1
- NSwag.Generation 14.6.2
Тестирование
Для тестирования приложений, использующих Tisa.XrmApp, рекомендуется использовать FakeXrmEasy:
using FakeXrmEasy;
using Microsoft.Xrm.Sdk;
[TestClass]
public class ContactServiceTests
{
private IXrmFakedContext _context;
private IOrganizationService _service;
private ContactService _contactService;
[TestInitialize]
public void Initialize()
{
_context = new XrmFakedContext();
_service = _context.GetOrganizationService();
_contactService = new ContactService(_service);
}
[TestMethod]
public async Task CreateContact_ShouldReturnContactId()
{
// Arrange
var firstName = "John";
var lastName = "Doe";
// Act
var contactId = await _contactService.CreateContactAsync(
firstName,
lastName,
CancellationToken.None
);
// Assert
Assert.IsNotNull(contactId);
var contact = _service.Retrieve("contact", contactId, new ColumnSet(true));
Assert.AreEqual(firstName, contact["firstname"]);
Assert.AreEqual(lastName, contact["lastname"]);
}
}
Авторы
Команда разработчиков TISA
Лицензия
MIT License
Поддержка
Для получения поддержки или сообщения об ошибках, пожалуйста, напишите нам на support@tisn.ru
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. 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 is compatible. 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. |
-
net10.0
- FluentValidation (>= 12.1.1)
- Mediator.Abstractions (>= 3.0.1)
- NSwag.Generation (>= 14.6.3)
- Tisa.Authorization (>= 2026.9.10.310)
- Tisa.Common (>= 2026.9.10.310)
- Tisa.Infrastructure (>= 2026.9.10.310)
- Tisa.XrmCore (>= 2026.9.10.310)
-
net8.0
- FluentValidation (>= 12.1.1)
- Mediator.Abstractions (>= 3.0.1)
- NSwag.Generation (>= 14.6.3)
- Tisa.Authorization (>= 2026.9.10.310)
- Tisa.Common (>= 2026.9.10.310)
- Tisa.Infrastructure (>= 2026.9.10.310)
- Tisa.XrmCore (>= 2026.9.10.310)
-
net9.0
- FluentValidation (>= 12.1.1)
- Mediator.Abstractions (>= 3.0.1)
- NSwag.Generation (>= 14.6.3)
- Tisa.Authorization (>= 2026.9.10.310)
- Tisa.Common (>= 2026.9.10.310)
- Tisa.Infrastructure (>= 2026.9.10.310)
- Tisa.XrmCore (>= 2026.9.10.310)
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 | |
|---|---|---|---|
| 2026.9.10.310 | 82 | 3/8/2026 | |
| 2026.9.10.300 | 74 | 3/4/2026 | |
| 2025.9.10.1120 | 367 | 11/17/2025 | |
| 2025.9.9.1111 | 296 | 11/12/2025 | |
| 2025.9.9.1105 | 213 | 10/30/2025 | |
| 2025.9.9.1101 | 305 | 10/28/2025 | |
| 2025.9.9.410 | 248 | 4/9/2025 | |
| 2025.9.9.400 | 341 | 4/8/2025 |