Что такое код asp ontransactioncommit


Содержание

IT-ЗАМЕТКИ

Инструменты пользователя

Инструменты сайта

Содержание

Транзакция — это набор операций, который должен либо выполниться успешно, либо не выполниться, причем как единое целое. Целью транзакции является гарантия того, что данные всегда находятся в корректном, согласованном состоянии.

Транзакции характеризуются четырьмя свойствами, которые называются свойствами ACID.

Обратите внимание, что эти идеальные характеристики транзакции не всегда полностью достижимы. Одна из проблем связана с обеспечением изоляции. Реляционная СУБД должна блокировать данные, чтобы другие пользователи не имели к ним доступа, пока транзакция выполняется. Чем больше используется блокировок, и чем они крупнее, тем выше шансы того, что пользователь не сможет выполнить другую работу, пока транзакция находится в процессе выполнения. Другими словами, иногда приходится идти на компромисс между степенью параллелизма работы пользователей и изоляцией.

Транзакции и приложения ASP.NET

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

Транзакции хранимых процедур

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

В этом примере проверяется значение @@TRANCOUNT для определения того, выполняется ли транзакция. (Переменная @@TRANCOUNT подсчитывает количество активных транзакций для текущего соединения. Оператор BEGIN STATEMENT увеличивает @@TRANCOUNT на единицу, в то время как операторы ROLLBACK и COMMIT это значение на единицу уменьшают.) Чтобы предотвратить молчаливое подавление ошибки в блоке catch, используется оператор RAISERROR. ADO.NET транслирует это сообщение в объект SqlException, который понадобится перехватить в своем коде .NET.

Инициированные клиентом транзакции ADO.NET

Большинство поставщиков данных ADO.NET включают поддержку баз данных. Транзакции стартуют через объект Connection вызовом метода BeginTransaction().

Класс Transaction предоставляет два ключевых метода.

Типы возвращаемых значений действий контроллера в веб-API ASP.NET Core Controller action return types in ASP.NET Core web API

ASP.NET Core предоставляет следующие параметры для типов возвращаемых значений действий контроллера веб-API: ASP.NET Core offers the following options for web API controller action return types:

В этом документе объясняется, когда лучше использовать каждый тип возвращаемого значения. This document explains when it’s most appropriate to use each return type.

Определенный тип Specific type

Простейшее действие возвращает элементарный или сложный тип данных (например, string или пользовательский тип объекта). The simplest action returns a primitive or complex data type (for example, string or a custom object type). Рассмотрим следующее действие, которое возвращает коллекцию пользовательских объектов Product : Consider the following action, which returns a collection of custom Product objects:

Если не известны условия, которые необходимо соблюдать при выполнении действия, конкретного типа будет достаточно. Without known conditions to safeguard against during action execution, returning a specific type could suffice. Предыдущее действие не принимает параметры, поэтому проверка ограничений параметров не требуется. The preceding action accepts no parameters, so parameter constraints validation isn’t needed.

Если в действии необходимо учитывать известные условия, используется несколько путей возврата. When known conditions need to be accounted for in an action, multiple return paths are introduced. В этом случае рекомендуется комбинировать тип возвращаемого значения класса ActionResult с примитивным или сложным типом возвращаемого значения. In such a case, it’s common to mix an ActionResult return type with the primitive or complex return type. Для этого типа действия требуется IActionResult или ActionResult . Either IActionResult or ActionResult are necessary to accommodate this type of action.

Получение IEnumerable или IAsyncEnumerable Return IEnumerable or IAsyncEnumerable

В ASP.NET Core 2.2 и более ранних версиях получение интерфейса IAsyncEnumerable из действия приводит к тому, что сериализатор выполняет синхронную итерацию операции сбора. In ASP.NET Core 2.2 and earlier, returning IAsyncEnumerable from an action results in synchronous collection iteration by the serializer. В результате вызовы блокируются, что может стать причиной перегрузки пула потоков. The result is the blocking of calls and a potential for thread pool starvation. Представьте, что Entity Framework (EF) Core используется веб-API для доступа к данным. To illustrate, imagine that Entity Framework (EF) Core is being used for the web API’s data access needs. Во время сериализации выполняется синхронное перечисление для типа возвращаемого значения следующего действия: The following action’s return type is synchronously enumerated during serialization:

Чтобы не допустить синхронного перечисления и блокировки операций ожидания для базы данных в ASP.NET Core 2.2 и более ранних версий, вызовите ToListAsync : To avoid synchronous enumeration and blocking waits on the database in ASP.NET Core 2.2 and earlier, invoke ToListAsync :

В ASP.NET Core 3.0 и более поздних версиях получение IAsyncEnumerable из действия: In ASP.NET Core 3.0 and later, returning IAsyncEnumerable from an action:

  • больше не приводит к синхронной итерации; No longer results in synchronous iteration.
  • по эффективности не отличается от получения IEnumerable . Becomes as efficient as returning IEnumerable .

ASP.NET Core 3.0 и более поздних версий помещает результаты следующего действия в буфер перед предоставлением его сериализатору: ASP.NET Core 3.0 and later buffers the result of the following action before providing it to the serializer:

Мы рекомендуем объявлять тип возвращаемого значения для сигнатуры действия как IAsyncEnumerable для гарантированного выполнения асинхронной итерации. Consider declaring the action signature’s return type as IAsyncEnumerable to guarantee the asynchronous iteration. То есть режим итерации зависит от возвращаемого базового конкретного типа. Ultimately, the iteration mode is based on the underlying concrete type being returned. MVC автоматически буферизует все конкретные типы, которые реализуют IAsyncEnumerable . MVC automatically buffers any concrete type that implements IAsyncEnumerable .

Рассмотрим следующее действие, которое возвращает записи о продуктах со сниженной ценой как IEnumerable

: Consider the following action, which returns sale-priced product records as IEnumerable

для предшествующего действия является: The IAsyncEnumerable

equivalent of the preceding action is:

Начиная с версии ASP.NET Core 3.0, оба предшествующих действия не являются блокирующими. Both of the preceding actions are non-blocking as of ASP.NET Core 3.0.

Тип IActionResult IActionResult type

Тип возвращаемого значения IActionResult можно использовать, если в действии допускаются несколько типов возвращаемого значения ActionResult . The IActionResult return type is appropriate when multiple ActionResult return types are possible in an action. Типы ActionResult представляют различные коды состояния HTTP. The ActionResult types represent various HTTP status codes. Любой неабстрактный класс, унаследованный от квалификаторов ActionResult в виде допустимого типа возвращаемого значения. Any non-abstract class deriving from ActionResult qualifies as a valid return type. Некоторые распространенные типы возвращаемых значений в этой категории: BadRequestResult (400), NotFoundResult (404) и OkObjectResult (200). Some common return types in this category are BadRequestResult (400), NotFoundResult (404), and OkObjectResult (200). Кроме того, удобные методы в классе ControllerBase можно использовать для получения типов ActionResult из действия. Alternatively, convenience methods in the ControllerBase class can be used to return ActionResult types from an action. Например, return BadRequest(); — это сокращенная форма return new BadRequestResult(); . For example, return BadRequest(); is a shorthand form of return new BadRequestResult(); .

Так как в типе действия есть несколько типов возвращаемого значения и путей, необходимо использовать атрибут [ProducesResponseType]. Because there are multiple return types and paths in this type of action, liberal use of the [ProducesResponseType] attribute is necessary. Этот атрибут создает описательные сведения об ответе для страниц справки по веб-API, создаваемых с помощью таких инструментов, как Swagger. This attribute produces more descriptive response details for web API help pages generated by tools like Swagger. [ProducesResponseType] указывает известные типы и коды состояния HTTP, возвращаемые действием. [ProducesResponseType] indicates the known types and HTTP status codes to be returned by the action.

Синхронное действие Synchronous action

Рассмотрим следующее синхронное действие, в котором возможны два типа возвращаемых значений: Consider the following synchronous action in which there are two possible return types:

В предшествующем действии: In the preceding action:

  • возвращается код состояния 404, если продукт, представленный id , не существует в базовом хранилище данных; A 404 status code is returned when the product represented by id doesn’t exist in the underlying data store. вызывается удобный метод NotFound в качестве сокращения return new NotFoundResult(); . The NotFound convenience method is invoked as shorthand for return new NotFoundResult(); .
  • Код состояния 200 возвращается с объектом Product , если продукт не существует. A 200 status code is returned with the Product object when the product does exist. Удобный метод Ok вызывается как сокращение для return new OkObjectResult(product); . The Ok convenience method is invoked as shorthand for return new OkObjectResult(product); .

Асинхронное действие Asynchronous action

Рассмотрим следующее асинхронное действие, в котором возможны два типа возвращаемых значений: Consider the following asynchronous action in which there are two possible return types:

