Unity kullanarak ASP.NET Web API Denetleyicisine Bağımlılıklar Eklenemiyor


82

ASP.NET WebAPI denetleyicilerine bağımlılıklar enjekte etmek için bir IoC kapsayıcısı kullanarak başarılı olan var mı? İşe yarayacak gibi görünmüyorum.

Şu an yaptığım bu.

Benim 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;
    }

Kontrolör fabrikam:

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);
                }
            }

Bağımlılıkları çözmek için asla birlik dosyama bakmıyor gibi görünüyor ve şöyle bir hata alıyorum:

'PersonalShopper.Services.WebApi.Controllers.ShoppingListController' türünde bir denetleyici oluşturmaya çalışırken bir hata oluştu. Denetleyicinin parametresiz bir genel oluşturucuya sahip olduğundan emin olun.

System. (HttpControllerContext controllerContext, String controllerName) System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal (HttpRequestMessage isteği, CancellationToken cancellationToken), System.Web.Http.Dispatcher.Httpendcontroller (HttpControllerContext controllerContext, String controllerName)

Denetleyici şöyle görünür:

public class ShoppingListController : System.Web.Http.ApiController
    {
        private Repositories.IProductListRepository _ProductListRepository;


        public ShoppingListController(Repositories.IUserRepository userRepository,
            Repositories.IProductListRepository productListRepository)
        {
            _ProductListRepository = productListRepository;
        }
}

Unity dosyam şöyle görünüyor:

<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>

Denetleyicinin kendisi için bir kaydım olmadığını unutmayın, çünkü mvc'nin önceki sürümlerinde denetleyici fabrikası, çözülmesi gereken bağımlılıkların farkına varırdı.

Görünüşe göre kontrolör fabrikam hiç aranmıyor.

Yanıtlar:


42

Anladım.

İçin ApiControllers MVC 4 kullanan System.Web.Http.Dispatcher.IHttpControllerFactory ve System.Web.Http.Dispatcher.IHttpControllerActivator denetleyicileri oluşturun. Bunların uygulanmasının ne olduğunu kaydetmek için statik bir yöntem yoksa; bunlar çözüldüğünde, mvc çerçevesi bağımlılık çözümleyicide uygulamaları arar ve bulunmazlarsa varsayılan uygulamaları kullanır.

Aşağıdakileri yaparak çalışan denetleyici bağımlılıklarının birim çözümlemesine sahibim:

Bir UnityHttpControllerActivator oluşturuldu:

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);
    }
}

Bu denetleyici etkinleştiriciyi, birlik kabının kendisinde uygulama olarak kaydetti:

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.
       });
}

Lamda'yı nasıl uyguladınız GlobalConfiguration.Configuration.ServiceResolver.SetResolver()?
jrummell

7
Bu gönderi güncel değil. MVC RC ile daha temiz bir çözüm için CodeKata tarafından verilen yanıta bakın.
Matt Randle

Bu modası geçmiş. Microsoft, Web API ile DI yapan bir Nuget paketine sahiptir. Cevabımı gör.
garethb

37

1
Bu, MVC RC ile çok daha iyi bir çözümdür. IHttpControllerActivator'ın bir uygulamasını sağlamanıza gerek yoktur. Bunun yerine System.Web.Http.Dependencies.IDependencyResolver uygularsınız.
Matt Randle

22
Bloglara bağlanmanın büyük bir hayranı değilsiniz - burada özetleyip bağlantıyı koyabilir misiniz? :)
Nate-Wilkins

3
İyi bir yaklaşım gibi görünüyor ancak aşağıdaki gibi birkaç hata alıyorum. Görünüşe göre yeni çözücü birkaç türü çözemiyor. Bağımlılığın çözümlenmesi başarısız oldu, type = "System.Web.Http.Hosting.IHostBufferPolicySelector", name = "(none)". Şu sırada istisna oluştu: çözülürken. Özel durum: InvalidOperationException - IHostBufferPolicySelector türünün erişilebilir bir kurucusu yoktur. --- İstisna anında kapsayıcı şöyleydi:
Resolving

