Невозможно вложить зависимостей в ASP.NET Web API Controller с помощью Unity
Кто-нибудь имел успех с использованием контейнера IoC для ввода зависимостей в контроллеры ASP.NET WebAPI? Кажется, я не могу заставить его работать.
Это то, что я сейчас делаю.
В моем global.ascx.cs
:
public static void RegisterRoutes(RouteCollection routes)
{
// code intentionally omitted
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
IUnityContainer container = BuildUnityContainer();
System.Web.Http.GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
t =>
{
try
{
return container.Resolve(t);
}
catch (ResolutionFailedException)
{
return null;
}
},
t =>
{
try
{
return container.ResolveAll(t);
}
catch (ResolutionFailedException)
{
return new System.Collections.Generic.List<object>();
}
});
System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
BundleTable.Bundles.RegisterTemplateBundles();
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer().LoadConfiguration();
return container;
}
Мой контроллер factory:
public class UnityControllerFactory : DefaultControllerFactory
{
private IUnityContainer _container;
public UnityControllerFactory(IUnityContainer container)
{
_container = container;
}
public override IController CreateController(System.Web.Routing.RequestContext requestContext,
string controllerName)
{
Type controllerType = base.GetControllerType(requestContext, controllerName);
return (IController)_container.Resolve(controllerType);
}
}
Кажется, что я не вижу в файле единства разрешения для зависимостей, и я получаю ошибку, например:
Произошла ошибка при попытке создать контроллер типа 'PersonalShopper.Services.WebApi.Controllers.ShoppingListController. Удостоверьтесь, что у контроллера нет конструктора без параметров.
в System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpControllerContext controllerContext, Тип controllerType) в System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateInstance(HttpControllerContext controllerContext, HttpControllerDescriptor controllerDescriptor) в System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateController(HttpControllerContext controllerContext, String controllerName) в System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage запрос, Отмена Отмена бронированияОтмена) в System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage запрос, Отмена Отмена бронированияТокнуть)
Контроллер выглядит так:
public class ShoppingListController : System.Web.Http.ApiController
{
private Repositories.IProductListRepository _ProductListRepository;
public ShoppingListController(Repositories.IUserRepository userRepository,
Repositories.IProductListRepository productListRepository)
{
_ProductListRepository = productListRepository;
}
}
Мой файл слияния выглядит так:
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<register type="PersonalShopper.Repositories.IProductListRepository, PersonalShopper.Repositories" mapTo="PersonalShopper.Implementations.MongoRepositories.ProductListRepository, PersonalShopper.Implementations" />
</container>
</unity>
Обратите внимание, что у меня нет регистрации для самого контроллера, потому что в предыдущих версиях mvc контроллер factory обнаружил, что зависимости нужно разрешить.
Кажется, мой контроллер factory никогда не вызывается.
Ответы
Ответ 1
Выяснил это.
Для ApiControllers MVC 4 использует System.Web.Http.Dispatcher.IHttpControllerFactory и System.Web.Http.Dispatcher.IHttpControllerActivator для создания контроллеров. Если нет статического метода для регистрации того, какова их реализация; когда они разрешены, инфраструктура mvc ищет реализации в преобразователе зависимостей, а если они не найдены, использует реализации по умолчанию.
У меня получилось единообразное разрешение зависимостей контроллера, выполнив следующие действия:
Создано UnityHttpControllerActivator:
public class UnityHttpControllerActivator : IHttpControllerActivator
{
private IUnityContainer _container;
public UnityHttpControllerActivator(IUnityContainer container)
{
_container = container;
}
public IHttpController Create(HttpControllerContext controllerContext, Type controllerType)
{
return (IHttpController)_container.Resolve(controllerType);
}
}
Зарегистрировал активатор контроллера как реализацию в контейнере единства:
protected void Application_Start()
{
// code intentionally omitted
IUnityContainer container = BuildUnityContainer();
container.RegisterInstance<IHttpControllerActivator>(new UnityHttpControllerActivator(container));
ServiceResolver.SetResolver(t =>
{
// rest of code is the same as in question above, and is omitted.
});
}
Ответ 2
Существует лучшее решение, которое работает корректно здесь
http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver
Ответ 3
Возможно, вы захотите взглянуть на пакет Unity.WebApi NuGet, который будет сортировать все это, а также обслуживать IDisposable компоненты.
см.
http://nuget.org/packages/Unity.WebAPI
или
http://www.devtrends.co.uk/blog/introducing-the-unity.webapi-nuget-package
Ответ 4
Microsoft разработала пакет для этого.
Выполните следующую команду из консоли диспетчера пакетов.
install-package Unity.AspNet.WebApi
Если у вас уже установлен Unity, он спросит, хотите ли вы перезаписать App_Start\UnityConfig.cs. Ответьте "Нет" и продолжайте.
Не нужно менять какой-либо другой код, и DI (с единицей) будет работать.
Ответ 5
У меня была такая же ошибка, и я пару раз искал в Интернете решения. В конце концов выяснилось, что мне пришлось регистрировать Unity до того, как я звонил в WebApiConfig.Register. Теперь мой global.asax выглядит как
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
UnityConfig.RegisterComponents();
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
Для меня это решило проблему, что Unity не может разрешить зависимости в моих контроллерах
Ответ 6
В недавнем RC я обнаружил, что больше нет метода SetResolver. Чтобы включить IoC для контроллера и webapi, я использую Unity.WebApi(NuGet) и следующий код:
public static class Bootstrapper
{
public static void Initialise()
{
var container = BuildUnityContainer();
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new ControllerActivator()));
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
container.Configure(c => c.Scan(scan =>
{
scan.AssembliesInBaseDirectory();
scan.With<UnityConfiguration.FirstInterfaceConvention>().IgnoreInterfacesOnBaseTypes();
}));
return container;
}
}
public class ControllerActivator : IControllerActivator
{
IController IControllerActivator.Create(RequestContext requestContext, Type controllerType)
{
return GlobalConfiguration.Configuration.DependencyResolver.GetService(controllerType) as IController;
}
}
Я также использую UnityConfiguration (также от NuGet) для магии IoC.;)
Ответ 7
После прочтения ответов мне все равно пришлось много копаться, чтобы приземлиться на эту добычу, так что это на пользу сверстников:
Это все, что вам нужно сделать в ASP.NET 4 Web API RC (по состоянию на 8 августа 13):
- Добавить ссылку на "Microsoft.Practices.Unity.dll" [я на версии
3.0.0.0, добавленный через NuGet]
- Добавить ссылку на "Unity.WebApi.dll" [Я на версии 0.10.0.0, добавлен через NuGet]
- Зарегистрируйте сопоставления типов с контейнером - аналогично коду в Bootstrapper.cs, который добавляется в ваш проект по проекту Unity.WebApi.
- В контроллерах, которые наследуются от класса ApiController, создайте параметризованные конструкторы, которые имеют свои типы параметров в качестве отображаемых типов
Lo и вот, вы получаете зависимости, введенные в ваш конструктор без другой строки кода!
ПРИМЕЧАНИЕ. Я получил эту информацию из одного из комментариев в ЭТО блог от автора.
Ответ 8
У меня было исключение с тем же исключением, и в моем случае у меня был конфликт между MVC3 и MVC4. Это мешало моим контроллерам правильно регистрироваться с моим контейнером МОК. Проверьте свой web.config и убедитесь, что он указал на правильные версии MVC.
Ответ 9
Эта проблема возникает из-за регистрации контроллеров с Unity. Я решил это, используя регистрацию по соглашению, как показано ниже. Пожалуйста, отфильтруйте любые дополнительные типы по мере необходимости.
IUnityContainer container = new UnityContainer();
// Do not register ApiControllers with Unity.
List<Type> typesOtherThanApiControllers
= AllClasses.FromLoadedAssemblies()
.Where(type => (null != type.BaseType)
&& (type.BaseType != typeof (ApiController))).ToList();
container.RegisterTypes(
typesOtherThanApiControllers,
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.ContainerControlled);
Также в приведенном выше примере используется AllClasses.FromLoadedAssemblies()
. Если вы хотите загрузить сборки из базового пути, это может не работать так, как ожидалось, в проекте веб-API с использованием Unity. Пожалуйста, взгляните на мой ответ на другой вопрос, связанный с этим. fooobar.com/questions/76312/...
Ответ 10
У меня была такая же проблема при использовании пакета Unity.WebAPI NuGet. Проблема заключалась в том, что пакет никогда не добавлял вызов UnityConfig.RegisterComponents()
в мой Global.asax.
Global.asax.cs должен выглядеть следующим образом:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
UnityConfig.RegisterComponents();
...
}
}
Ответ 11
Краткое описание ASP.NET Web API 2.
Установите Unity
из NuGet.
Создайте новый класс под названием UnityResolver
:
using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;
public class UnityResolver : IDependencyResolver
{
protected IUnityContainer container;
public UnityResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
public object GetService(Type serviceType)
{
try
{
return container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return container.ResolveAll(serviceType);
}
catch (ResolutionFailedException)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
var child = container.CreateChildContainer();
return new UnityResolver(child);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
container.Dispose();
}
}
Создайте новый класс под названием UnityConfig
:
public static class UnityConfig
{
public static void ConfigureUnity(HttpConfiguration config)
{
var container = new UnityContainer();
container.RegisterType<ISomethingRepository, SomethingRepository>();
config.DependencyResolver = new UnityResolver(container);
}
}
Изменить App_Start → WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
UnityConfig.ConfigureUnity(config);
...
Теперь это сработает.
Исходный источник, но немного измененный:
https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection