在 ASP.NET MVC 应用中使用 NInject 注入 ASMX 类型的 Web Service
这几天,有同学问到为什么在 ASP.NET MVC 应用中,无法在 .ASMX 中使用 NInject 进行注入。
现象
比如,我们定义了一个接口,然后定义了一个实现。
public interface IMessageProvider
{
string GetMessage();
}
定义一个接口的实现。
public class NinjectMessageProvider : IMessageProvider
{
public string GetMessage()
{
return "This message was provided by Ninject";
}
}
在 ASMX 中进行 NInject 进行注入。
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class MyService
{ [Ninject.Inject]
public IMessageProvider MessageProvider { set; get; } [WebMethod]
public string HelloWorld()
{
var result = MessageProvider.GetMessage();return "Hello World";
}
}
你会发现,注入失败!!!
System.NullReferenceException: 未将对象引用设置到对象的实例。
分析
Why?
这需要从 ASP.NET MVC 应用的结构说起了,相对与 WebForm 应用,MVC 是微软重新打造的崭新 Web 应用框架,虽然已经诞生多年了,没有那么新了,但是,从理念到实现确实是革命性的不同。这里面最核心的一个不同,就是在 MVC 中从框架级别全面使用了 DI 容器。在 MVC 中,所有对象的创建都使用了容器来获取,你自己定义的类就看你自己了,反正系统已经做到了。
在使用 NInject 的时候,一个重要的步骤就是在 global.asax 中的第一行就替换掉系统默认的容器,这样保证新创建的对象是从 NInject 中获取的,以便 NInject 完成依赖注入的实现。
System.Web.Mvc.DependencyResolver.SetResolver(new NinjectDependencyResolver());
上面的这行代码大家应该很熟悉了,这样就把对象创建的所有权转移到了 NInject 手中。
但是,这是对 MVC 来说的,对于原来的 WebForm, ASMX 等等,MVC 是不管的,Scott Hanselman 有一篇文章讨论了这个问题。
Plug-In Hybrids: ASP.NET WebForms and ASP.MVC and ASP.NET Dynamic Data Side By Side
所以,好消息是在 MVC 应用中,可以继续使用原有的 ASPX,ASMX 等等类型的特性,坏消息就是,这些类型的对象都不是 MVC 来管理创建和使用的,也就是说,MVC 的 DI 容器不管理这些对象,所以,在使用 NInject 的时候,也就无法实现注入了。
思路
如果我们能够获取刚刚创建的 MyService 对象,然后自己使用 NInject 注入一下,不就解决了吗?只要我们能够获取刚刚创建的对象,也能够获取 NInject 的容器,调用一下容器提供的注入方法 Inject 就可以了。
//
// Summary:
// Injects the specified existing instance, without managing its lifecycle.
//
// Parameters:
// instance:
// The instance to inject.
//
// parameters:
// The parameters to pass to the request.
void Inject(object instance, params IParameter[] parameters);
解决问题
获取 NInject 容器
在我们 NInject 管理对象中,就可以直接获取容器对象,我们可以添加一个注入特定对象的方法。
public void Inject(object target)
{
this.kernel.Inject(target);
}
以后,直接调用这个方法就可以了。完整的类定义如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Ninject; namespace MvcNinjectAsmx.Models
{
public class NinjectDependencyResolver
: System.Web.Mvc.IDependencyResolver
{
private Ninject.IKernel kernel;
public NinjectDependencyResolver()
{
this.kernel = new Ninject.StandardKernel();
this.AddBindings();
} private void AddBindings()
{
this.kernel.Bind<IMessageProvider>()
.To<NinjectMessageProvider>();
} public object GetService(Type serviceType)
{
return this.kernel.TryGet(serviceType);
} public IEnumerable<object> GetServices(Type serviceType)
{
return this.kernel.GetAll(serviceType);
} public void Inject(object target)
{
this.kernel.Inject(target);
}
}
}
这是个实例方法,在整个 MVC 中只有一个实例,就是在 Global.asax 中创建的那个,以后,我们可以从 MVC 中直接获取这个对象,并调用我们的注入方法。
var resolver = System.Web.Mvc.DependencyResolver.Current
as NinjectDependencyResolver;
resolver.Inject(this);
获取新创建的服务对象
现在的问题变成了如何获取刚刚创建的 MyService 服务对象了。
最为简单的方式,是在使用之前,调用我们的注入方法。比如在调用需要注入的对象之前,手工完成注入。
[Ninject.Inject]
public IMessageProvider MessageProvider { set; get; } [WebMethod]
public string HelloWorld()
{
var resolver = System.Web.Mvc.DependencyResolver.Current
as NinjectDependencyResolver;
resolver.Inject(this); var result = MessageProvider.GetMessage();
return "Hello World";
}
这样有点太笨了。
在 ASP.NET 中服务对象都是从 HandlerFactory 中创建的,我们应该可以替换掉 .asmx 的处理器工厂,如何能够获取到刚刚创建的 MyService 对象,就可以完美处理这个问题了。
打开系统的 web.config 文件,可以找到 .asmx 的处理器管理配置信息。
<add path="*.asmx" verb="*" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False" />
StackOverflow 上的一篇文章,描述了如何获取 ScriptHandlerFactory 创建的处理器。
Getting ScriptHandlerFactory handler
public class WebServiceFactory : IHttpHandlerFactory
{
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
PrivilegedCommand cmd = new PrivilegedCommand();
SecurityCritical.ExecutePrivileged(new PermissionSet(PermissionState.Unrestricted), new SecurityCritical.PrivilegedCallback(cmd.Execute));
var handlerFactory = cmd.Result;
var handler = handlerFactory.GetHandler(context, context.Request.RequestType, url, pathTranslated); // Inject
var resolver = System.Web.Mvc.DependencyResolver.Current
as NinjectDependencyResolver;
resolver.Inject(handler); return handler;
} public void ReleaseHandler(IHttpHandler handler)
{ } private class PrivilegedCommand
{
public IHttpHandlerFactory Result = null; public void Execute()
{
Type handlerFactoryType = typeof(System.Web.Services.WebService).Assembly.GetType("System.Web.Services.Protocols.WebServiceHandlerFactory");
Result = (IHttpHandlerFactory)Activator.CreateInstance(handlerFactoryType, true);
}
}
}
实际上,还是注入失败了,如果检查一下,可以发现,我们获取的 handler 并不是 MyService,而是下面的类型。
System.Web.Services.Protocols.SyncSessionlessHandler
在这个类的内部通过反射来创建 MyService。我们还是没有拿到刚刚创建的 MyService 对象来实现我们的注入。
所以,这个方法就算了。
换一个思路,我们可以给 MyService 对象提供一个构造函数,这个构造函数总是要被调用的,我们在这里来实现注入不就可以了吗?
另一篇文章提到这个思路:
Ninject w/ ASMX web service in a MVC3/Ninject 3 environment
public class MyService
{ [Ninject.Inject]
public IMessageProvider MessageProvider { set; get; } public MyService()
{
var resolver = System.Web.Mvc.DependencyResolver.Current
as NinjectDependencyResolver;
resolver.Inject(this);
} [WebMethod]
public string HelloWorld()
{
var result = MessageProvider.GetMessage();
return "Hello World";
}
}
如果我们定义了多个 WebService ,这样的话,在每个构造函数中都要写上注入的这两行,还是再优化一下。
定义一个支持 NInject 注入的基类来完成这个工作。
public class NInjectWebService
{
public NInjectWebService()
{
var resolver = System.Web.Mvc.DependencyResolver.Current
as NinjectDependencyResolver;
resolver.Inject(this);
}
}
this 就是我们刚刚创建的对象实例。
然后,将我们的服务类定义成派生自这个类的基类。
public class MyService : NInjectWebService
{
[Ninject.Inject]
public IMessageProvider MessageProvider { set; get; } [WebMethod]
public string HelloWorld()
{
var result = MessageProvider.GetMessage();
return "Hello World";
}
}
这样,以后的 WebServe 只要从这个基类派生就可以了。
在 ASP.NET MVC 应用中使用 NInject 注入 ASMX 类型的 Web Service的更多相关文章
- ASP.NET MVC学前篇之Ninject的初步了解
ASP.NET MVC学前篇之Ninject的初步了解 1.介绍 废话几句,Ninject是一种轻量级的.基础.NET的一个开源IoC框架,在对于MVC框架的学习中会用到IoC框架的,因为这种IoC开 ...
- 【初学者指南】在ASP.NET MVC 5中创建GridView
介绍 在这篇文章中,我们将会学习如何在 ASP.NET MVC 中创建一个 gridview,就像 ASP.NET Web 表单中的 gridview 一样.服务器端和客户端有许多可用的第三方库,这些 ...
- [转]在 ASP.NET MVC 4 中创建为移动设备优化的视图
原文链接 https://msdn.microsoft.com/zh-cn/magazine/dn296507.aspx 如果深入探讨有关编写移动设备网站的常识性考虑因素,会发现其中有一种内在矛盾. ...
- ASP.NET MVC 4中如何为不同的浏览器自适应布局和视图
在ASP.NET MVC 4中,可以很简单地实现针对不同的浏览器自适应布局和视图.这个得归功于MVC中的"约定甚于配置"的设计理念. 默认的自适应 MVC 4自动地为移动设备浏览器 ...
- 在ASP.NET MVC环境中使用加密与解密
在.NET Framework 4.5的NET框架中,在程序中加密与解密很方便.现在均学习ASP.NET MVC程序了,因此Insus.NET也在此写个学习的例子.在需要时可以参考与查阅. 写一个Ut ...
- 在 ASP.NET MVC 项目中使用 WebForm、 HTML
原文地址:http://www.cnblogs.com/snowdream/archive/2009/04/17/winforms-in-mvc.html ASP.NET MVC和WebForm各有各 ...
- C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(中)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(中)),不对的地方欢迎指出与交流. 章节出自<Professional C# ...
- asp.net MVC 框架中控制器里使用Newtonsoft.Json对前端传过来的字符串进行解析
下面我用一个实例来和大家分享一下我的经验,asp.net MVC 框架中控制器里使用Newtonsoft.Json对前端传过来的字符串进行解析. using Newtonsoft.Json; usin ...
- 在已有的Asp.net MVC项目中引入Taurus.MVC
Taurus.MVC是一个优秀的框架,如果要应用到已有的Asp.net MVC项目中,需要修改一下. 1.前提约定: 走Taurus.MVC必须指定后缀.如.api 2.原项目修改如下: web.co ...
随机推荐
- S盒
在密码学中,一个S盒(Substitution-box,置换盒)是对称密钥算法执行置换计算的基本结构.在块密码中,它们通常用于模糊密钥和密文之间的关系——香农的混淆理论.[1] 通常,S-Box接受特 ...
- Redis主从同步介绍
Redis主从同步命令和配置项 启动主从复制:master无需任何操作,slave中使用以下任意一种开启复制功能 (1).通过配置文件启动主从复制: 在redis.conf中加入"slave ...
- thinkphp禁止模版标签解析
场景: 页面中某些样式或者js中含有tp定义的模版标签,如果被tp当成模版标签解析,就会解析异常. tp中提供了<literal></literal>标签用于禁止标签内部的代码 ...
- LDAP过滤器使用说明(用户、组和容器的默认 LDAP 过滤器和属性)
说明来源:http://docs.oracle.com/html/E35191_01/ldap-filters-attrs-users.html#ldap-filters-attributes-use ...
- linux下查看系统进程占用的句柄数
---查看系统默认的最大文件句柄数,系统默认是1024 # ulimit -n 1024 ----查看当前进程打开了多少句柄数 # lsof -n|awk '{print $2}'|sort|uniq ...
- string和stringBuilder区别
C# String 对象是不可改变的.每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间.在需要对字符串执行重复修改的情况下,与 ...
- SVN在团队项目中的使用技巧:[2]Tag操作
SVN是Subversion的简称,是一个开放源代码的版本控制系统 本节讲述SVN使用中的TAG操作 文中若有错误或不足之处,欢迎留言指正 工具/原料 电脑 SVN 方法/步骤 1.认识SVN中T ...
- (VS TFS) Adding existing project to solution in TFS.
正常的情况话,直接加入project,然后选择"Source control" -> “Add selected projects to source control.... ...
- SpringMVC控制器设值原理分析(ModelAndView的值通过HttpServletRequest直接取到的原因)
@RequestMapping("/userlist.do") public String getUserList(Model model){ HttpServletRequest ...
- Unable to locate Android SDK used by project
Unable to locate Android SDK used by project: DJIgojava.lang.RuntimeException: Unable to locate Andr ...