公司目前使用的依赖注入框架是Unity,所以就对这个框架进行了对应的学习,网上也有比较多的资料,不过注入方面大部分都是偏向于构造器注入、属性注入和方法注入等,好像还没有自动注入相关的文章,所以就有了本文。
测试环境:
vistual studio 2017
.net framework的版本为4.6.1
Demo源码:https://gitee.com/zxy15914507674/shared_resource_name/blob/master/依赖注入.rar
步骤(下面定义了那么多层是为了可扩展性):
一 新增类库项目,名为 '接口层'
1 定义接口ITestInterface1,并编写如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 接口层
{
public interface ITestInterface1
{
void TestMethod();
}
}
2 添加NuGet包, 搜索Unity并且安装,如下图(由于我的已经安装过了,就显示卸载):
3 定义特性类DefaultAttribute,并编写如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity;
using Unity.Lifetime;
namespace 接口层
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class DefaultAttribute : Attribute
{
public DefaultAttribute(Type registerAs)
{
this.RegisterAs = registerAs;
}
public Type RegisterAs { get; private set; }
/// <summary>
/// 进行注入
/// </summary>
/// <param name="container"></param>
/// <param name="type"></param>
public void OnVisit(IUnityContainer container, Type type)
{
if (type.IsAbstract) throw new ArgumentException(string.Format("虚拟类型{0}不能注册为服务实例。", type));
container.RegisterType(RegisterAs, type, new ContainerControlledLifetimeManager());
}
}
}
4 定义类UnityRoot,目的是实例化容器,编辑如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity;
namespace 接口层
{
//这里的UnityRoot类正常情况下不应该放在接口层,应该放到主程序能访问到的层,现在放在这里是为了方便测试
public class UnityRoot
{
public static IUnityContainer container = new UnityContainer();
}
}
二 新建类库项目,名为 ' 接口实现层'
1 添加‘接口层’ 的项目引用
2 新建类TestClass1来实现前面接口层的接口ITestInterface1,并编辑如下:
using System.Text;
using System.Threading.Tasks;
using 接口层;
namespace 接口实现层
{
[Default(typeof(ITestInterface1))]
public class TestClass1 : ITestInterface1
{
public void TestMethod()
{
Console.WriteLine("TestClass1类中的TestMethod方法");
}
}
}
3 生成前面所建立的两个项目
三 实现自动注入和测试
1 新建控制台项目作为测试项目,名为‘依赖注入’,并设为启动项目
2 添加 ‘接口层’ 的项目引用
3 添加NuGet包, 安装Unity
4 主程序编写如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using 接口层;
using Unity;
namespace 依赖注入
{
class Program
{
static void Main(string[] args)
{
#region 实现自动注入
//通过反射获取接口实现层.dll程序集中所有的Type,当然程序集的路径可以保持在数据库中
//需要替换成对应得路径
Assembly assembly = Assembly.LoadFile("F:\\vs2017Project\\依赖注入\\接口实现层\\bin\\Debug\\接口实现层.dll");
IList<Type> types = assembly.GetTypes();
//定义集合,存储在接口上标注DefaultAttribute的Type
IList<KeyValuePair<DefaultAttribute, Type>> items = new List<KeyValuePair<DefaultAttribute, Type>>();
foreach (Type type in types)
{
foreach (DefaultAttribute attr in type.GetCustomAttributes(typeof(DefaultAttribute), true))
{
items.Add(new KeyValuePair<DefaultAttribute, Type>(attr, type));
}
}
try
{
foreach (KeyValuePair<DefaultAttribute, Type> kvp in items)
{
//进行注入
(kvp.Key).OnVisit(UnityRoot.container, kvp.Value);
}
}
catch (Exception ex)
{
Console.WriteLine("自动注入出错");
}
#endregion
#region 测试(从容器中获取对象)
//测试:从容器中获取对象
ITestInterface1 testInterface1 = UnityRoot.container.Resolve<ITestInterface1>();
testInterface1.TestMethod();
Console.Read();
#endregion
}
}
}
5 运行效果如下图:
后期要扩展就方便多了,如在接口层新增接口ITestInterface2,在接口实现层新增接口ITestInterface2实现类TestClass2
接口ITestInterface2如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 接口层
{
public interface ITestInterface2
{
void TestMethod();
}
}
接口实现类TestClass2:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 接口层;
namespace 接口实现层
{
[Default(typeof(ITestInterface2))]
public class TestClass2 : ITestInterface2
{
public void TestMethod()
{
Console.WriteLine("TestClass2类中的TestMethod方法");
}
}
}
主程序测试如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using 接口层;
using Unity;
namespace 依赖注入
{
class Program
{
static void Main(string[] args)
{
#region 实现自动注入
//通过反射获取接口实现层.dll程序集中所有的Type,当然程序集的路径可以保持在数据库中
//需要替换成对应得路径
Assembly assembly = Assembly.LoadFile("F:\\vs2017Project\\依赖注入\\接口实现层\\bin\\Debug\\接口实现层.dll");
IList<Type> types = assembly.GetTypes();
//定义集合,存储在接口上标注DefaultAttribute的Type
IList<KeyValuePair<DefaultAttribute, Type>> items = new List<KeyValuePair<DefaultAttribute, Type>>();
foreach (Type type in types)
{
foreach (DefaultAttribute attr in type.GetCustomAttributes(typeof(DefaultAttribute), true))
{
items.Add(new KeyValuePair<DefaultAttribute, Type>(attr, type));
}
}
try
{
foreach (KeyValuePair<DefaultAttribute, Type> kvp in items)
{
//进行注入
(kvp.Key).OnVisit(UnityRoot.container, kvp.Value);
}
}
catch (Exception ex)
{
Console.WriteLine("自动注入出错");
}
#endregion
#region 测试(从容器中获取对象)
//测试:从容器中获取对象
ITestInterface1 testInterface1 = UnityRoot.container.Resolve<ITestInterface1>();
testInterface1.TestMethod();
ITestInterface2 testInterface2 = UnityRoot.container.Resolve<ITestInterface2>();
testInterface2.TestMethod();
Console.Read();
#endregion
}
}
}
效果图如下图: