ASP.NET 5:依赖注入

时间:2024-01-12 16:51:50

ASP.NET 5:依赖注入

1.背景

如果某个具体的(或类)对象被客户程序所依赖,通常把它们抽象成抽象类或接口。简单说,客户程序摆脱所依赖的具体类型,称之为面向接口编程。

那么问题来了?如何选择客户程序所需要的实现类?在使用创建型模式下创建对象是不难解决这个问题。

但如果设计的不是具体业务逻辑,而是公共类库或框架程序,对外只提供抽象而已,该如何把外部使用的类型传递给它们?

我们可以采用“依赖注入”的方式,将加工好的抽象类型实体“注入”到客户程序中。

注:依赖注入DI(Dependency Injection),它和控制反转IOC(Inversion of Control)是同一个意思,不同术语叫法。

2.场景

客户程序获取年份,我们先设计一个接口:

ASP.NET 5:依赖注入
using System;

namespace Blog.Consoles
{
public interface ITimeProvider
{
DateTime CurrentDate { get; }
}
}
ASP.NET 5:依赖注入

并对这个接口实现(可以多种实现哦,这也是设计接口的原因):

ASP.NET 5:依赖注入
using System;

namespace Blog.Consoles
{
public class TimeProvider : ITimeProvider
{
public DateTime CurrentDate
{
get { return DateTime.Now; }
}
}
}
ASP.NET 5:依赖注入

那么我们在客户程序使用:

ASP.NET 5:依赖注入
using System;

namespace Blog.Consoles
{
class Program
{
static void Main(string[] args)
{
ITimeProvider tp = new TimeProvider();
Console.WriteLine(tp.CurrentDate.Year); Console.ReadKey();
}
}
}
ASP.NET 5:依赖注入

这样实现的依赖关系是:

ASP.NET 5:依赖注入

显然这样客户程序还需要知道具体类型TimeProvider,我们增加一个对象:

ASP.NET 5:依赖注入
using System;
using System.Collections.Generic; namespace Blog.Consoles
{
public class Assembler
{
//保存抽象类型和实体类型
static Dictionary<Type, Type> d = new Dictionary<Type, Type>(); static Assembler()
{
//注册抽象类型需要使用的实体类型
d.Add(typeof(ITimeProvider), typeof(TimeProvider));
} public object Create(Type type)
{
if ((type == null) || !d.ContainsKey(type))
{
throw new NullReferenceException();
} return Activator.CreateInstance(d[type]);
} public T Create<T>()
{
return (T)Create(typeof(T));
}
}
}
ASP.NET 5:依赖注入

此时再改造依赖关系:

ASP.NET 5:依赖注入

这样客户程序只依赖Assembler和ITimeProvider,并不知道TimeProvider存在。

接下来如何写注入代码?下面分几种方式。

3.构造注入

构造注入使用构造方法,通过Assembler或其它机制把抽象类型作为参数传递。其实现代码:

ASP.NET 5:依赖注入
using System;

namespace Blog.Consoles
{
class Program
{
public ITimeProvider _tp;
public Program(ITimeProvider tp)
{
_tp = tp;
} public int GetYear()
{
return _tp.CurrentDate.Year;
} static void Main(string[] args)
{
ITimeProvider time = new Assembler().Create<ITimeProvider>();
Program p = new Program(time);
Console.WriteLine(p.GetYear()); Console.ReadKey();
}
}
}
ASP.NET 5:依赖注入

4.设置注入

通过属性方法赋值实现的,相对于构造方法一次性注入的方式,设置注入可以在需要时有更改的机会。实现代码:

ASP.NET 5:依赖注入
using System;

namespace Blog.Consoles
{
class Program
{
public ITimeProvider _tp { get; set; } public int GetYear()
{
return _tp.CurrentDate.Year;
} static void Main(string[] args)
{
ITimeProvider time = new Assembler().Create<ITimeProvider>(); Program p = new Program();
p._tp = time; Console.WriteLine(p.GetYear()); Console.ReadKey();
}
}
}
ASP.NET 5:依赖注入

还可以简写:

ASP.NET 5:依赖注入
using System;

namespace Blog.Consoles
{
class Program
{
public ITimeProvider _tp { get; set; } public int GetYear()
{
return _tp.CurrentDate.Year;
} static void Main(string[] args)
{
var p = new Program() { _tp = new Assembler().Create<ITimeProvider>() }; Console.WriteLine(p.GetYear()); Console.ReadKey();
}
}
}
ASP.NET 5:依赖注入

5.MEF注入

导出部件:

ASP.NET 5:依赖注入

下面不使用Assembler对象,实现代码:

ASP.NET 5:依赖注入
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection; namespace Blog.Consoles
{
class Program
{
private static CompositionContainer container; private void Compose()
{
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
container = new CompositionContainer(catalog);
//将部件和宿主程序添加到组合容器
container.ComposeParts(this, new TimeProvider());
} //导入部件
[Import]
public ITimeProvider _tp { get; set; } public int GetYear()
{
return _tp.CurrentDate.Year;
} static void Main(string[] args)
{
var p = new Program();
p.Compose(); Console.WriteLine(p.GetYear()); Console.ReadKey();
}
}
}
ASP.NET 5:依赖注入

 6.ASP.NET 5注入

修改Startup.cs:

ASP.NET 5:依赖注入

测试:

ASP.NET 5:依赖注入

7.小结

关于依赖注入的方式,还有接口注入,自定义特性等(它们之间区别和选择,自行体会或查资料),不常用就不说了。

感受到ASP.NET 5自带DI爽就行啦!(ASP.NET 5真的海皮啦!)

后面的帖子,进入项目实战了,最近比较忙,请园友保持耐心。

分类: DotNet 2015