В предшествующем действии: In the preceding action:

  • Код состояния 400 возвращается, если описание продукта содержит строку XYZ Widget. A 400 status code is returned when the product description contains «XYZ Widget». Удобный метод BadRequest вызывается как сокращение для return new BadRequestResult(); . The BadRequest convenience method is invoked as shorthand for return new BadRequestResult(); .
  • Код состояния 201 генерируется удобным методом CreatedAtAction при создании продукта. A 201 status code is generated by the CreatedAtAction convenience method when a product is created. В качестве альтернативы вызову CreatedAtAction можно использовать return new CreatedAtActionResult(nameof(GetBy >. An alternative to calling CreatedAtAction is return new CreatedAtActionResult(nameof(GetBy >. В этом пути к коду объект Product предоставляется в тексте ответа. In this code path, the Product object is provided in the response body. Также предоставляется заголовок ответа Location с URL-адресом только что созданного продукта. A Location response header containing the newly created product’s URL is provided.

Например, следующая модель указывает на то, что запросы должны включать свойства Name и Description . For example, the following model indicates that requests must include the Name and Description properties. Если Name и Description не были указаны в запросе, происходит сбой проверки модели. Failure to provide Name and Description in the request causes model validation to fail.

Если атрибут [ApiController] применяется в ASP.NET Core 2.1 и более поздних версиях, ошибки при проверке модели приводят к коду состояния 400. If the [ApiController] attribute in ASP.NET Core 2.1 or later is applied, model validation errors result in a 400 status code. Дополнительные сведения см. в разделе Автоматические отклики HTTP 400. For more information, see Automatic HTTP 400 responses.

Тип ActionResult ActionResult type

ASP.NET Core 2.1 предоставляет тип возвращаемого значения ActionResult для действий контроллера веб-API. ASP.NET Core 2.1 introduced the ActionResult return type for web API controller actions. Он позволяет возвращать тип, производный от ActionResult или определенный тип. It enables you to return a type deriving from ActionResult or return a specific type. ActionResult имеет следующие преимущества по сравнению с типом IActionResult: ActionResult offers the following benefits over the IActionResult type:

  • Свойство Type атрибута [ProducesResponseType] можно исключить. The [ProducesResponseType] attribute’s Type property can be excluded. Например, [ProducesResponseType(200, Type = typeof(Product))] упрощается до [ProducesResponseType(200)] . For example, [ProducesResponseType(200, Type = typeof(Product))] is simplified to [ProducesResponseType(200)] . Ожидаемый тип возвращаемого значения действия вместо этого выводится из T в ActionResult . The action’s expected return type is instead inferred from the T in ActionResult .
  • Операторы неявного приведения поддерживают преобразование T и ActionResult в ActionResult . Implicit cast operators support the conversion of both T and ActionResult to ActionResult . T преобразуется в ObjectResult, то есть return new ObjectResult(T); упрощается до return T; . T converts to ObjectResult, which means return new ObjectResult(T); is simplified to return T; .

C# не поддерживает операторы неявных приведений в интерфейсах. C# doesn’t support implicit cast operators on interfaces. Следовательно, для преобразования в конкретный тип необходимо использовать ActionResult . Consequently, conversion of the interface to a concrete type is necessary to use ActionResult . Например, использование IEnumerable не работает в следующем примере: For example, use of IEnumerable in the following example doesn’t work:

Один из способов исправить приведенный выше код — возвратить _repository.GetProducts().ToList(); . One option to fix the preceding code is to return _repository.GetProducts().ToList(); .

Большинство действий имеют тип возвращаемого значения. Most actions have a specific return type. При выполнении действия могут возникнуть непредвиденные условия, и в этом случае определенный тип не возвращается. Unexpected conditions can occur during action execution, in which case the specific type isn’t returned. Например, входной параметр действия может не пройти проверку модели. For example, an action’s input parameter may fail model validation. В этом случае обычно возвращается подходящий тип ActionResult вместо конкретного типа. In such a case, it’s common to return the appropriate ActionResult type instead of the specific type.

Синхронное действие Synchronous action

Рассмотрим синхронное действие, в котором возможны два типа возвращаемых значений: Consider a synchronous action in which there are two possible return types:

В предшествующем действии: In the preceding action:

  • возвращается код состояния 404, если продукт не существует в базе данных; A 404 status code is returned when the product doesn’t exist in the database.
  • возвращается код состояния 200 с соответствующим объектом Product , если продукт существует. A 200 status code is returned with the corresponding Product object when the product does exist. До версии ASP.NET Core 2.1 строка return product; имела бы вид return Ok(product); . Before ASP.NET Core 2.1, the return product; line had to be return Ok(product); .

Асинхронное действие Asynchronous action

Рассмотрим асинхронное действие, в котором возможны два типа возвращаемых значений: Consider an asynchronous action in which there are two possible return types:

В предшествующем действии: In the preceding action:

Как вызвать через include ASP с параметром

то, конечно, работает, но нужно сразу в адрес задать параметр, например, так:

Но говорит, что нет такого файла.
Дык, как сделать так, чтобы вызвать ASP с определенными параметрами а адресе?

17.08.2007, 23:09

Как использовать ASP в include?
Я хочу вместо zzzz программно подставлять разные значения, но у меня не работает. Помогите, как.

Как вызвать метод, параметром которого является делегат СompareDelegate
Здравствуйте! Помогите разобраться с использованием делегата. У меня задача, в ней есть класс.

Условный в ASP
Возможно ли условное включение файлов в ASP? Код подобный 6 17.08.2007, 23:56 2

Директива Include не ВЫЗЫВАЕТ файл а ВКЛЮЧАЕТ его в текст данного файла (еще до компиляции) как если бы включаемый файл был бы частью данного.

Так что вопрос не совсем корректен.

18.08.2007, 01:36 3 18.08.2007, 15:20 [ТС] 4

18.08.2007, 21:06 5

А не хочешь необходимый параметр засунуть в переменную сессии.
Делается так: Session(‘variable’) = ‘всех обманем’
Дальше в том файле читаешь таким же образом в нужном файле: Response.Write Session(‘variable’)

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

18.08.2007, 21:17 6 18.08.2007, 21:21 7

Execute
The Execute method calls an .asp file and processes it as if it were part of the calling ASP script. The Execute method is similar to a procedure call in many programming languages.

Syntax
Server.Execute( Path )

Parameters
Path
A string specifying the location of the .asp file to execute. If an absolute path is specified for this parameter then it must be for an .asp file within the same application space.
Remarks
The Server.Execute method provides a way of dividing a complex application into individual modules. By employing the Server.Execute method, you can develop a library of .asp files that you can call as needed. This approach is an alternative to server-side includes.

After IIS processes the .asp file specified in the input parameter to Server.Execute, the response is returned to the calling ASP script. The executed .asp file may modify HTTP headers. However, as with any .asp file, if the executed .asp file attempts to modify HTTP headers after it sends a response to the client, it will generate an error.

The path parameter may be for either an absolute or a relative path. If the path is absolute, it must map to an ASP script in the same application as the calling .asp file.

The path parameter may contain a query string.

If either the calling or called .asp file contains a transaction directive, the status of the transaction will apply to the .asp file which contains the directive. For example, if ASP1 below calls ASP2 and the transaction is aborted while ASP2 is being processed, ASP2’s OnTransactionAbort (if present) will be called. After ASP2 completes processing, ASP1’s OnTransactionAbort (if present) will be called.

Example
The following example demonstrates executing an .asp file that returns some text. The output from these two scripts is:

Что такое код asp ontransactioncommit

The OnTransactionCommit event occurs after a transactional script transacts. When the OnTransactionCommit event occurs, IIS will process the script’s OnTransactionCommit subroutine, if it exists.

The following example sends a response to the client when the transaction commits:

Client: Requires Windows XP Professional, Windows 2000 Professional, or Windows NT Workstation 4.0.

Server: Requires Windows Server 2003, Windows 2000 Server, or Windows NT Server 4.0.

Transaction — Commit after user confirm

I have a method where the user changed many fields and save the result. During updating process (transactional), it’s necessary to do many checks if others process are afected by the changes. If this occurs, it is necessary to request user confirmation before proceeding with the process. If it confirms the commit of the transaction is performed, otherwise rollback is required.

It’ not possible to perform these validations without changing the tables in question, since these changes have a direct impact on the necessary validations.

Is it possible to keep an open transaction in the bank until user confirmation? How to do this using Asp .Net MVC and TransactionScope (Oracle DataBase)?

Авторизация и аутентификация в MVC 5

ASP.NET >Последнее обновление: 31.10.2015

Релиз ASP.NET MVC 5 ознаменовался выходом новой системой авторизации и аутентификации в .NET приложениях под названием ASP.NET Identity. Эта система пришла на смену провайдерам Simple Membership, которые были введены в ASP.NET MVC 4.

Илон Маск рекомендует:  Что такое код setsoundnoise

Рассмотрим систему авторизации и аутентификации ASP.NET Identity на примере приложения MVC 5. Итак, при создании приложения MVC 5 Visual Studio предлагает нам выбрать один из типов аутентификации:

Нажав на кнопку Change Authentication , мы можем изменить тип аутентификации, выбрав одно из следующих:

No Authentication : ASP.NET Identity и встроенная система аутентификации отсутствует

Individual User Accounts : проект по умолчанию включает систему ASP.NET Identity, которая позволяет авторизовать как пользователей внутри приложения, так и с помощью внешних сервисов, как google, твиттер и т.д.

Organizational Accounts : подходит для сайтов и веб-приложений отдельных компаний и организаций

Windows Authentication : система аутентификации для сетей intranet с помощью учетных записей Windows

Оставим значение по умолчанию, то есть Individual User Accounts и создадим проект.

Созданный проект уже по умолчанию имеет всю необходимую для авторизации инфраструктуру: модели, контроллеры, представления. Если мы заглянем в узел References (Библиотеки), то увидим там ряд ключевых библиотек, которые и содержит необходимые для авторизации и аутентификации классы:

Это ряд библиотек OWIN, которые добавляют функциональность OWIN в проект, а также три библиотеки собственно ASP.NET Identity:

Microsoft.AspNet.Identity.EntityFramework : содержит классы Entity Framework, применяющие ASP.NET Identity и осуществляющие связь с SQL Serveroм

Microsoft.AspNet.Identity.Core : содержит ряд ключевых интерфейсов ASP.NET Identity. Реализация этих интерфейсов позволит выйти за рамки MS SQL Server и использовать в качестве хранилища учетных записей другие СУБД, в том числе системы NoSQL

Microsoft.AspNet.Identity.OWIN : привносит в приложение ASP.NET MVC аутентификацию OWIN с помощью ASP.NET Identity

Поскольку вся инфраструктура уже имеется в проекте, запустим проект, на отобразившейся в браузере странице найдем ссылку Register и нажмем на нее. На открывшейся странице регистрации введем какие-нибудь данные:

После регистрации логин будет отображаться в правом верхнем углу веб-страницы веб-приложения. Осуществив регистрацию, мы можем разлогиниться, нажав на LogOff, и снова войти в систему. Таким образом, мы можем уже начать пользоваться встроенной системой аутентификации в приложении ASP.NET MVC 5. Теперь же рассомотрим ее основные моменты.

Во-первых, где это все хранится? Куда попадают данные зарегистрированных пользователей?

В данном случае используется подход Code First. В файле web.config уже имеется строка подключения по умолчанию, которая задает каталог базы данных. Если мы раскроем папку App_Data, то сможем увидеть созданную базу данных:

Если вдруг в папке база данных не видна, нажмем вверху окна Solution Explorer на кнопку Show All Files (Показать все файлы).

Мы можем открыть эту базу данных в окне Server Explorer и увидеть ее содержимое:

По умолчанию при регистрации первого пользователя создается следующий набор таблиц:

_MigrationHistory : используется EntityFramework для миграций БД

AspNetRoles : содержит определения ролей

AspNetUserClaims : таблица, хранящая набор клеймов (claim). Claim представляет иную модель авторизации по сравнению с ролями. Грубо говоря, claim содержит некоторую информацию о пользователе, например, адрес электронной почты, логин, возраст и т.д. И эта информация позволяет идентифицировать пользователя и наделить его соответствующими правами доступа.

AspNetUserLogins : таблица логинов пользователя

AspNetUserRoles : таблица, устанавливающая для пользователей определенные роли

AspNetUsers : собственно таблица пользователей. Если мы ее откроем, то увидим данные зарегистрированного пользователя

Ключевыми объектами в AspNet >пользователи и роли . Вся функциональность по созданию, удалению пользователей, взаимодействию с хранилищем пользователей хранится в классе UserManager . Для работы с ролями и их управлением в AspNet >RoleManager . Классы UserManager и RoleManager находятся в библиотеке Microsoft.AspNet.Identity.Core.

Каждый пользователь для UserManager представляет объект интерфейса IUser. А все операции по управлению пользователями производятся через хранилище, представленное объектом IUserStore.

Каждая роль представляет реализацию интерфейса IRole, а управление ролями классом RoleManager происходит через хранилище IRoleStore.

Непосредственную реализацию интерфейсов IUser, IRole, IUserStore и IRoleStore предоставляет пространство имен Microsoft.AspNet.Identity.EntityFramework:

Класс IdentityUser является реализацией интерфейса IUser. А класс хранилища пользователей — UserStore реализует интерфейс IUserStore.

Подобным образом класс IdentityRole реализует интерфейс IRole, а класс хранилища ролей — RoleStore реализует интерфейс IRoleStore.

А для взаимодействия с базой данных в пространстве имен Microsoft.AspNet. >IdentityDbContext

В приложении ASP.NET MVC мы не будем работать напрямую с классами IdentityUser и IdentityDbContext. По умолчанию в проект в папку Models добавляется файл IdentityModels.cs, который содержит определения классов пользователей и контекста данных:

В приложении мы не работаем напрямую с классами IdentityUser и IdentityDbContext, а имеем дело с классами-наследниками.

Класс ApplicationUser наследует от IdentityUser все свойства. И кроме того добавляет метод GenerateUserIdentityAsync() , в котором с помощью вызова UserManager.CreateIdentityAsync создается объект ClaimsIdentity . Данный объект содержит информацию о данном пользователе.

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

Я не буду подробно расписывать весь функционал AspNet Identity, который по умолчанию добавляется в проект, обозначу вкратце лишь основные возможности.

Во-первых, чтобы задействовать AspNet Identity, в проект в папку App_Start добавляются два файла. Файл Startup.Auth.cs содержит класс запуска приложения OWIN. Поскольку AspNet Identity использует инфраструктуру OWIN, то данный класс является одним из ключевых и необходимых для работы.

Файл IdentityConfig.cs содержит ряд дополнительных вспомогательных классов: сервисы для двухфакторной валидации с помощью email и телефона EmailService и SmsService , класс менеджера пользователей ApplicationUserManager , добавляющий к UserManager ряд дополнительных функций, и класс ApplicationSignInManager , используемый для входа и выхода с сайта.

Базовая функциональность системы аутентификации и управления учетными записями расположена в двух контроллерах: AccountController и ManageController

В AccountController определены методы для логина, регистрации, верификации кода, отправленного по email или по смс, сброс пароля, напоминание пароля, вход на сайт с помощью внешних сервисов. Контроллер ManageController используется для управления учетной записью и предполагает возможности по смене пароля и управлению телефонными номерами в системе. Для обоих контроллеров уже по умолчанию генерируются все необходимые представления и специальные модели представлений.

Несмотря на то, что по умолчанию нам уже предоставляется готовый функционал, однако в ряде случаев, например, для отправки смс или электронной почты необходима дополнительная настройка. Теперь рассмотрим основные моменты системы AspNet Identity.

Что такое код asp ontransactioncommit

ПРИМЕНЯЕТСЯ К: SQL Server (начиная с 2008) База данных SQL Azure Хранилище данных SQL Azure Parallel Data Warehouse

Отмечает успешное завершение явной или неявной транзакции. Если @@TRANCOUNT -1, инструкция COMMIT TRANSACTION делает все изменения данных, проведенных с момента начала транзакции постоянной частью базы данных, освобождает ресурсы, удерживаемые транзакцией и уменьшает значение @@TRANCOUNT 0. Если @@TRANCOUNT больше 1, инструкция COMMIT TRANSACTION уменьшает значение @@TRANCOUNT только по 1 и транзакция остается активной.

transaction_name
ПРИМЕНЯЕТ Кому: SQL Server и базы данных Azure SQL

Не учитывается компонентом Компонент SQL Server Database Engine. transaction_name указывает имя транзакции, назначенный предыдущих BEGIN TRANSACTION. transaction_nameдолжны соответствовать правилам для идентификаторов, но не может превышать 32 символов. transaction_name может использоваться как подсказка, показывающая программистам, которые BEGIN TRANSACTION, инструкция COMMIT TRANSACTION, связанные с вложенными.

@tran_name_variable
ПРИМЕНЯЕТ Кому: SQL Server и базы данных Azure SQL

Имя определенной пользователем переменной, содержащей допустимое имя транзакции. Переменная должна быть объявлена с типом данных char, varchar, nvarchar или nchar. Если переменной присваивается значение длиной более 32 символов, используются только первые 32, остальные усекаются.

DELAYED_DURABILITY
ПРИМЕНЯЕТ Кому: SQL Server и базы данных Azure SQL

Параметр, который запрашивает фиксацию этой транзакции с задержанной устойчивостью. Этот запрос пропускается, если база данных была изменена с использованием DELAYED_DURABILITY = DISABLED или DELAYED_DURABILITY = FORCED . См. в разделе управление устойчивостью транзакций подробнее.


Обязанностью программиста на языке Transact-SQL является вызов инструкции COMMIT TRANSACTION только в том случае, когда все данные, относящиеся к транзакции, логически верны.

Если зафиксированная транзакция является распределенной транзакцией Transact-SQL, инструкция COMMIT TRANSACTION вызывает координатор MS DTC для использования двухфазного протокола фиксации на всех серверах, участвующих в транзакции. Если локальная транзакция охватывает две или более базы данных одного и того же экземпляра компонента Компонент Database Engine, то экземпляр использует встроенный двухэтапный алгоритм для фиксирования всех баз данных, вызванных в транзакции.

При использовании вложенных транзакций фиксация внутренних транзакций не освобождает ресурсы и не делает их изменения постоянными. Изменения данных становятся постоянными и ресурсы освобождаются только при фиксации внешней транзакции. Выданный каждой инструкции COMMIT TRANSACTION, когда @@TRANCOUNT больше, чем 1 просто уменьшает значение @@TRANCOUNT на 1. Если @@TRANCOUNT — уменьшится до нуля, вся внешняя транзакция зафиксирована. Поскольку transaction_name игнорируется Компонент Database Engine, выполнив COMMIT TRANSACTION, ссылаясь на имя внешней транзакции, если существуют необработанные внутренние транзакции только уменьшает значение @@TRANCOUNT на 1.

Вызов инструкции COMMIT TRANSACTION при @@TRANCOUNT 0 приводит к возникновению ошибки; нет соответствующей инструкции BEGIN TRANSACTION.

Нельзя произвести откат транзакции после вызова инструкции COMMIT TRANSACTION, так как измененные данные уже стали частью базы данных.

Компонент Database Engine увеличивает счетчик транзакций внутри выражения, только когда счетчик транзакций равен нулю при запуске инструкции.

Необходимо быть членом роли public .

A. Фиксация транзакции

ПРИМЕНЯЕТ Кому: SQL Server, база данных Azure SQL, хранилище данных Azure SQL и параллельные хранилища данных

В следующем примере удаляется кандидат на вакансию. Она использует AdventureWorks.

Б. Фиксация вложенной транзакции

ПРИМЕНЯЕТ Кому: SQL Server и базы данных Azure SQL

В следующем примере создается таблица и формируется три уровня вложенных транзакций, которые затем фиксируются. Хотя каждый COMMIT TRANSACTION инструкция имеет transaction_name параметр, не существует связи между COMMIT TRANSACTION и BEGIN TRANSACTION инструкции. Transaction_name параметры являются просто средства обеспечения удобочитаемости кода помогают программисту удостовериться что закодировано верное количество фиксированных Уменьшаемое @@TRANCOUNT 0 и таким образом зафиксировать внешнюю транзакцию.

Инфраструктура System.Transactions в мире .NET

Встречали ли вы в C# конструкцию типа using (var scope = new TransactionScope(TransactionScopeOption.Required)) ? Это значит, что код, выполняющийся в блоке using , заключается в транзакцию и после выхода из этого блока изменения будут зафиксированы или отменены. Звучит понятно, пока не начинаешь копать глубже. И чем глубже копаешь, тем «страньше и страньше» становится. Во всяком случае, у меня при более близком знакомстве с классом TransactionScope и вообще транзакциями .NET возникла целая уйма вопросов.

Что за класс TransactionScope ? Как только мы используем конструкцию using (var scope = new TransactionScope()) , все в нашей программе сразу становится транзакционным? Что такое «управляющий ресурсами» (Resource Manager) и «управляющий транзакциями» (Transaction Manager)? Можно ли написать свой управляющий ресурсами и как он «подключается» к созданному экземпляру TransactionScope ? Что такое распределенная транзакция и правда ли, что распределенная транзакция в SQL Server или Oracle Database — это то же самое, что и распределенная транзакция .NET?

В данной публикации я постарался собрать материал, помогающий найти ответы на указанные вопросы и сформировать понимание транзакций в мире .NET.

Введение

Что такое транзакции и какие проблемы они решают

Транзакции, о которых здесь идет речь, — это операции, переводящие систему из одного приемлемого состояния в другое и гарантированно не оставляющие систему в неприемлемом состоянии даже при возникновении непредвиденных ситуаций. Что это за приемлемые состояния, в общем случае зависит от контекста. Здесь мы будем считать приемлемой ситуацию, в которой обрабатываемые нами данные целостны. При этом подразумевается, что изменения, составляющие транзакцию, все вместе либо совершаются, либо не совершаются. Кроме того, изменения одной транзакции могут быть изолированы от изменений, вносимых в систему другой транзакцией. Основные требования, предъявляемые к транзакциям, обозначаются аббревиатурой ACID. Для первого знакомства с ними подойдет статья в «Википедии».

Классический пример транзакции — перевод денег между двумя счетами. В этой ситуации снятие денег со счета № 1 без зачисления на счет № 2 неприемлемо, точно так же, как и зачисление на счет № 2 без снятия со счета № 1. Другими словами, мы хотим, чтобы обе операции — и снятие, и зачисление — выполнялись сразу. Если же какую-то из них выполнить не удастся, то и вторая операция выполняться не должна. Можно называть этот принцип «все или ничего». Более того, желательно, чтобы операции выполнялись синхронно даже в случае таких системных сбоев, как отключение электроэнергии, то есть чтобы мы видели систему в приемлемом состоянии, как только она станет доступна после восстановления.

В математических терминах можно сказать, что относительно системы существует инвариант, который мы хотели бы обязательно сохранить. Например, сумма на обоих счетах: нужно, чтобы после транзакции (перевода денег) сумма осталась такой же, как и до нее. Кстати, в классическом примере с переводом денег еще и фигурирует учет — предметная область, где естественным образом возникло понятие транзакции.

Проиллюстрируем пример с переводом денег между двумя счетами. На первой картинке изображена ситуация, когда перевод 50 рублей со счета № 1 на счет № 2 завершился успешно. Зеленый цвет подчеркивает, что система находится в приемлемом состоянии (данные целостны).

Теперь представим, что перевод осуществляется вне транзакции и после снятия денег со счета № 1 произошел сбой, из-за которого снятые деньги не были начислены на счет № 2. Система окажется в неприемлемом состоянии (красный цвет).

Если же ошибка произошла между операциями снятия и зачисления, но перевод осуществлялся в рамках одной транзакции, то операция снятия денег будет отменена. В результате система останется в исходном приемлемом состоянии.

Приведу примеры ситуаций из опыта нашей компании, в которых транзакции бывают полезны: учет товаров (учет количества товаров разных видов, которые находятся в определенных хранилищах и в пути), учет ресурсов хранилищ (учет объема помещения, который занимают товары определенного типа, объема помещения, свободного для размещения товаров, количества товаров, которое могут переместить сотрудники и автоматизированные системы хранилища за день).

Проблемы, возникающие при нарушении целостности данных, очевидны. Информация, предоставляемая системой, не просто становится недостоверной — она теряет связь с действительностью и превращается в бессмыслицу.

Какие транзакции здесь рассматриваются

Предоставляемые транзакциями выгоды известны. Так что же, для поддержания целостности данных нам нужна реляционная база данных, поскольку именно там делаются транзакции? Не совсем. Выше было сказано, что понятие транзакции зависит от контекста, и сейчас кратко рассмотрим, о каких транзакциях можно говорить при обсуждении информационных систем.

Для начала разделим понятия транзакций предметной области (бизнес-транзакций) и системных транзакций. Вторые могут быть реализованы в разных местах и разными способами.

Зайдем с самого высокого уровня — предметной области. Интересант может объявить, что существуют некоторые приемлемые состояния и он не желает видеть информационную систему вне этих состояний. Не будем придумывать лишних примеров: перевод денег между счетами здесь подходит. Только уточним, что перевод — это не обязательно перевод денег между расчетными счетами двух клиентов банков. Не менее важна и задача бухгалтерского учета, когда счета должны отражать источники и предназначение средств организации, а перевод — изменение распределения средств по этим источникам и предназначению. Это был пример транзакции предметной области.

Теперь посмотрим самые распространенные и интересные примеры реализации системных транзакций. В системных транзакциях разными техническими средствами обеспечиваются требования предметной области. Классическим проверенным решением такого рода являются транзакции реляционных СУБД (первый пример). Современные системы управления БД (как реляционными, так и не очень) предоставляют механизм транзакций, который позволяет или сохранить (зафиксировать) все сделанные за указанный период работы изменения, или отказаться от них (откатить). При использовании такого механизма операции снятия денег с одного счета и зачисления на другой счет, составляющие транзакцию предметной области, средствами СУБД будут объединены в системную транзакцию и либо выполнятся вместе, либо не выполнятся вовсе.

Пользоваться СУБД, конечно, не обязательно. Грубо говоря, можно вообще реализовать механизм транзакций СУБД на своем любимом языке программирования и наслаждаться неустойчивым и заваленным ошибками аналогом существующих инструментов. Зато ваш «велосипед» может быть оптимизирован для конкретных ситуаций предметной области.

Есть и более интересные варианты. Современные промышленные языки программирования (C# и Java в первую очередь) предлагают средства, предназначенные специально для организации транзакций с участием совершенно разных подсистем, а не только СУБД. В данной публикации будем называть такие транзакции программными. В случае C# это транзакции из пространства имен System.Transactions (второй пример), именно о них и рассказывается ниже.

Прежде чем перейти к транзакциям System.Transactions , нельзя не упомянуть еще одно интересное явление. Средства System.Transactions позволяют программисту самостоятельно реализовать программную транзакционную память. В этом случае программные операции, влияющие на состояние системы (в случае классических императивных языков программирования это операция присваивания), по умолчанию включаются в транзакции, которые можно фиксировать и откатывать почти так же, как и транзакции СУБД. При таком подходе необходимость использовать механизмы синхронизации (в C# — lock , в Java — synchronized ) значительно снижается. Дальнейшим развитием этой идеи является программная транзакционная память, поддерживаемая на уровне платформы (третий пример). Такое чудо ожидаемо обнаруживается в языке, изящность которого превосходит его промышленную применимость, — Clojure. А для рабоче-крестьянских языков существуют подключаемые библиотеки, предоставляющие функциональность программной транзакционной памяти.

Системные транзакции могут включать несколько информационных систем, в таком случае они становятся распределенными. Распределенными могут быть и транзакции СУБД, и программные; все зависит от того, какую функциональность поддерживает конкретное средство реализации транзакции. Подробнее распределенные транзакции рассматриваются в соответствующем разделе. Приведу картинку, чтобы было легче разобраться в обсуждаемых предметах.

TL;DR по разделу

Существуют процессы, которые состоят из нескольких неделимых (атомарных) операций, применяемых к системе, в общем случае не обязательно информационной. Каждая неделимая операция может оставить систему в неприемлемом состоянии, когда целостность данных нарушается. Например, если перевод денег между двумя счетами представлен двумя неделимыми операциями снятия со счета № 1 и зачисления на счет № 2, то выполнение только одной из этих операций нарушит целостность данных. Деньги или пропадут неизвестно куда, или появятся неизвестно откуда. Транзакция объединяет неделимые операции так, чтобы они выполнялись все вместе (разумеется, последовательно, если это необходимо) или не выполнялись вообще. Можно говорить о транзакциях предметной области и о транзакциях в технических системах, которые обычно реализуют транзакции предметной области.

Транзакции на основе System.Transactions

Что это

В мире .NET существует программный каркас, предназначенный создателями платформы для управления транзакциями. С точки зрения программиста, пользующегося транзакциями, этот каркас состоит из типов TransactionScope , TransactionScopeOption , TransactionScopeAsyncFlowOption и TransactionOptions пространства имен System.Transactions . Если говорить про .NET Standard, то все это доступно начиная с версии 2.0.

Транзакции из пространства имен System.Transactions основываются на стандарте X/Open XA от консорциума The Open Group. В этом стандарте вводятся многие термины, обсуждаемые далее, и, главное, описываются распределенные транзакции, которым в данной публикации также посвящен специальный раздел. На этом же стандарте основываются реализации программных транзакций и в других платформах, например в Java.

Типовой сценарий использования транзакций для программиста C# следующий:

Внутри блока using помещается код, выполняющий работу, результаты которой должны быть зафиксированы или отменены все вместе. Классические примеры такой работы — это чтение и запись в БД или отправка и получение сообщений из очереди. Когда управление покинет блок using , транзакция будет зафиксирована. Если убрать вызов Complete , то транзакция откатится. Довольно просто.

Получается, что во время отката транзакции все операции, сделанные внутри такого блока using , будут отменены? И если я присвоил какой-нибудь переменной другое значение, то у этой переменной восстановится старое значение? Когда я впервые увидел подобную конструкцию, то так и подумал. На самом деле, конечно, откатятся не все изменения, а только очень особенные. Если бы откатывались вообще все изменения, то это и было бы программной транзакционной памятью, описанной выше. А сейчас посмотрим, что это за особенные изменения, которые могут участвовать в программных транзакциях на основе System.Transactions .

Управляющие ресурсами

Чтобы нечто поддерживало транзакции на основе System.Transactions , нужно, чтобы оно обладало информацией о том, что в данный момент идет транзакционная работа, и чтобы оно регистрировалось в каком-то реестре участников транзакции. Получить информацию о том, идет ли транзакционная работа, можно, проверив статическое свойство Current класса System.Transactions.Transaction . Вход в блок using указанного выше вида как раз устанавливает это свойство, если оно не было установлено раньше. А чтобы зарегистрироваться как участник транзакции, можно использовать методы типа Transaction.EnlistSmth . Кроме этого, нужно реализовать интерфейс, требуемый данными методами. Управляющий ресурсами (Resource Manager) — это именно такое «нечто», поддерживающее взаимодействие с транзакциями из System.Transactions (более конкретное определение дано ниже).

Илон Маск рекомендует:  Что такое код declare cursor

Какие бывают управляющие ресурсами? Если мы работаем из C# с СУБД, например SQL Server или Oracle Database, то обычно пользуемся соответствующими драйверами, а они и являются управляющими ресурсами. В коде они представляются типами System.Data.SqlClient.SqlConnection и Oracle.ManagedDataAccess.Client.OracleConnection . Еще, говорят, MSMQ поддерживает транзакции на основе System.Transactions . Руководствуясь знаниями и примерами, почерпнутыми из интернета, можно создать и собственный управляющий ресурсами. Простейший пример приведен в следующем разделе.

Кроме управляющих ресурсами, у нас должен быть еще управляющий транзакциями (Transaction Manager), который будет следить за транзакцией и своевременно отдавать распоряжения управляющим ресурсами. В зависимости от того, какие управляющие ресурсами участвуют в транзакции (какие у них характеристики и где они расположены), к работе подключаются разные управляющие транзакциями. При этом выбор подходящей версии происходит автоматически и не требует вмешательства программиста.

Если говорить конкретнее, то управляющий ресурсами — это экземпляр класса, реализующего специальный интерфейс System.Transactions.IEnlistmentNotification . Экземпляр класса по указанию клиента регистрируется как участник транзакции, пользуясь статическим свойством System.Transactions.Transaction.Current . В дальнейшем управляющий транзакциями по мере необходимости вызывает методы указанного интерфейса.

Ясно, что во время выполнения может изменяться набор управляющих ресурсами, участвующих в транзакции. Например, после входа в блок using мы можем сначала сделать что-то в SQL Server, а потом — в Oracle Database. В зависимости от такого набора управляющих ресурсами и определяется используемый управляющий транзакциями. Если быть точнее, то по набору управляющих ресурсами определяется используемый транзакционный протокол, а уже на основе протокола определяется поддерживающий его управляющий транзакциями. Транзакционные протоколы мы рассмотрим позже, когда будем говорить о распределенных транзакциях. Механизм автоматического выбора подходящего управляющего транзакциями во время выполнения при изменении участвующих в транзакции управляющих ресурсами называется продвижением транзакции (Transaction Promotion).

Виды управляющих ресурсами

Управляющие ресурсами можно разделить на две большие группы: долговечные и непостоянные.

Долговечный управляющий ресурсами (Durable Resource Manager) — управляющий ресурсами, который поддерживает транзакцию даже в случае недоступности информационной системы (например, при перезапуске компьютера). Непостоянный управляющий ресурсами (Volatile Resource Manager) — управляющий ресурсами, который не поддерживает транзакцию в случае недоступности информационной системы. Непостоянный управляющий ресурсами поддерживает транзакции только в оперативной памяти.

Классические долговечные управляющие ресурсами — это СУБД (или драйвер СУБД для программной платформы). Что бы ни произошло — хоть сбой в работе операционной системы, хоть отключение электроэнергии, — СУБД будет гарантировать целостность данных после того, как она снова придет в рабочее состояние. За это, разумеется, приходится платить некоторыми неудобствами, но в данной статье мы их рассматривать не будем. Пример непостоянного управляющего ресурсами — упомянутая выше программная транзакционная память.

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

При создании объекта типа TransactionScope можно указывать некоторые параметры.

Во-первых, есть настройка, сообщающая среде выполнения о том, что нужно:

  1. пользоваться уже существующей к этому моменту транзакцией;
  2. обязательно создавать новую;
  3. наоборот, выполнять код внутри блока using вне транзакции.

За все это отвечает перечисление System.Transactions.TransactionScopeOption .

Во-вторых, можно задать уровень изоляции транзакции. Это параметр, который позволяет найти компромисс между независимостью изменений и скоростью работы. Самый независимый уровень — сериализуемый — гарантирует, что не существует таких ситуаций, когда изменения, внесенные в рамках одной еще не зафиксированной транзакции, можно увидеть в другой транзакции. Каждый следующий уровень добавляет по одной такой конкретной ситуации, когда одновременно выполняющиеся транзакции могут влиять друг на друга. По умолчанию транзакция открывается на сериализуемом уровне, что может быть неприятно (см., например, этот комментарий).

Настройка уровня изоляции транзакции при создании TransactionScope носит рекомендательный характер для управляющих ресурсами. Они могут даже не поддерживать все уровни, представленные в перечислении System.Transactions.IsolationLevel . Кроме того, надо учитывать, что при использовании пула соединений для работы с БД соединение, для которого был изменён уровень изоляции транзакции, по возвращении в пул сохранит этот уровень. Теперь, когда программист получит это соединение из пула и положится на значения по умолчанию, он будет наблюдать неожиданное поведение.

Типовые сценарии работы c TransactionScope и существенные подводные камни (а именно вложенные транзакции) хорошо освещены в этой статье на «Хабре».

Применимость программных транзакций

Следует сказать, что почти в любой информационной системе, находящейся в промышленной эксплуатации, запускаются процессы, которые могут привести систему в неприемлемое состояние. Поэтому бывает необходимо контролировать эти процессы, выяснять, является ли текущее состояние системы приемлемым, и, если нет, восстанавливать его. Программные транзакции — готовый инструмент для поддержания системы в приемлемом состоянии.

В каждом конкретном случае конструктивно было бы рассматривать стоимость:

  1. интеграции процессов в инфраструктуру программных транзакций (нужно, чтобы эти процессы знали о TransactionScope и о многих других вещах);
  2. поддержания этой инфраструктуры (например, стоимость аренды оборудования с Windows на борту);
  3. обучения сотрудников (так как тема транзакций .NET мало распространена).

Нельзя забывать и о том, что от транзакционного процесса может потребоваться сообщать о своем прогрессе «внешнему миру», например вести журнал действий вне транзакции.

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

Пример непостоянного управляющего ресурсами

Сейчас рассмотрим пример простейшего управляющего ресурсами, не поддерживающего восстановление после системного сбоя. У нас будет блок программной транзакционной памяти, хранящий некоторое значение, которое можно читать и перезаписывать. В отсутствие транзакции этот блок ведет себя как обычная переменная, а при наличии транзакции он сохраняет начальное значение, которое может быть восстановлено после отката транзакции. Код такого управляющего ресурсами представлен далее:

Видно, что единственным формальным требованием является реализация интерфейса System.Transactions.IEnlistmentNotification . Из интересного стоит отметить методы Enlist (не являющиеся частью System.Transactions.IEnlistmentNotification ) и Prepare . Метод Enlist как раз проверяет, работает ли данный код в рамках транзакции, и, если да, регистрирует экземпляр своего класса в качестве непостоянного управляющего ресурсами. Метод Prepare вызывается управляющим транзакциями перед фиксацией изменений. Наш управляющий ресурсами сигнализирует о своей готовности к фиксации, вызывая метод System.Transactions.PreparingEnlistment.Prepared .

Дальше приведен код, показывающий пример использования нашего управляющего ресурсами:

Если сразу после выхода из блока using прочитать свойство stm.Value , то там ожидаемо будет установленное значение 2 . А если убрать вызов scope.Complete , то транзакция откатится и в свойстве stm.Value будет значение 1 , заданное до начала транзакции.

Упрощенно последовательность вызовов при работе с транзакциями System.Transactions показана на приведенной ниже диаграмме.

Видно, что в данном примере рассмотрены не все возможности, предоставляемые инфраструктурой System.Transactions . Более полно мы рассмотрим их после того, как познакомимся с транзакционными протоколами и распределенными транзакциями в следующем разделе.


TL;DR по разделу

Программист может использовать класс TransactionScope для того, чтобы выполнить некоторый код в рамках имеющейся или новой транзакции. Транзакция фиксируется тогда и только тогда, когда у имеющегося экземпляра класса TransactionScope вызывается метод Dispose , при том что перед этим у него был вызван метод Complete . Программист может указывать, желает ли он непременно начать новую транзакцию, воспользоваться уже имеющейся или, наоборот, выполнить код вне имеющейся транзакции. В транзакции участвуют только управляющие ресурсами — программные компоненты, реализующие определенную функциональность. Управляющие ресурсами могут быть долговечными (восстанавливающимися после сбоя системы) и непостоянными (не восстанавливающимися). СУБД — пример долговечного управляющего ресурсами. Координацией управляющих ресурсами занимается управляющий транзакциями — программный компонент, который выбирается исполняющей средой автоматически, без участия программиста.

Непостоянный управляющий ресурсами — это класс, реализующий интерфейс System.Transactions.IEnlistmentNotification и в методе Prepare подтверждающий свою готовность к фиксации изменений или, наоборот, сигнализирующий об откате изменений. Когда вызывающая сторона что-то делает с управляющим ресурсами, он проверяет, открыта ли сейчас транзакция, и, если открыта, регистрируется с помощью метода System.Transactions.Transaction.EnlistVolatile .

Распределенные транзакции

Что это

Распределенная транзакция задействует несколько информационных подсистем (на самом деле не все так просто, ниже об этом рассказано подробнее). Подразумевается, что изменения во всех системах, участвующих в распределенной транзакции, должны или фиксироваться, или откатываться.

Выше были представлены разные средства реализации транзакций: СУБД, инфраструктура System.Transactions , встроенная в платформу программная транзакционная память. Этими инструментами могут обеспечиваться и распределенные транзакции. Например, в СУБД Oracle Database изменение (а на самом деле и чтение) данных в нескольких БД в рамках одной транзакции автоматически превращает ее в распределенную. Далее же пойдет речь о программных распределенных транзакциях, которые могут включать разнородные управляющие ресурсами.

Транзакционные протоколы

Транзакционный протокол — набор принципов, по которым взаимодействуют участвующие в транзакции приложения. В мире .NET чаще всего встречаются следующие протоколы.

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

OleTx. Допускается взаимодействие между несколькими доменами-приложениями и несколькими компьютерами. Допускается использование множества долговечных управляющих ресурсами. Все участвующие компьютеры должны находиться под управлением Windows. Используется удаленный вызов процедуры (Remote Procedure Calls, RPCs).

WS-AT. Допускается взаимодействие между несколькими доменами-приложениями и несколькими компьютерами. Допускается использование множества долговечных управляющих ресурсами. Участвующие компьютеры могут находиться под управлением различных ОС, не только Windows. Используется протокол передачи гипертекста (Hypertext Transmission Protocol, HTTP).

Выше было отмечено, что действующий транзакционный протокол влияет на выбор управляющего транзакциями, а на выбор протокола влияют характеристики управляющих ресурсов, участвующих в транзакции. Теперь перечислим известные управляющие транзакциями.

Lightweight Transaction Manager (LTM). Представлен в .NET Framework 2.0 и в более поздних версиях. Управляет транзакциями, использующими протокол Lightweight.

Kernel Transaction Manager (KTM). Представлен в Windows Vista и Windows Server 2008. Управляет транзакциями, использующими протокол Lightweight. Может вызывать транзакционную файловую систему (Transactional File System, TxF) и транзакционный реестр (Transactional Registry, TxR) в Windows Vista и Windows 2008.

Distributed Transaction Coordinator (MSDTC). Управляет транзакциями, использующими протоколы OleTx и WS-AT.

Нужно также иметь в виду, что часть управляющих ресурсами не поддерживает все перечисленные протоколы. Например, MSMQ и SQL Server 2000 не поддерживают Lightweight, поэтому транзакции с участием MSMQ или SQL Server 2000 будут управляться MSDTC, даже если они являются единственными участниками. Технически это ограничение возникает от того, что указанные управляющие ресурсами, реализуя, конечно, интерфейс System.Transactions.IEnlistmentNotification , не реализуют интерфейс System.Transactions.IPromotableSinglePhaseNotification . В нем есть, кроме прочего, метод Promote , который исполняющая среда вызывает при необходимости перейти на более «крутой» управляющий транзакциями.

Сейчас должна стать очевидной неоднозначность понятия распределенной транзакции. Например, можно определять распределенную транзакцию как транзакцию, в которой участвует:

  1. не менее двух любых управляющих ресурсами;
  2. сколько угодно непостоянных управляющих ресурсами и не менее двух долговечных;
  3. не менее двух любых управляющих ресурсами, обязательно расположенных на разных компьютерах.

Поэтому лучше всегда уточнять, о каких именно транзакциях идет речь.

И в этом контексте в первую очередь обсуждается MSDTC. Он представляет собой программный компонент Windows, управляющий распределенными транзакциями. Имеется графический интерфейс для конфигурации и наблюдения за транзакциями, который можно обнаружить в утилите «Службы компонентов», пройдя по пути «Компьютеры — Мой компьютер — Координатор распределенных транзакций — Локальная DTC».

Для конфигурации нужно выбрать пункт «Свойства» в контекстном меню узла «Локальная DTC», а для наблюдения за распределенными транзакциями — пункт «Статистика транзакций» в центральной панели.

Двухфазная фиксация

Если в транзакции участвует несколько управляющих ресурсами, то результаты их работы могут отличаться: например, у одного из них все завершилось успешно, и он готов фиксировать изменения, а у другого произошла ошибка, и он собирается откатить изменения. Однако суть распределенной транзакции заключается как раз в том, что изменения всех участвующих в транзакции управляющих ресурсов либо фиксируются все вместе, либо откатываются. Поэтому в таких случаях обычно применяется протокол двухфазной фиксации.

В общем суть этого протокола заключается в следующем. Во время первой фазы участвующие в транзакции управляющие ресурсами подготавливают информацию, достаточную для восстановления после сбоя (если это долговечный управляющий ресурсами) и для успешного окончания работы в результате фиксации. С технической точки зрения управляющий ресурсами сигнализирует о том, что он закончил первую фазу, вызывая метод System.Transactions.PreparingEnlistment.Prepared в методе Prepare . Либо управляющий ресурсами может уведомить об откате изменений, вызвав метод ForceRollback .

Когда все участвующие в транзакции управляющие ресурсами «проголосовали», то есть уведомили управляющий транзакциями о том, хотят ли они фиксировать или откатывать изменения, начинается вторая фаза. В это время управляющие ресурсами получают указание фиксировать свои изменения (если все участники проголосовали за фиксацию) или отказаться от изменений (если хотя бы один участник проголосовал за откат). Технически это выражается в вызове методов Commit и Rollback , которые реализуют управляющие ресурсами и в которых они вызывают метод System.Transactions.Enlistment.Done .

Управляющий ресурсами может вызвать метод System.Transactions.Enlistment.Done и во время первой фазы. В таком случае подразумевается, что он не собирается фиксировать никаких изменений (например, работает только на чтение) и не будет участвовать во второй фазе. Подробнее про двухфазную фиксацию можно прочитать у Microsoft.

Если связь между управляющим транзакциями и хотя бы одним из управляющих ресурсами теряется, то транзакция становится зависшей («под сомнением», in-doubt). Управляющий транзакциями, вызывая методы InDoubt , оповещает об этом событии доступные управляющие ресурсами, которые могут отреагировать подходящим образом.

Есть еще трехфазная фиксация и ее модификации со своими достоинствами и недостатками. Протокол трехфазной фиксации менее распространен, возможно, потому, что требует еще больше расходов на сообщения между взаимодействующими подсистемами.

Шпаргалка по интерфейсам System.Transactions

Что-то сложно получается. Чтобы немного разложить все по полочкам, я кратко опишу основные интерфейсы пространства имен System.Transactions , нужные для создания управляющего ресурсами. Вот диаграмма классов.

IEnlistmentNotification. Управляющий ресурсами обязательно реализует этот интерфейс. Управляющий транзакциями вызывает реализованные методы в следующем порядке. Во время первой фазы он вызывает метод Prepare (если только не сошлись звезды для вызова метода ISinglePhaseNotification.SinglePhaseCommit , о чем написано в следующем пункте). В рамках этого метода управляющий ресурсами сохраняет необходимую для восстановления после сбоя информацию, готовится к окончательной фиксации изменений на своей стороне и голосует за фиксацию или откат изменений. Если наступает вторая фаза, то в зависимости от доступности управляющих ресурсами и от результатов голосования управляющий транзакциями вызывает один из трех методов: Commit , InDoubt , Rollback .

ISinglePhaseNotification. Управляющий ресурсами реализует этот интерфейс, если хочет предоставить управляющему транзакциями возможность оптимизировать выполнение за счет редуцирования второй фазы фиксации. Если управляющий транзакциями видит только один управляющий ресурсами, то на первой фазе фиксации он пытается вызвать у управляющего ресурсами метод SinglePhaseCommit (вместо IEnlistmentNotification.Prepare ) и тем самым исключить голосование и переход ко второй фазе. У этого подхода есть достоинства и недостатки, о которых понятнее всего написано у Microsoft здесь.

ITransactionPromoter. Управляющий ресурсами реализует этот интерфейс (только не напрямую, а через интерфейс IPromotableSinglePhaseNotification ), если хочет предоставить управляющему транзакциями возможность придерживаться протокола Lightweight даже при удаленном вызове, пока не наступят другие условия, которые потребуют усложнения протокола. Когда потребуется усложнить протокол, будет вызван метод Promote .

IPromotableSinglePhaseNotification. Управляющий ресурсами реализует этот интерфейс, чтобы, во-первых, реализовать интерфейс ITransactionPromoter , а во-вторых, чтобы управляющий транзакциями мог воспользоваться однофазной фиксацией, вызывая методы IPromotableSinglePhaseNotification.SinglePhaseCommit и IPromotableSinglePhaseNotification.Rollback . Управляющий транзакциями вызывает метод IPromotableSinglePhaseNotification.Initialize , чтобы отметить успешную регистрацию управляющего ресурсами по упрощенной схеме. Более-менее это можно понять из документа Microsoft.

Еще немного посмотрим на класс System.Transactions.Enlistment и его наследников. Экземпляры этого типа предоставляет управляющий транзакциями, когда вызывает методы интерфейсов, реализуемые управляющим ресурсами.

Enlistment. Управляющий ресурсами может вызвать единственный метод этого типа — Done , — чтобы сигнализировать об успешном выполнении своей части работы.

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

SinglePhaseEnlistment. С помощью экземпляра этого типа управляющий ресурсами может передать информацию управляющему транзакциями о результатах своей работы при использовании упрощенной схемы (однофазной фиксации).

Ограничения программных распределенных транзакций и альтернативы

Краткое исследование мнений, встречающихся в интернете, показывает, что во многих областях распределенные транзакции выходят из моды. Посмотрите, например, на этот ехидный комментарий. Основной объект критики, о котором вкратце сказано здесь, — это синхронная (блокирующая) природа распределенных транзакций. Если пользователь отправил запрос, во время обработки которого была организована распределенная транзакция, то он получит ответ только после того, как (успешно или с ошибкой) закончат работу все подсистемы, включенные в транзакцию. При этом есть подкрепленное исследованиями мнение, что протокол двухфазной фиксации показывает плохую производительность, особенно с ростом числа вовлеченных в транзакцию подсистем, о чем упоминается, например, в этой публикации на «Хабре».

Если создатель системы предпочитает вернуть ответ пользователю как можно скорее, отложив согласование данных на потом, то ему больше подойдет какое-нибудь другое решение. В контексте теоремы Брюера (теоремы CAP) можно сказать, что распределенные транзакции подходят для случаев, когда согласованность данных (Consistency) важнее доступности (Availability).

Существуют и другие практические ограничения на использование программных распределенных транзакций. Например, опытным путем было установлено, что распределенные транзакции по протоколу OleTx не должны пересекать сетевые домены. Во всяком случае, долгие попытки заставить их работать не увенчались успехом. Кроме того, было выявлено, что взаимодействие между несколькими экземплярами Oracle Database (распределенных транзакций СУБД) накладывает серьезные ограничения на применимость программных распределенных транзакций (опять же не удалось завести).

Какие есть альтернативы распределенным транзакциям? Сначала надо сказать, что вообще без технических транзакций (обычных, не распределенных) будет обойтись очень сложно. В системе наверняка будут процессы, которые могут временно нарушить целостность данных, и потребуется каким-то образом обеспечить надзор за такими процессами. Точно так же и в терминах предметной области может возникнуть понятие, включающее процесс, реализующийся совокупностью процессов в разных технических системах, который должен начинаться и заканчиваться в области целостных данных.

Переходя к альтернативам распределенных транзакций, можно отметить решения на основе служб сообщений, например RabbitMQ и Apache Kafka. В этой публикации на «Хабре» рассматривается четыре таких решения:

  1. подсистема публикует сообщение, на основе которого происходит и запись в БД, и уведомление других подсистем;
  2. подсистема пишет в БД, а уведомления других подсистем формируются на основе журнала транзакций БД (Transaction Log Tailing);
  3. подсистема ведет в БД как таблицы сущностей, так и таблицы сообщений для внешних подсистем;
  4. сущности в БД однозначно представляются очередью записей об их создании и изменениях (Event Sourcing).

Еще одна альтернатива — шаблон «Сага». Он предполагает каскад подсистем со своими локальными транзакциями. По завершении работы каждая система вызывает следующую (либо самостоятельно, либо при помощи координатора). Для каждой транзакции имеется соответствующая отменяющая транзакция, и подсистема может вместо передачи управления дальше, наоборот, инициировать отмену изменений, сделанных ранее предыдущими подсистемами. На «Хабре» есть несколько хороших статей о шаблоне «Сага». К примеру, в этой публикации приводятся общие сведения о поддержании принципов ACID в микрослужбах, а в этой статье подробно рассматривается пример внедрения шаблона «Сага» с координатором.

В нашей компании в некоторых продуктах успешно используются программные распределенные транзакции через WCF, но бывают и другие варианты. Однажды, когда мы пытались подружить новую систему с распределенными транзакциями, у нас возникло много проблем, включая столкновение с описанными выше ограничениями и параллельные неприятности с обновлением программной инфраструктуры. Поэтому в условиях дефицита ресурсов на обкатывание другого капитального решения мы применили следующую тактику. Вызываемая сторона фиксирует изменения в любом случае, но отмечает, что они находятся в состоянии черновика, поэтому эти изменения пока не влияют на работу вызываемой системы. Потом уже вызывающая сторона при завершении своей работы через распределенную транзакцию СУБД активизирует изменения, сделанные вызываемой системой. Таким образом, вместо программных распределенных транзакций мы воспользовались распределенными транзакциями СУБД, которые в данном случае оказались гораздо надежнее.

Так это есть в .NET Core?

В .NET Core (и даже в .NET Standard) есть все необходимые типы для организации транзакций и создания своего управляющего ресурсами. К сожалению, в .NET Core у транзакций на основе System.Transactions есть серьезное ограничение: они работают только с протоколом Lightweight. Например, если в коде используется два долговечных управляющих ресурсами, то во время выполнения среда выбросит исключение, как только будет произведено обращение ко второму управляющему.

Илон Маск рекомендует:  Что такое код pfpro_process_raw

Дело в том, что .NET Core стараются делать независимым от операционной системы, поэтому завязка на такие управляющие транзакциями, как KTM и MSDTC, исключается, а именно они и нужны для поддержания транзакций с указанными свойствами. Возможно, будет реализовано подключение управляющих транзакциями в виде плагинов, но пока что это вилами по воде писано, поэтому рассчитывать на промышленное применение распределенных транзакций в .NET Core пока нельзя.

На опыте можно убедиться в различиях распределенных транзакций в .NET Framework и в .NET Core, написав одинаковый код, скомпилировав и запустив его на разных платформах.

Готовые для сборки проекты находятся на GitHub.

Запуск примера для .NET Core заканчивается ошибкой. От порядка вызова СУБД зависят место и тип выбрасываемого исключения, но в любом случае это исключение свидетельствует о недопустимой транзакционной операции. Запуск примера для .NET Framework заканчивается успешно, если в это время работает MSDTC; при этом в графическом интерфейсе MSDTC можно наблюдать регистрацию распределенной транзакции.

Распределенные транзакции и WCF

Windows Communication Foundation (WCF) — это программный каркас мира .NET, предназначенный для организации и вызова сетевых служб. По сравнению с более модными подходами REST и ASP.NET Web API он обладает своими достоинствами и недостатками. WCF очень хорошо дружит с транзакциями .NET, и в мире .NET Framework его удобно использовать для организации транзакций, распределенных между клиентом и службой.

В .NET Core эта технология работает только со стороны клиента, то есть создать службу нельзя, а можно только обратиться к уже существующей. Это, однако, не слишком важно, потому что, как сказано выше, с распределенными транзакциями в .NET Core дела вообще обстоят не очень хорошо.

Как устроен WCF

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

Чтобы создать службу на WCF, обычно надо написать интерфейс, описывающий контракт создаваемой службы, и реализующий этот интерфейс класс. Класс и интерфейс размечаются особыми атрибутами WCF, выделяющими их среди остальных типов, и задают некоторые детали поведения во время обнаружения и вызова службы. Эти типы обертываются во что-то, работающее в виде сервера (например, в DLL, на которую натравливают IIS), и дополняются конфигурационным файлом (есть варианты), где указаны подробности реализации службы. После запуска к службе можно обращаться, например, по сетевому адресу; в интернет-обозревателе можно посмотреть контракты, которые реализует запрошенная служба.

Программист, желающий обратиться к существующей службе WCF, использует консольную утилиту или встроенный в среду разработки графический интерфейс, чтобы по имеющемуся адресу службы сформировать типы на языке C# (или на другом поддерживаемом языке), соответствующие контрактам службы. Файл с полученными типами включается в проект клиентского приложения, и после этого программист пользуется теми же терминами, что содержатся в интерфейсе службы, наслаждаясь благами прогресса (статической типизацией). Кроме этого, в конфигурационном файле клиента указываются технические характеристики вызываемой службы (возможна настройка и в коде, без конфигурационного файла).

WCF поддерживает разные виды транспорта, шифрования и иных более тонких технических параметров. Большинство из них объединяются понятием «привязка» (Binding). Есть три важных параметра службы WCF:

  1. адрес, по которому она доступна;
  2. привязка;
  3. контракт (интерфейсы).

Все эти параметры устанавливаются в конфигурационных файлах службы и клиента.

В нашей компании WCF (и с распределенными транзакциями, и без них) широко используется во внедренных продуктах, однако, учитывая веяния моды, его использование в новых продуктах пока что находится под вопросом.

Как завести распределенные транзакции в WCF

Чтобы завести в WCF транзакции на основе System.Transactions , программисту нужно расставить в коде несколько атрибутов, убедиться, что используемые привязки поддерживают распределенные транзакции, на клиенте и в службе написано transactionFlow=»true» и что на всех задействованных компьютерах запущен подходящий управляющий транзакциями (скорее всего, это будет MSDTC).

Привязки, поддерживающие распределенные транзакции: NetTcpBinding, NetNamedPipeBinding, WSHttpBinding, WSDualHttpBinding и WSFederationHttpBinding.

Метод (операцию) интерфейса службы надо пометить атрибутом System.ServiceModel.TransactionFlowAttribute . Тогда при определенных параметрах атрибута и при установке параметра TransactionScopeRequired атрибута System.ServiceModel.OperationBehaviorAttribute транзакция будет распределяться между клиентом и службой. Кроме того, по умолчанию считается, что служба голосует за фиксацию транзакции, если только во время выполнения не было выброшено исключение. Чтобы поменять это поведение, надо установить соответствующее значение параметра TransactionAutoComplete атрибута System.ServiceModel.OperationBehaviorAttribute .

TL;DR по разделу

Распределенные транзакции включают несколько управляющих ресурсами, и все изменения должны либо фиксироваться, либо откатываться. Некоторые современные СУБД реализуют распределенные транзакции, которые представляют удобный механизм связи нескольких БД. Программные (реализуемые не в СУБД) распределенные транзакции могут включать разные комбинации управляющих ресурсами на разных компьютерах под управлением разных операционных систем, но они имеют ограничения, которые нужно учесть перед тем, как полагаться на них. Современной альтернативой распределенным транзакциям являются решения на основе службы сообщений. В .NET Core распределенные транзакции пока что не поддерживаются.

WCF — это один из стандартных и проверенных инструментов создания и обращения к службам в мире .NET, поддерживающий несколько видов транспорта и шифрования. WCF очень близко дружит с распределенными транзакциями на основе System.Transactions . Настройка распределенных транзакций для WCF заключается в разметке кода несколькими атрибутами и в добавлении пары слов в конфигурационных файлах службы и клиента. Не все привязки WCF поддерживают распределенные транзакции. Кроме того, очевидно, транзакции в WCF имеют те же ограничения, что и без использования WCF. Платформа .NET Core пока что позволяет только обращаться к службам на WCF, а не создавать их.

Заключение-шпаргалка

Эта публикация представляет собой обзор основ программных транзакций .NET. Некоторые выводы относительно тенденций в программных транзакциях можно найти в разделах, посвященных применимости и ограничениям обсуждаемых предметов, а в заключении собраны основные тезисы публикации. Предполагаю, что их можно использовать в качестве шпаргалки при рассмотрении программных транзакций как одного из вариантов реализации технической системы или для освежения в памяти соответствующей информации.

Транзакции (предметной области, СУБД, программные). Требования предметной области иногда формулируют в виде транзакций — операций, которые, начинаясь в области целостных данных, по завершении (в том числе и неудачном) должны приходить также в область целостных данных (возможно, уже другую). Эти требования обычно реализуются в виде системных транзакций. Классическим примером транзакции является перевод денег между двумя счетами, состоящий из двух неделимых операций — снятия денег с одного счета и зачисления на другой. Кроме известных всем транзакций, реализуемых средствами СУБД, существуют и программные транзакции, например в мире .NET. Управляющие ресурсами — это программные компоненты, знающие о существовании таких транзакций и имеющие возможность включаться в них, то есть фиксировать или откатывать сделанные изменения. Управляющие ресурсами получают инструкции о фиксации и откате изменений от управляющего транзакциями, составляющего основу инфраструктуры System.Transactions .

Долговечные и непостоянные управляющие ресурсами. Долговечные управляющие ресурсами поддерживают восстановление данных после системного сбоя. Драйверы СУБД для .NET как раз обычно предлагают такую функциональность. Непостоянные управляющие ресурсами не поддерживают восстановление после сбоя. Программную транзакционную память — способ управления объектами в оперативной памяти — можно рассматривать как пример непостоянного управляющего ресурсами.


Транзакции и управляющие ресурсами .NET. Программист .NET пользуется программными транзакциями и создает собственные управляющие ресурсами с помощью типов из пространства имен System.Transactions . Эта инфраструктура позволяет использовать транзакции различной вложенности и изоляции (с известными ограничениями). Использование транзакций — дело не сложное, и заключается оно в обертывании кода в блок using с определенными характеристиками. Однако включаемые таким образом в транзакцию управляющие ресурсами должны поддерживать требуемую функциональность со своей стороны. Использование в транзакции разнородных управляющих ресурсами или использование одного управляющего разными способами может автоматически превратить транзакцию в распределённую.

Распределенные транзакции (СУБД, программные). Распределенная транзакция охватывает несколько подсистем, изменения в которых должны быть синхронизированы, то есть они либо фиксируются все вместе, либо откатываются. Распределенные транзакции реализованы в некоторых современных СУБД. Программные распределенные транзакции (это уже не те, которые реализуются СУБД) накладывают дополнительные ограничения на взаимодействующие процессы и платформы. Распределенные транзакции постепенно выходят из моды, уступая решениям на основе служб сообщений. Чтобы превратить обычную транзакцию в распределенную, программисту почти ничего не надо делать: при включении в транзакцию управляющего ресурсами с определенными характеристиками во время выполнения управляющий транзакциями автоматически сделает все, что нужно. Обычные программные транзакции доступны в .NET Core и .NET Standard, а распределенные — не доступны.

Распределенные транзакции через WCF. WCF — это один из стандартных для .NET инструментариев создания и вызова служб, поддерживающий в том числе и стандартизированные протоколы. Другими словами, к определенным образом сконфигурированным службам WCF можно обращаться из любого приложения, не только .NET или Windows. Чтобы создать распределенную транзакцию поверх WCF, нужно разметить типы, составляющие службу, дополнительными атрибутами и внести минимальные изменения в конфигурационные файлы службы и клиента. В .NET Core и .NET Standard нельзя создавать службы WCF, но можно создавать клиенты WCF.

Аутентификация с помощью ASP.NET >

Самым базовым средством работы с ASP.NET Identity является аутентификация пользователей, и в этой статье я покажу как ее использовать. Ниже собраны базовые вопросы, которые могут возникнуть у вас в данный момент:

Аутентификация — это проверка учетных данных, предоставляемых пользователем. После успешной аутентификации пользователя, во все последующие запросы добавляется cookie-файл с идентификатором пользователя.

Зачем нужно использовать?

Система аутентификации проверяет подлинность пользователей и является первым шагом к ограничению доступа к важной части приложения.

Как использовать в рамках MVC?

В ASP.NET MVC используется атрибут авторизации, который применяется к контроллерам и методам действий для того, чтобы ограничить доступ неавторизованным пользователям.

Далее я продемонстрирую возможности систем аутентификации и авторизации Identity на учетных записях пользователей, хранящихся в локальной базе данных. Однако, в последующих статьях мы разберем аутентификацию через социальные сети (такие как Facebook, Вконтакте и т.д.)

Процесс аутентификации/авторизации

Платформа ASP.NET >атрибут Authorize. В этой статье мы ограничим доступ к методу действия Index контроллера Home и реализуем функции, которые позволят идентифицировать пользователей, чтобы они могли получить к нему доступ. В примере ниже я применил атрибут Authorize:

Атрибут Authorize по умолчанию ограничивает доступ к методам действий для пользователей, не прошедших аутентификацию. Если вы запустите приложение и запросите представление Index контроллера Home (по любому из адресов /Home/Index, /Home или просто /), вы увидите сообщение об ошибке, показанное на рисунке ниже:

Платформа ASP.NET предоставляет полезную информацию о пользователе в объекте HttpContext, которую использует атрибут Authorize, чтобы проверить состояние текущего запроса и посмотреть, является ли пользователь аутентифицированным. Свойство HttpContext.User возвращает реализацию интерфейса IPrincipal, который определен в пространстве имен System.Security.Principal. Интерфейс IPrincipal определяет свойства и методы, перечисленные в таблице ниже:

Методы и свойства, определенные в интерфейсе IPrincipal

Возвращает реализацию интерфейса IIdentity, описывающую пользователя, связанного с запросом.

Возвращает true, если пользователь является членом указанной роли.

Интерфейс IIdentity, реализация которого возвращается свойством Iprincipal.Identity, также содержит несколько полезных свойств:

Название Описание
Identity
IsInRole(role)
Свойства интерфейса IIDentity

Возвращает строку, описывающую механизм, который используется для аутентификации пользователей.

Возвращает true, если пользователь аутентифицирован.

Возвращает имя текущего пользователя.

ASP.NET Identity содержит модуль, который обрабатывает глобальное событие AuthenticateRequest жизненного цикла приложения в котором проверяет cookie в браузере пользователя, для определения того, является ли он аутентифицированным. Позже я покажу как используются эти cookie-файлы. Если пользователь аутентифицирован, ASP.NET задает свойству IIdentity.IsAuthenticated значение true. (В нашем приложении мы еще не реализовали функцию аутентификации, поэтому свойство IsAuthenticated будет всегда возвращать false.)

Модуль авторизации проверяет свойство IsAuthenticated и, если пользователь не прошел проверку, возвращает HTTP-статус 401 и завершает запрос. В этот момент модуль ASP.NET Identity перехватывает запрос и перенаправляет пользователя на страницу входа /Account/Login. Это URL-адрес, который я определил в классе IdentityConfig ранее:

Браузер перенаправит вас по адресу /Account/Login, но т. к. мы еще не добавили контроллер для этого запроса, появится ошибка 404.

Подготовка к реализации системы аутентификации

Даже несмотря на то, что наш запрос на данный момент заканчивается ошибкой, мы наглядно показали как вписывается Identity в жизненный цикл приложения ASP.NET. Следующий шаг — создать контроллер, обрабатывающий запрос к URL /Account/Login, для отображения формы входа в приложение. Добавьте сначала новый класс view-model в файл UserViewModels.cs:

Новый класс модели содержит свойства Name и Password, декларированные атрибутом Required, который говорит системы валидации модели, что эти свойства не должны иметь пустых значений. В реальном проекте не забывайте также добавлять клиентскую проверку при вводе пользователем имени и пароля. В данном проекте мы пропустим эту проверку, т. к. основная наша цель — разобраться в ASP.NET Identity.

Теперь добавьте контроллер Account в наше приложение, с кодом, показанным в примере ниже, который содержит две перегруженные версии метода Login. Обратите внимание, что здесь не реализована логика проверки достоверности модели, т. к. мы возвращаем представление для проверки учетных данных пользователя и входа пользователей в приложение.

Хотя этот код еще не относится к аутентификации пользователей, контроллер Account все же содержит некоторую полезную инфраструктуру, не относящуюся к ASP.NET Identity.

Во-первых, обратите внимание, что оба метода Login принимают строковый аргумент returnUrl. Когда пользователь запрашивает URL-адрес с ограниченным доступом, он перенаправляется по адресу /Account/Login со строкой запроса, содержащей адрес страницы с ограниченным доступом. Например, если сейчас вы запросите адрес /Home/Index, вас перенаправит на URL следующего вида:

С помощью параметра ReturnUrl мы сможем перенаправить пользователя, успешно прошедшего аутентификацию, на страницу, с которой он пришел. Это обеспечивает простой и понятный процесс навигации пользователя в приложении при аутентификации.

Далее обратите внимание на атрибуты, которые я применил к контроллеру и его методам действий. Функции контроллера Account (такие как смена пароля, например) по умолчанию должны быть доступны только авторизованным пользователям. Для этого мы применили атрибут Authorize к контроллеру Account и добавили атрибут AllowAnonymous к некоторым методам действий. Это позволяет ограничить методы действий для авторизованных пользователей по умолчанию, но открыть доступ неавторизованным пользователям для входа в приложение.

Наконец, мы добавили атрибут Val >метод AntiForgeryToken защищает от , благодаря тому, что генерирует скрытое поле формы с токеном, который проверяется при отправке формы.

Последний подготовительный шаг — создание формы входа в приложение. Добавьте в папку /Views/Account файл представления Login.cshtml:

В этой разметке стоит обратить внимание на использование вспомогательного метода Html.AntiForgeryToken и создание скрытое поля с сохранением параметра returnUrl. В остальном — это обычная форма, генерируемая с помощью Razor.

Итак, мы завершили подготовку к созданию системы аутентификации. Запустите приложение и перейдите по адресу /Home/Index – система перенаправит вас на страницу входа:

Аутентификация пользователей

Запросы к страницам с ограниченным доступом успешно перехватываются и перенаправляются контроллеру Account, но учетные данные, предоставляемые пользователем, пока не проверяются системой аутентификации. В примере ниже вы можете увидеть, как мы завершим процесс входа пользователей в систему:

Самая простая часть — это проверка учетных данных, которую мы делаем с помощью метода FindAsync класса AppUserManager:

В дальнейшем мы будем многократно обращаться к экземпляру класса AppUserManager, поэтому мы создали отдельное свойство UserManager, который возвращает экземпляр класса AppUserManager с помощью метода расширения GetOwinContext() класса HttpContext.

Метод FindAsync принимает в качестве параметров имя и пароль, введенные пользователем, и возвращает экземпляр класса пользователя (AppUser в примере) если учетная запись с такими данными существует. Если нет учетной записи с таким именем или пароль не совпадает, метод FindAsync возвращает значение null. В этом случае мы добавляем ошибку в метаданные модели, которая будет отображена пользователю. Claims >метода Create >перечислении DefaultAuthenticationTypes.

Следующий шаг — удаление всех старых аутентифицирующих файлов cookie и создание новых. Мы добавили свойство AuthManager в контроллере Account, которое возвращает объект, реализующий интерфейс IAuthenticationManager, который отвечает за выполнение аутентификации в ASP.NET Identity. В таблице ниже перечислено несколько полезных методов этого интерфейса:

Название Описание
AuthenticationType
IsAuthenticated
Наиболее полезные методы интерфейса IAuthenticationManager

Вход пользователя в систему, что фактически означает создание аутентифицирующего cookie-файла

Выход пользователя из системы, который обычно означает удаление файлов cookie

Аргументами метода SignIn являются объект AuthenticationProperties, определяющий свойства настройки процесса аутентификации и объект Claims >свойству IsPersistent объекта AuthenticationProperties значение true — это означает, что файлы cookie будут сохраняться между сессиями пользователей. Благодаря этому, если пользователь выйдет из приложения и зайдет, например, на следующий день, он будет автоматически аутентифицирован. (Есть и другие полезные свойства, определенные в классе AuthenticationProperties, но свойство IsPersistent является единственным, который широко используется на данный момент.)

После воссоздания файлов cookie мы перенаправляем пользователя по URL-адресу, который он просматривал до аутентификации (используя параметр returnUrl).

Давайте протестируем процесс аутентификации. Запустите приложение и перейдите по адресу /Home/Index. Браузер перенаправит вас на страницу входа, где необходимо ввести данные пользователя, которого мы создали ранее при тестировании панели администратора:

Двухфакторная аутентификация

В примере выше мы реализовали однофакторную систему аутентификации. Это означает, что для входа в систему пользователю нужно знать всего лишь пароль.

Является ли «transaction.atomic» таким же, как «transaction.commit_on_success»?

Django 1.6 предлагает @transaction.atomic как часть ремонта в управлении транзакциями от 1.5.

У меня есть функция, которая вызывается командой управления Django, которая, в свою очередь, называется cron, т.е. никаких запросов HTTP-запроса при запуске транзакций в этом случае. Отрывок:

В приведенном выше блоке кода commit_on_success использует одну транзакцию для всей работы, выполненной в my_function .

Выполняет ли замена @transaction.commit_on_success на @transaction.atomic одинаковое поведение? @transaction.atomic docs state:

Атомность — определяющее свойство транзакций базы данных. атомное позволяет нам создать блок кода, в котором атомарность на база данных гарантирована. Если блок кода успешно завершены изменения, внесенные в базу данных. Если есть исключение, изменения откатываются.

Я считаю, что они приводят к такому же поведению; правильно?

Да. Вы должны использовать atomic в тех местах, где вы ранее использовали commit_on_success .

Так как новая транзакционная система спроектирована так, чтобы быть более надежной и последовательной, возможно, вы могли видеть другое поведение. Например, если вы поймаете ошибки базы данных и попытаетесь продолжить, вы увидите TransactionManagementError , тогда как предыдущее поведение было undefined и, вероятно, зависящим от случая.

Но если вы делаете все правильно, все должно продолжать работать одинаково.

Основываясь на документации, которую я прочитал по этому вопросу, существует значительная разница, когда эти декораторы вложены.

Вложение двух блоков atomic не работает так же, как вложение двух блоков commit_on_success .

Проблема в том, что есть две гарантии, которые вы хотели бы иметь из этих блоков.

  • Вы хотите, чтобы содержимое блока было атомарным, либо все внутри блока зафиксировано, либо ничего не сделано.
  • Вы хотели бы долговечность, как только вы покинули блок без исключения, вам гарантировано, что все, что вы написали внутри блока, является постоянным.

Невозможно предоставить обе гарантии, когда блоки вложены. Если исключение возникает после выхода из самого внутреннего блока, но перед выходом из внешнего блока вам придется сбой одним из двух способов:

  • Невозможно обеспечить долговечность самого внутреннего блока.
  • Невозможно обеспечить атомарность для внешнего блока.

Здесь вы найдете разницу. Использование commit_on_success обеспечило бы прочность для самого внутреннего блока, но без атомарности для самого внешнего блока. Использование atomic даст атомарность для самого внешнего блока, но не долговечность для самого внутреннего блока.

Простое создание исключения в случае вложенности может помешать вам решить проблему. Самый внутренний блок всегда вызывает исключение, поэтому никогда не будет promises любой долговечности. Но это теряет гибкость.

Лучшим решением было бы получить более подробную информацию о том, что вы просите. Если вы можете отдельно задавать атомарность и долговечность, вы можете выполнить вложенность. Вам просто нужно убедиться, что каждый блок, требующий долговечности, находится за пределами тех, кто требует атомарности. Требование прочности внутри блока, запрашивающего атомарность, должно было бы вызвать исключение.

atomic должен обеспечивать часть атомарности. Насколько я могу судить, django 1.6.1 не имеет декоратора, который может просить о прочности. Я попытался написать один, и опубликовал его на codereview.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL
Название Описание
SignIn(options, identity)