Không thể đưa phụ thuộc vào Bộ điều khiển API Web ASP.NET bằng Unity


82

Có ai đã thành công khi sử dụng vùng chứa IoC để đưa các phụ thuộc vào bộ điều khiển ASP.NET WebAPI không? Tôi dường như không thể làm cho nó hoạt động.

Đây là những gì tôi đang làm bây giờ.

Trong của tôi 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;
    }

Nhà máy sản xuất bộ điều khiển của tôi:

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

Dường như không bao giờ tìm kiếm trong tệp thống nhất của tôi để giải quyết các phụ thuộc và tôi gặp lỗi như:

Đã xảy ra lỗi khi cố gắng tạo bộ điều khiển loại 'PersonalShopper.Services.WebApi.Controllers.ShoppingListController'. Đảm bảo rằng bộ điều khiển có một hàm tạo công khai không tham số.

tại System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create (HttpControllerContext controllerContext, Type controllerType) tại System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateInstance (HttpControllerContext controllerContext, Type controllerType) tại System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateInstance (HttpControllerControllerContext controller AtContext.llerWreatepaultControllerContext controller. (HttpControllerContext controllerContext, String controllerName) tại System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal (HttpRequestMessage request, Can HủyToken hủyToken) tại System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal (yêu cầu HttpRequestMessage, Can Hủy

Bộ điều khiển trông giống như:

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


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

Tệp thống nhất của tôi trông giống như:

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

Lưu ý rằng tôi không có đăng ký cho chính bộ điều khiển vì trong các phiên bản trước của mvc, nhà máy sản xuất bộ điều khiển sẽ tìm ra rằng các phụ thuộc cần được giải quyết.

Có vẻ như nhà máy điều khiển của tôi không bao giờ được gọi.

Câu trả lời:


42

Tìm ra.

Đối với ApiControllers , MVC 4 sử dụng System.Web.Http.Dispatcher.IHttpControllerFactorySystem.Web.Http.Dispatcher.IHttpControllerActivator để tạo bộ điều khiển. Nếu không có phương thức tĩnh nào để đăng ký việc thực hiện chúng là gì; khi chúng được giải quyết, khuôn khổ mvc sẽ tìm kiếm các triển khai trong trình giải quyết phụ thuộc và nếu chúng không được tìm thấy, hãy sử dụng các triển khai mặc định.

Tôi nhận được giải pháp thống nhất của các phụ thuộc bộ điều khiển hoạt động bằng cách thực hiện như sau:

Đã tạo 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);
    }
}

Đã đăng ký trình kích hoạt bộ điều khiển đó làm quá trình triển khai trong chính vùng chứa thống nhất:

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

Bạn đã triển khai lamda GlobalConfiguration.Configuration.ServiceResolver.SetResolver()như thế nào?
jrummell

7
Bài đăng này đã lỗi thời. Xem câu trả lời của CodeKata để biết giải pháp sạch hơn với MVC RC.
Matt Randle

Điều này đã lỗi thời. Microsoft có một gói Nuget thực hiện DI với API Web. Hãy xem câu trả lời của tôi.
garethb

37

Có một giải pháp tốt hơn hoạt động chính xác ở đây

http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver


1
Đây là một giải pháp tốt hơn nhiều với MVC RC. Bạn không cần cung cấp triển khai IHttpControllerActivator. Thay vào đó, bạn triển khai System.Web.Http.Dependencies.IDependencyResolver.
Matt Randle

22
Không phải là người thích liên kết đến các blog - bạn có thể tóm tắt ở đây và đặt liên kết không? :)
Nate-Wilkins

3
Có vẻ như đây là một cách tiếp cận tốt nhưng tôi gặp một số lỗi như sau. Có vẻ như trình phân giải mới không thể giải quyết một số loại. Không giải quyết được phần phụ thuộc, gõ = "System.Web.Http.Hosting.IHostBufferPolicySelector", name = "(none)". Đã xảy ra ngoại lệ while: trong khi giải quyết Ngoại lệ là: InvalidOperationException - Loại IHostBufferPolicySelector không có hàm tạo có thể truy cập. --- Tại thời điểm ngoại lệ, vùng chứa là: Hệ thống giải quyết
.Web.Http.Hosting.IHostBufferPolicySelector

