ASMX依赖注入和IoC

26

我在尝试使用依赖注入和IoC来使我的asmx Web服务使用内部业务层服务时遇到了问题。我希望我的Web服务能够使用内部业务层服务。该Web服务将由来自不同域的外部客户端使用,主要用于发送和接收有关实体(例如订单和客户)的信息。

一个示例:

public class MyService : System.Web.Services.WebService
{
    [WebMethod]
    public string HelloWorld()
    {
        return new MyBusinessService().MyMethod();
    } 
}

public class MyBusinessService : IMyBusinessService
{
    public string MyMethod()
    {
        return "hello";
    }
}

我想使用依赖注入来消除创建服务对象时需要使用"new"关键字的需求,但我无法找到方法实现。我可以通过一种叫做“穷人版”依赖注入的方式实现,或者至少我认为是这个名字。

像这样:

public class MyService : System.Web.Services.WebService
{
    private IMyBusinessService _myService;

    public MyService(IMyBusinessService myService)
    {
        _myService = myService;
    }

    public MyService() : this(new MyBusinessServie()) { }

    [WebMethod]
    public string HelloWorld()
    {
        return _myService.MyMethod();
    }
}

然而,我根本搞不清楚如何使用IoC容器来注入我的依赖项,因为如果没有无参构造函数,我无法让服务运行起来。 请友善一点,我不是有经验的程序员,只是开始测试依赖注入,并成功在我的Windows表单应用程序中使用了StructureMap,但在这个问题上卡住了。

3个回答

43
很遗憾,在ASP.NET中无法通过构造函数注入进行Web服务。ASP.NET要求提供默认构造函数。MyService的构造函数是这种Web服务中最接近组合根的构造函数,而不使用DI容器。
在ASP.NET中,拥有多个组合根并不罕见。可以是各个Web服务和Web页面的构造函数。如果您正在使用ASP.NET MVC,则是ControllerFactory,它更加友好于DI。
对于您的实现,重要的部分不是将对象图的构建从Web服务中移动,因为这是您的组合根。主要的事情是尽可能使Web服务保持简单,将大部分逻辑保留在依赖关系中,以便进行测试或重用。从HTTP标头中提取信息是Web服务可以执行的任务之一,然后将该信息传递给依赖项。
参考DI模式和技术的好书是Mark Seemann的Dependency Injection in .NET
如果您的网络服务实现了 System.Web.IHttpHandler 而不是从 System.Web.Services.WebService 派生,您可以这样实现您的 DI:

Global.ashx.cs

public class Global : HttpApplication
{
    protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
    {
        var context = ((HttpApplication)sender).Context;

        var needsMyBusinessService = context.Handler as INeedMyBusinessService;
        if (needsMyBusinessService != null)
            needsMyBusinessService.MyBusinessService = new MyBusinessService();
    }
}

MyService.ashx.cs

public class MyService : IHttpHandler, INeedMyBusinessService
{
    public IMyBusinessService MyBusinessService { get; set; }

    public bool IsReusable { get { return true; } }

    public void ProcessRequest(HttpContext context)
    {
        // uses my dependency
    }
}

INeedMyBusinessService.cs

public interface INeedMyBusinessService
{
    IMyBusinessService MyBusinessService { get; set; }
}

然而,这种实现方式的问题在于它不能与实现System.Web.Services.WebService的Web服务一起使用,因为Web服务对象直到调用PreRequestHandlerExecute事件之后才被初始化,这是在调用ProcessRequest之前的最后一个事件。

如果您想为每个Web服务拥有一个唯一的实例,则上面的示例可以正常工作。如果您想要为每个Web服务请求拥有相同的实例(Singleton生命周期)MyBusinessService,则可以像下面这样实现Global.ashx.cs文件:

public class Global : HttpApplication
{
    private static IMyBusinessService businessService;

    protected void Application_Start(object sender, EventArgs e)
    {
        Global.businessService = new MyBusinessService();
    }

    protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
    {
        var context = ((HttpApplication)sender).Context;

        var needsMyBusinessService = context.Handler as INeedMyBusinessService;
        if (needsMyBusinessService != null)
            needsMyBusinessService.MyBusinessService = Global.businessService;
    }
}

3

在 WebService 上,你只能有一个没有参数的构造函数。然而,你可以在 WebService 类上拥有一个属性,在没有参数的构造函数内调用像 _myDependency = container.resolve(); 这样的方法。


1
这是一个老问题,不太可能帮助到很多人,但你基本上需要实现IDependencyResolverIDependencyScope,当ServiceProvider::GetService()无法返回所请求类型的实例时,该类型将被明确地实例化(最好使用容器来解析构造函数参数)。
可以使用反射或像ActivatorUtilities.GetServiceOrCreateInstance(...)这样的现代方法来"明确地实例化"该类型。
默认情况下,当依赖项解析器未为控制器、页面、模块(Web服务)等请求的类型提供实例时,运行时将尝试使用事实上的Activator类来创建实例,而Activator只能创建具有公共的无参构造函数的类型的实例。
因此,说你的类型需要一个公共的无参构造函数只是部分正确的。如果你无法或不愿意创建IDependencyResolver/IDependencyScope实现,那么自定义依赖项解析器实现是一件微不足道的事情,MSDN有基本实现的文章和示例。
唯一需要的另一件事情是明显的 DI 容器,其中包含类型注册,以及在 ASP.NET 中注册的 IDependencyResolver,利用您的 DI 容器。今天,这可能类似于 Microsoft.Extensions.DependencyInjection,但对于问题的日期和内容,也可以是 Unity(依赖注入框架,而不是游戏引擎)。配置和集成 DI 容器略微超出了 Q&A 的范围,可能已经有其他 Q&A 涵盖了这方面的内容。我会注意到,如果您正在构建自己的 IDependencyResolver,即使您使用的是来自 2000 年代的 ASP.NET 版本,您也可以选择使用更现代的 DI 框架(例如在 ASP.NET Core 脚手架中使用的框架)。底层 DI 框架是无关紧要的,关键是 Dependency Resolver。

另请参阅:

希望有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接