Хорошо, в моем предыдущем вопросе/настройке было слишком много переменных, поэтому я разбираю его до базовых компонентов.
Учитывая приведенный ниже код с использованием StructureMap3...
//IoC setup
For<HttpContextBase>().UseSpecial(x => x.ConstructedBy(y => HttpContext.Current != null ? new HttpContextWrapper(HttpContext.Current) : null ));
For<ICurrentUser>().Use<CurrentUser>();
//Classes used
public class CurrentUser : ICurrentUser
{
public CurrentUser(HttpContextBase httpContext)
{
if (httpContext == null) return;
if (httpContext.User == null) return;
var user = httpContext.User;
if (!user.Identity.IsAuthenticated) return;
UserId = httpContext.User.GetIdentityId().GetValueOrDefault();
UserName = httpContext.User.Identity.Name;
}
public Guid UserId { get; set; }
public string UserName { get; set; }
}
public static class ClaimsExtensionMethods
public static Guid? GetIdentityId(this IPrincipal principal)
{
//Account for possible nulls
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal == null)
return null;
var claimsIdentity = claimsPrincipal.Identity as ClaimsIdentity;
if (claimsIdentity == null)
return null;
var claim = claimsIdentity.FindFirst(x => x.Type == ClaimTypes.NameIdentifier);
if (claim == null)
return null;
//Account for possible invalid value since claim values are strings
Guid? id = null;
try
{
id = Guid.Parse(claim.Value);
}
catch (ArgumentNullException) { }
catch (FormatException) { }
return id;
}
}
Как это возможно в окне Watch?
У меня есть веб-приложение, которое я обновляю до использования StructureMap 3.x из 2.x, но я получаю странное поведение при определенных зависимостях.
У меня есть ISecurityService, который я использую для проверки некоторых вещей, когда пользователь запрашивает страницу. Эта служба зависит от небольшого интерфейса, который я назвал ICurrentUser. Реализация класса довольно проста, на самом деле это может быть структура.
public interface ICurrentUser
{
Guid UserId { get; }
string UserName { get; }
}
Это достигается путем внедрения зависимостей с использованием приведенного ниже кода.
For<ICurrentUser>().Use(ctx => getCurrentUser(ctx.GetInstance<HttpContextBase>()));
For<HttpContextBase>().Use(() => getHttpContext());
private HttpContextBase getHttpContext()
{
return new HttpContextWrapper(HttpContext.Current);
}
private ICurrentUser getCurrentUser(HttpContextBase httpContext)
{
if (httpContext == null) return null;
if (httpContext.User == null) return null; // <---
var user = httpContext.User;
if (!user.Identity.IsAuthenticated) return null;
var personId = user.GetIdentityId().GetValueOrDefault();
return new CurrentUser(personId, ClaimsPrincipal.Current.Identity.Name);
}
Когда приходит запрос, сначала происходит проверка подлинности на всем сайте, что зависит от ISecurityService
. Это происходит внутри OWIN и, по-видимому, происходит до того, как HttpContext.User
будет заполнено, поэтому оно равно нулю, пусть будет так.
Позже у меня есть ActionFilter, который проверяет с помощью ISecurityService
, согласен ли текущий пользователь с текущей версией Условия использования для сайта, если нет, он перенаправляется на страницу, чтобы сначала согласиться с ними.
Все это отлично работало в структуре карты 2.x. Для перехода на StructureMap3 я установил пакет Nuget StructureMap.MVC5, чтобы ускорить процесс.
Когда мой код попадает в строку моего ActionFilter для проверки условий использования, у меня есть это.
var securityService = DependencyResolver.Current.GetService<ISecurityService>();
agreed = securityService.CheckLoginAgreedToTermsOfUse();
Внутри CheckLoginAgreedToTermsOfUse()
мой экземпляр CurrentUser
равен нулю. Даже если бы это удалось, и моя точка останова внутри getCurrentUser() никогда не срабатывала. Это почти так, как если бы это было предрешено, поскольку в прошлый раз оно было нулевым, хотя на этот раз оно было бы решено.
Я немного озадачен тем, почему getCurrentUser()
никогда не вызывается при запросе ISecurityService
. Я даже пытался явно прикрепить .LifecycleIs<UniquePerRequestLifecycle>()
к моему подключению для обработки ICurrentUser
безрезультатно.
ОБНОВЛЕНИЕ: Итак, просто на заметку, я начал использовать метод, принятый ниже, и, хотя он до сих пор отлично работал, он не решил мою основную проблему. Оказывается, новый StructureMap.MVC5
, основанный на StructureMap3
, использует NestedContainers. Которые ограничивают свои запросы временем существования NestedContainer, независимо от значения по умолчанию Transient. Поэтому, когда я запросил HttpContextBase
в первый раз, он вернет тот же самый экземпляр для остальной части запроса (хотя позже в течение срока действия запроса контекст изменился. Вам нужно либо не использовать NestedContainer (что, как я понимать, что это усложнит работу ASP.NET vNext), или вы явно задали жизненный цикл сопоставления For<>().Use<>()
, чтобы предоставить вам новый экземпляр для каждого запроса. Обратите внимание, что эта область видимости для NestedContainer также вызывает проблемы с контроллерами в MVC. В то время как пакет StructureMap.MVC5
обрабатывает это с ControllerConvention
, он не обрабатывает представления, а рекурсивные представления или представления, используемые несколько раз, вероятно, также вызовут у вас проблемы. Я все еще ищу постоянное решение проблемы с представлениями, на данный момент я вернулся к DefaultContainer
.