Microsoft, Web API ile DI yapan bir Nuget paketine sahiptir. Cevabımı gör.
garethb

24

Tüm bunları sıralayacak ve ayrıca IDisposable bileşenlere hitap edecek Unity.WebApi NuGet paketine bir göz atmak isteyebilirsiniz.

görmek

http://nuget.org/packages/Unity.WebAPI

veya

http://www.devtrends.co.uk/blog/introducing-the-unity.webapi-nuget-package


2
DI'yi oldukça temiz bir şekilde uygulamak için Unity.mvc3 (MVC4 ile de çalışır) ve Unit.WebApi'nin bir kombinasyonunu kullanmanın bir yolunu içeren burada ( netmvc.blogspot.com/2012/04/… ) iyi bir blog yazısı var .
Phred Menyhert

1
Açıklamada belirtilen bağlantı, şu anda bununla karşılaşan herkes için güncellenmiş API'yi yansıtır: GlobalConfiguration.Configuration.ServiceResolver.SetResolver (yeni Unity.WebApi.UnityDependencyResolver (kapsayıcı)); artık GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver (kapsayıcı);
Robert Zahm

Dikkat edin, aynı zamanda özel mesajlardan daha iyi hata ayıklama mesajları sağlar UnityResolver.
Ioannis Karadimas

9

Microsoft bunun için bir paket oluşturdu.

Paket yöneticisi konsolundan aşağıdaki komutu çalıştırın.

Unity.AspNet.WebApi yükleme paketi

Zaten Unity kuruluysa, App_Start \ UnityConfig.cs üzerine yazmak isteyip istemediğinizi soracaktır. Hayır cevabını ver ve devam et.

Başka bir kodu değiştirmeye gerek yoktur ve DI (birleşik) çalışacaktır.


nuget UnityConfig.cs üzerine yazmak istediğinde neden 'hayır' olarak yanıtladığımızı bana bildirebilir misin? Soru 2: Unity.AspNet.WebApi'yi kullanmak için bazı örnek kodlar var mı?
Thomas.Benz

Üzgünüm, Unity'nin kolayca yapamadığı ve Autofac'ın kutudan çıktığı bazı şeyler ihtiyacım olduğu için Autofac'e geçtim. Ancak bellekten hayır demeniz gerekir, zaten kurulu bir biriminiz varsa zaten çalışan birim yapılandırmanızın üzerine yazmak istemiyorsanız, yalnızca unity webapi yapılandırmasını eklemek istiyorsunuz. Unity'yi webapi'de kullanmak, normal olarak tam olarak nasıl kullanacağınızdır. Diğer sınıflarınız için olduğu gibi bağımlılıklarınızı enjekte etmiyorsa, bir soru göndermenizi öneririm. Diğer sınıflardan (denetleyiciler gibi) webapi'ye enjekte etmenin farklı bir yolu yoktur
garethb

@garethb: unity.webapi kullanıyorum, ancak sevmediğim ControllerContext'i çözmek için birim testlerine birkaç satır kod eklemem gerekiyor. Diyelim ki, Unity.AspNet.WebApi kullanırsam, ControllerContext'i otomatik olarak UnitTests projesi içinden çözen adres olur mu? Lütfen öner
sam

Ben öyle düşünmüyorum. İkisinin de benzer şekilde çalıştığından oldukça eminim. Birim testlerimde yeni bir sahte bağlam oluşturuyorum.
garethb

@garethb: Hızlı yanıtınız için teşekkür ederim. alay etmek işe yaradı. Unity.WebApi veya Unity.Aspnet.webapi kullanarak UrlHelper'ı enjekte edemiyorum. Unity kullanırken bunu nasıl yaptığını hatırlıyor musun? Şimdiden teşekkür ederim
sam

3

Aynı hatayı aldım ve birkaç saat boyunca internette çözüm arıyordum. Sonunda, WebApiConfig.Register'ı çağırmadan ÖNCE Unity'yi kaydetmem gerektiği ortaya çıktı. Global.asax'ım şimdi şöyle görünüyor

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);
   }
}