Microsoft có một gói Nuget thực hiện DI với API Web. Hãy xem câu trả lời của tôi.
garethb

24

Bạn có thể muốn xem gói Unity.WebApi NuGet sẽ sắp xếp tất cả những điều này và cũng phục vụ cho các thành phần IDisposable.

xem

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

hoặc là

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


2
Có một bài đăng trên blog hay ở đây ( netmvc.blogspot.com/2012/04/… ) có hướng dẫn sử dụng kết hợp Unity.mvc3 (cũng hoạt động với MVC4) và Unit.WebApi để triển khai DI khá rõ ràng.
Phred Menyhert

1
Liên kết được đề cập trong nhận xét phản ánh API đã cập nhật, cho bất kỳ ai xem qua điều này ngay bây giờ: GlobalConfiguration.Configuration.ServiceResolver.SetResolver (new Unity.WebApi.UnityDependencyResolver (container)); bây giờ là GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver (container);
Robert Zahm

Hãy lưu ý, nó cũng cung cấp thông báo gỡ lỗi tốt hơn so với tùy chỉnh UnityResolver.
Ioannis Karadimas

9

Microsoft đã tạo một gói cho việc này.

Chạy lệnh sau từ bảng điều khiển trình quản lý gói.

install-package Unity.AspNet.WebApi

Nếu bạn đã cài đặt Unity, nó sẽ hỏi bạn có muốn ghi đè lên App_Start \ UnityConfig.cs hay không. Trả lời không và tiếp tục.

Không cần thay đổi bất kỳ mã nào khác và DI (với sự thống nhất) sẽ hoạt động.


bạn có thể cho tôi biết tại sao chúng tôi trả lời 'không' khi nuget nhắc ghi đè UnityConfig.cs không? Câu hỏi 2: Có một số mã mẫu để sử dụng Unity.AspNet.WebApi?
Thomas.Benz

Xin lỗi, tôi đã đổi sang Autofac vì có một số thứ tôi cần mà Unity không thực hiện dễ dàng và Autofac đã làm được. Nhưng từ bộ nhớ, bạn cần phải nói không nếu bạn đã cài đặt thống nhất vì bạn không muốn ghi đè cấu hình thống nhất đã hoạt động của mình, bạn chỉ muốn thêm cấu hình webapi thống nhất. Để sử dụng thống nhất trong webapi chính xác là cách bạn sử dụng nó bình thường. Nếu nó không tiêm các phụ thuộc của bạn như đối với các lớp khác của bạn, tôi khuyên bạn nên đăng một câu hỏi. Không có cách nào khác nhau của tiêm vào WebAPI hơn các lớp khác (như bộ điều khiển)
garethb

@garethb: Tôi đang sử dụng Uniform.webapi nhưng phải thêm vài dòng mã trong các bài kiểm tra đơn vị để giải quyết ControllerContext mà tôi không thích. Giả sử, nếu tôi sử dụng Unity.AspNet.WebApi, địa chỉ đó có tự động giải quyết ControllerContext từ bên trong dự án UnitTests không? Vui lòng đề xuất
sam

Tôi không nghĩ vậy. Khá chắc chắn rằng cả hai đều hoạt động theo cách tương tự. Tôi tạo một bối cảnh giả mới trong các bài kiểm tra đơn vị của mình.
garethb

@garethb: Cảm ơn bạn đã phản hồi nhanh chóng. chế nhạo đã hoạt động. Tôi không thể đưa UrlHelper bằng Unity.WebApi hoặc Unity.Aspnet.webapi. Bạn có nhớ mình đã làm như thế nào khi sử dụng Unity không? Cảm ơn bạn trước
sam

3

Tôi đã gặp lỗi tương tự và đang tìm kiếm giải pháp trên Internet trong vài giờ. Cuối cùng, có vẻ như tôi phải đăng ký Unity TRƯỚC KHI gọi WebApiConfig.Register. Global.asax của tôi bây giờ trông giống như

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

Đối với tôi, điều này đã giải quyết được vấn đề Unity không thể giải quyết các phụ thuộc trong bộ điều khiển của tôi


