在 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 ...
随机推荐
- 如何将NetBeans转换为英文版(图)
很郁闷,又是一个系统语言的问题. NetBeans下载下来之后,怎么装都是个中文版,或者半中文半英文,不伦不类的.上网找了一个圈,很多都讲得不清楚,最后终于有了一个答案: 在NetBeans的配置文件 ...
- Linux From Scratch [2]
1. gcc需要的一些lib GMP:A free library for arbitrary precision arithmetic, operating on signed integers, ...
- Linux命令之WC
$ wc story.txt39 237 1901 story.txt● Use -l for only line count● Use -w for only word count● Use -c ...
- js 字符串转换为数值
原帖地址:http://www.cnblogs.com/jenney-qiu/archive/2012/02/27/2369848.html 使用parseInt()你可以从字符串中获取数值,该方法接 ...
- h264 流、帧结构
H264元素的分层结构 H.264编码器输出的Bit流中,每个Bit都隶属于某个句法元素.句法元素被组织成有层次的结构,分别描述各个层次的信息. 在H.264 中,句法元素共被组织成 序列.图像.片 ...
- 解决RPM包相互依赖的有效方法
出自:http://blog.csdn.net/kai27ks/article/details/7473683 前言:常用RPM的朋友们都知道,RPM简单易用,但是它的依赖关系是最头疼的!有时候比方说 ...
- Linux中文件描述符fd和文件指针flip的理解
转自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299861.html 简单归纳:fd只是一个整数,在open时产生.起到一个索引的作用,进程通 ...
- 【转】C#中判断扫描枪输入与键盘输入
提出问题:在收货系统中,常常要用到扫描枪扫描条码输入到TextBox,当条码无法扫描时,需要手工输入.如果是扫描枪输入时,我们将自动去判读条码,而手工输入时,最终需要加按回车键确认后判读条码.这时候我 ...
- C#全角和半角转换
在计算机屏幕上,一个汉字要占两个英文字符的位置,人们把一个英文字符所占的位置称为"半角",相对地把一个汉字所占的位置称为"全角".在汉字输入时,系统提供&quo ...
- 兼容所有浏览器---无缝上下左右交叉运动----原生js+css
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...