Я использую Autofac и OWIN в проекте WebAPI, который был создан с нуля (в соответствии с полным шаблоном WebAPI, доступным в VS2015). По общему признанию, я новичок в этом.Autofac DI для RequestContext.Principal с использованием WebAPI2 в единичном тесте
В модульном тестировании проекта, я создал класс Owin запуска в начале модульного тестирования:
WebApp.Start<Startup>("http://localhost:9000/")
Класс запуска выглядит следующим образом:
[assembly: OwinStartup(typeof(API.Specs.Startup))]
namespace API.Specs
{
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
//config.Filters.Add(new AccessControlAttribute());
config.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());
config.Formatters.JsonFormatter.SerializerSettings = Serializer.Settings;
config.MapHttpAttributeRoutes();
// Autofac configuration
var builder = new ContainerBuilder();
// Unit of Work
var unitOfWork = new Mock<IUnitOfWork>();
builder.RegisterInstance(unitOfWork.Object).As<IUnitOfWork>();
// Principal
var principal = new Mock<IPrincipal>();
principal.Setup(p => p.IsInRole("admin")).Returns(true);
principal.SetupGet(p => p.Identity.Name).Returns('test.user');
principal.SetupGet(p => p.Identity.IsAuthenticated).Returns(true);
Thread.CurrentPrincipal = principal.Object;
if (HttpContext.Current != null)
{
HttpContext.Current.User = new GenericPrincipal(principal.Object.Identity, null);
}
builder.Register(c => principal).As<IPrincipal>();
.
.
.
// Set up dependencies for Controllers, Services & Repositories
.
.
.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
appBuilder.UseWebApi(config);
}
private static void RegisterAssemblies<TModel, TController, TService, TRepoClass, TRepoInterface>(ref ContainerBuilder builder, ref Mock<IUnitOfWork> unitOfWork)
where TModel : class
where TRepoClass : class
where TService : class
{
RegisterController<TController>(ref builder);
var repositoryInstance = RegisterRepository<TRepoClass, TRepoInterface>(ref builder);
RegisterService<TService>(ref builder, ref unitOfWork, repositoryInstance);
}
private static void RegisterController<TController>(ref ContainerBuilder builder)
{
builder.RegisterApiControllers(typeof(TController).Assembly);
}
private static object RegisterRepository<TRepoClass, TRepoInterface>(ref ContainerBuilder builder)
where TRepoClass : class
{
var constructorArguments = new object[] { DataContexts.Instantiate };
var repositoryInstance = Activator.CreateInstance(typeof(TRepoClass), constructorArguments);
builder.RegisterInstance(repositoryInstance).As<TRepoInterface>();
return repositoryInstance;
}
private static void RegisterService<TService>(ref ContainerBuilder builder, ref Mock<IUnitOfWork> unitOfWork, object repositoryInstance)
where TService : class
{
var constructorArguments = new[] { repositoryInstance, unitOfWork.Object};
var serviceInstance = Activator.CreateInstance(typeof(TService), constructorArguments);
builder.RegisterAssemblyTypes(typeof(TService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();
builder.RegisterInstance(serviceInstance);
}
}
}
Side Примечание: В идеале я хотел бы установить Принцип как часть теста, чтобы иметь возможность передавать разные пользователи контроллеру, но если мне абсолютно необходимо сохранить настройку CurrentPrincipal/User в классе запуска, я могу обойти его.
Начальный класс работает отлично с доступом к моим контроллерам с использованием DI, однако Принципал в RequestContext.Principal
никогда не устанавливается. Он всегда равен нулю. То, как я намерен использовать контекст запроса выглядит следующим образом:
[HttpGet]
[Route("path/{testId}")]
[ResponseType(typeof(Test))]
public IHttpActionResult Get(string testId)
{
return Ok(_service.GetById(testId, RequestContext.Principal.Identity.Name));
}
Я также попытался инъекции высмеивал главный класс в конструктор моего контроллера в качестве обходного пути - я использовал тот же метод, как показано в родовом методе для настройки моих сервисов с использованием DI. Опять же, я только получил null в моем конструкторе.
На данный момент я сижу около дня с этой проблемой и вытаскиваю волосы. Любая помощь будет оценена по достоинству. Заранее спасибо.
Hi Travis. Оглядываясь назад, ваше решение совершенно очевидно. Спасибо, что так хорошо объяснили. Приносим извинения за то, что вы вернулись к вам сейчас, мне пришлось переходить к проекту с помощью обходного пути и только теперь вернулись к исправлению. – Ebbs