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
                    
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="Tisa.XrmApp" Version="2026.9.10.310" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tisa.XrmApp" Version="2026.9.10.310" />
                    
Directory.Packages.props
<PackageReference Include="Tisa.XrmApp" />
                    
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 Tisa.XrmApp --version 2026.9.10.310
                    
#r "nuget: Tisa.XrmApp, 2026.9.10.310"
                    
#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 Tisa.XrmApp@2026.9.10.310
                    
#: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=Tisa.XrmApp&version=2026.9.10.310
                    
Install as a Cake Addin
#tool nuget:?package=Tisa.XrmApp&version=2026.9.10.310
                    
Install as a Cake Tool

Tisa.XrmApp

NuGet Version License

Базовый пакет для разработки приложений, работающих с 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 для аутентификации запросов. Токен может передаваться:

  1. В заголовке x-api-key
  2. В параметре запроса Token
  3. В маршруте как параметр 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/ - Настройка API
    • Authentication/ - Настройка аутентификации
    • Authorization/ - Настройка авторизации
    • Common/ - Общие сервисы приложения
    • Logging/ - Настройка логирования
    • Messaging/ - Настройка CQRS/Mediator
    • Middleware/ - Регистрация 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 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. 
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
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.1101 is deprecated because it has critical bugs.
2025.9.9.410 248 4/9/2025
2025.9.9.400 341 4/8/2025 2025.9.9.400 is deprecated because it is no longer maintained.