2

Trong RC gần đây, tôi thấy rằng không còn phương thức SetResolver nữa. Để bật cả IoC cho bộ điều khiển và webapi, tôi sử dụng Unity.WebApi (NuGet) và mã sau:

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

Tôi cũng sử dụng UnityConfiguration (cũng từ NuGet) cho phép thuật IoC. ;)


Sẽ tốt hơn nếu sử dụng requestContext.Configuration.DependencyResolver.GetService ()?
Alwyn

2

Sau khi đọc câu trả lời, tôi vẫn còn phải đào bới rất nhiều để đạt được vị trí gotcha này vì vậy đây là vì lợi ích của các đồng nghiệp: Đây là tất cả những gì bạn cần làm trong ASP.NET 4 Web API RC (vào ngày 8 tháng 8 '13):

  1. Thêm tham chiếu vào "Microsoft.Practices.Unity.dll" [Tôi đang sử dụng phiên bản 3.0.0.0, được thêm thông qua NuGet]
  2. Thêm tham chiếu vào "Unity.WebApi.dll" [Tôi đang sử dụng phiên bản 0.10.0.0, được thêm thông qua NuGet]
  3. Đăng ký các ánh xạ kiểu của bạn với vùng chứa - tương tự như mã trong Bootstrapper.cs được dự án Unity.WebApi thêm vào dự án của bạn.
  4. Trong bộ điều khiển kế thừa từ lớp ApiController, hãy tạo các hàm tạo được tham số hóa có kiểu tham số của chúng là kiểu được ánh xạ

Lo và kìa, bạn nhận được các phụ thuộc được đưa vào hàm tạo của bạn mà không cần một dòng mã nào khác!

LƯU Ý: Tôi nhận được thông tin này từ một trong những nhận xét trong blog NÀY của tác giả đó.


2

Tóm tắt ngắn gọn về ASP.NET Web API 2.

Cài đặt Unitytừ NuGet.

Tạo một lớp mới có tên 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();
    }
}

Tạo một lớp mới có tên UnityConfig:

public static class UnityConfig
{
    public static void ConfigureUnity(HttpConfiguration config)
    {
        var container = new UnityContainer();
        container.RegisterType<ISomethingRepository, SomethingRepository>();
        config.DependencyResolver = new UnityResolver(container);
    }
}

Chỉnh sửa App_Start -> WebApiConfig.cs

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

Bây giờ nó sẽ hoạt động.

Nguồn gốc nhưng được sửa đổi một chút: https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection


1

Tôi đã có cùng một ngoại lệ được ném và trong trường hợp của tôi, tôi có xung đột giữa mã nhị phân MVC3 và MVC4. Điều này đã ngăn bộ điều khiển của tôi được đăng ký đúng cách với vùng chứa IOC của tôi. Kiểm tra web.config của bạn và đảm bảo rằng nó được trỏ đến đúng phiên bản MVC.


Điều này đã khắc phục sự cố dao cạo của tôi, nhưng không khắc phục sự cố giải quyết bộ điều khiển api.
Oved D

1

Sự cố này xảy ra do đăng ký Bộ điều khiển với Unity. Tôi đã giải quyết điều này bằng cách sử dụng đăng ký theo quy ước như hình dưới đây. Vui lòng lọc ra bất kỳ loại bổ sung nào theo yêu cầu.

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

Cũng ví dụ trên sử dụng AllClasses.FromLoadedAssemblies(). Nếu bạn đang xem việc tải các hợp ngữ từ đường dẫn cơ sở, nó có thể không hoạt động như mong đợi trong một dự án API Web sử dụng Unity. Vui lòng xem câu trả lời của tôi cho một câu hỏi khác liên quan đến vấn đề này. https://stackoverflow.com/a/26624602/1350747


0

Tôi đã gặp vấn đề tương tự khi sử dụng gói Unity.WebAPI NuGet. Vấn đề là gói không bao giờ thêm cuộc gọi đến UnityConfig.RegisterComponents()trong Global.asax của tôi.

Global.asax.cs sẽ trông như thế này:

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        UnityConfig.RegisterComponents();
        ...
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.