В проекте ASP.Net я недавно изменил некоторые длинные функции на асинхронные. Это оказалось причиной некоторых неожиданных проблем.
В основном функции работают при использовании await, но необходимость вызывать их из другой асинхронной функции в конечном итоге становится проблемой в форме. До сих пор я устанавливал для директивы страницы Async значение true и использовал RegisterAsyncTask для инициирования вызовов асинхронных функций. Звонок работает. Но оказывается, когда у меня включена директива страницы Async, контекст пользователя иногда меняется.
Чтобы олицетворять вызывающего пользователя для всего кода на определенных страницах, я унаследовал класс System.Web.UI.Page от класса ImpersonatedPage. В моем классе я использую OnLoad для имитации вызывающего пользователя. Таким образом, я уверен, что пользователь никогда не сможет видеть или делать больше, чем он/она уже имеет доступ, и журналы аудита показывают правильного пользователя.
Когда директива асинхронной страницы включена, она по-прежнему олицетворяет пользователя. Но, например, в коде нажатия кнопки пользовательский контекст внезапно возвращается к контексту пулов приложений. В Page_Load пользовательский контекст — это вызывающий пользователь, как и ожидалось. Что вызывает это, и можно ли избежать переключения обратно на пользователя пула приложений?
В качестве альтернативы я попытался удалить директиву страницы Async и вызвать функцию async с помощью Task.Run. Затем пользовательский контекст не переключается обратно, а вместо этого HttpContext.Current имеет значение null, и асинхронная функция не может выполнять свою работу.
Любые другие способы вызова асинхронной функции из ASP.Net без включения директивы страницы Async?
Целевая платформа: 4.6.1, насколько мне известно, причуды не включены
Класс ImpersonatedPage:
public class ImpersonatedPage : System.Web.UI.Page
{
WindowsImpersonationContext m_wic = null;
protected override void OnLoad(EventArgs e)
{
WindowsIdentity wi = (WindowsIdentity)System.Web.HttpContext.Current.User.Identity;
m_wic = wi.Impersonate();
HttpContext.Current.Response.Write("Impersonate<br />");
base.OnLoad(e);
}
protected override void OnUnload(EventArgs e)
{
base.OnUnload(e);
_ImpersonationUndo();
}
protected void _ImpersonationUndo()
{
if (m_wic != null)
{
m_wic.Undo();
HttpContext.Current.Response.Write("Impersonate undo<br />");
}
m_wic = null;
}
}
Директивы страницы формы:
<%@ Page Async="true" Language="C#" AutoEventWireup="true" Inherits="ProxyAddresses" Codebehind="ProxyAddresses.aspx.cs" %>
Объявление класса:
public partial class ProxyAddresses : ImpersonatedPage
Страница_Загрузка:
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("Page_Load: " + WindowsIdentity.GetCurrent().Name + "<br />");
if (!IsPostBack)
{
RegisterAsyncTask(new PageAsyncTask(InitialPageLoadAsync));
}
...
Асинхронная функция:
private async Task InitialPageLoadAsync()
{
Response.Write("InitialPageLoadAsync: " + WindowsIdentity.GetCurrent().Name + "<br />");
await fnDoSomeAsyncWork();
...
Щелчок кнопки:
protected void idBtnSaveSrv_Click(object sender, EventArgs e)
{
Response.Write("idBtnSaveSrv_Click: " + WindowsIdentity.GetCurrent().Name + "<br />");
...
Вывод отладки — первоначальный запрос:
- Выдавать себя за
- Page_Load: ДОМЕН\мой пользователь
- InitialPageLoadAsync: IIS APPPOOL\appuser (почему?)
- [содержимое страницы]
- Выдать себя за отмену
Вывод отладки — нажатие кнопки:
- Выдавать себя за
- Page_Load: ДОМЕН\мой пользователь
- idBtnSaveSrv_Click: IIS APPPOOL\appuser (Почему?)
- [содержимое страницы]
- Выдать себя за отмену
async void
, не использовать RegisterAsyncTask. RegisterAsyncTask предназначен для уведомления IIS о запущенной задаче, которую не следует прерывать после завершения текущего запроса. - person Panagiotis Kanavos   schedule 22.03.2016