Benim için bu, Unity'nin denetleyicilerimdeki bağımlılıkları çözememesi sorununu çözdü


2

Yakın tarihli bir RC'de, artık bir SetResolver yöntemi olmadığını görüyorum. Hem IoC'yi denetleyici hem de webapi için etkinleştirmek için Unity.WebApi (NuGet) ve aşağıdaki kodu kullanıyorum:

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;
    }
}

Ayrıca IoC sihri için UnityConfiguration (ayrıca NuGet'ten) kullanıyorum. ;)


RequestContext.Configuration.DependencyResolver.GetService () kullanmak daha iyi olmaz mıydı?
Alwyn

2

Cevapları okuduktan sonra, bu duruma inmek için hala çok fazla araştırma yapmak zorunda kaldım, bu yüzden işte akranların yararına: ASP.NET 4 Web API RC'de yapmanız gereken tek şey bu (8 Ağustos'ta olduğu gibi) '13):

  1. "Microsoft.Practices.Unity.dll" için bir başvuru ekleyin [3.0.0.0 sürümündeyim, NuGet aracılığıyla eklenmiştir]
  2. "Unity.WebApi.dll" için bir başvuru ekleyin [NuGet aracılığıyla eklenen 0.10.0.0 sürümündeyim]
  3. Tür eşlemelerinizi kapsayıcıya kaydedin - Unity.WebApi projesi tarafından projenize eklenen Bootstrapper.cs koduna benzer.
  4. ApiController sınıfından miras alan denetleyicilerde, parametre türlerini eşlenmiş türler olarak alan parametreleştirilmiş yapıcılar oluşturun

Bakın, bağımlılıkları başka bir kod satırı olmadan kurucunuza enjekte edersiniz!

NOT: Bu bilgiyi yazarının yazdığı BU blogdaki yorumlardan birinden aldım .


2

ASP.NET Web API 2 için kısa özet.

UnityNuGet'ten yükleyin .

Adında yeni bir sınıf oluşturun 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();
    }
}

Adında yeni bir sınıf oluşturun 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'ı Düzenle -> WebApiConfig.cs

public static void Register(HttpConfiguration config)
{
    UnityConfig.ConfigureUnity(config);
    ...

Şimdi işe yarayacak.

Orijinal kaynak ancak biraz değiştirilmiş: https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection


1

Aynı istisnaya sahiptim ve benim durumumda MVC3 ve MVC4 ikili dosyaları arasında bir çatışma yaşadım. Bu, denetleyicilerimin IOC kapsayıcıma düzgün şekilde kaydedilmesini engelliyordu. Web.config dosyanızı kontrol edin ve MVC'nin doğru sürümlerini gösterdiğinden emin olun.


Bu, Razor Sorunlarımı çözdü, ancak api denetleyicilerini çözme sorununu çözmedi.
Oved D

1

Bu sorun, Denetleyicilerin Unity'ye kaydedilmesi nedeniyle oluşur. Bunu aşağıda gösterildiği gibi konvansiyonel kayıt kullanarak çözdüm. Lütfen ek türleri gerektiği gibi filtreleyin.

    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);

Ayrıca yukarıdaki örnek kullanır AllClasses.FromLoadedAssemblies(). Derlemeleri temel yoldan yüklemeye bakıyorsanız, Unity kullanan bir Web API projesinde beklendiği gibi çalışmayabilir. Lütfen bununla ilgili başka bir soruya verdiğim yanıta bir göz atın. https://stackoverflow.com/a/26624602/1350747


0

Unity.WebAPI NuGet paketini kullanırken aynı sorunu yaşadım. Sorun, paketin UnityConfig.RegisterComponents()Global.asax'ıma hiçbir zaman bir çağrı eklemesiydi .

Global.asax.cs şöyle görünmelidir:

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        UnityConfig.RegisterComponents();
        ...
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.