首页 > 前端开发 > 最新文章

WebApi详解+Unity注入--下篇

CSDN博客 2026-05-14 17:16:13 人看过

16,IOC 容器Unity


DIP依赖倒置原则:

一种软件  架构设计 原则(抽象概念)。依赖抽象不依赖细节。

IOC控制反转(Inversion of Control

传统开发,上端依赖(调用/指定)下端对象,会有依赖,把对下端对象的依赖转移到第三方容器(工厂+配置+反射),使程序拥有更好的扩展性,是DIP 的具体实现方式,可以用来降低代码之间的额耦合度。

DI 即依赖注入(Dependency Injection):

是实现IOC的手段和方法,就是能做到构造某个对象时,将依赖的对象自动初始化并注入。

有三种注入方式:构造方法注入–属性注入–方法注入(按照时间顺序)。

构造方法注入用的最多,默认找参数最多的构造方法,可以不用特性,可以去掉对容器的依赖。

16.1,Unity

微软推出的IOC框架,使用这个框架,可以实现AOP切面编程,便于代码的后期维护,此外该框架还自带单例模式,可以提高程序的运行效率。

安装NuGet包:UnityUnity.AbstractionsUnity.Container

在这里插入图片描述

16.1.1,构造方法的注入

如果存在多个构造方法,且这些构造方法均适配依赖注入,那么默认情况下注入选择的是参数多的构造方法。但可通过[InjectionConstructor]特性指定特定的构造方法。

[InjectionConstructor]:标记指定的构造方法为构造方法注入

public class TestServiceB : ITestService.ITestServiceB    {        int id = 10;        [InjectionConstructor]//使用特性指定注入时选择此构造方法        public TestServiceB(ITestService.ITestServiceA testServiceA)        {        }        //进行构造方法注入时默认选择参数较多的构造方法,可使用[InjectionConstructor]特性指定注入构造方法        public TestServiceB(ITestService.ITestServiceA testServiceA1, ITestService.ITestServiceA testServiceA2)        {            var reuslt = Object.ReferenceEquals(testServiceA1, testServiceA2);        }        public void PrintInfo()        {            Console.WriteLine("TestServiceB");        }    }

16.1.2,属性的注入

在构建某一个对象的时候,如果明确需要做属性注入,该对象中的需要注入的属性,就会根据属性的  类 型,创建出对象,赋值给属性。

[Dependency]:标记属性为属性注入

[Dependency]//使用该特性进行属性注入        public ITestService.ITestServiceA TestServiceA { get; set; }

需要注意的是:注入顺序是首先注入构造方法,其次注入属性,再次注入方法,所以在构造方法里查看TestServiceA为null,当执行完构造方法后才再执行对属性的注入。

16.1.3,方法的注入

在构造某一个对象的时候,自动去执行某些方法,根据方法的参数类型,自动构造出参数的类型实例,传递到方法的参数中,就可以将这个参数注入到类的内部。

注意:方法的注入是在实例化对象的时候自动进行,不需要外部显式调用执行。方法类型需要为Public否则不能自动运行。

[InjectionMethod]:标记方法为方法注入

[InjectionMethod]        public void PrintInnerInfo(ITestService.ITestServiceA testServiceA)        {            Console.WriteLine($"方法注入:在对象实例化中自动调用。TestServicA.Id={testServiceA.Id}");        }

16.2,  生命周期


TransientLifetimeManager:瞬时生命周期


ContainerControlledLifetimeMannager: 单例生命周期


PerThreadLifetimeManager:线程单例生命周期

16.3,  配置文件

安装Nuget包:Unity.Configuration

在这里插入图片描述

示例:

<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <!-- Unity配置节声明 --> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Unity.Configuration"/> </configSections> <!-- Unity核心配置--> <unity> <containers> <container name="Container"> <!-- ITestService.ITestServiceA:接口名;ITestService:接口所在的程序集 --> <register type="ITestService.ITestServiceA,ITestService" mapTo="TestService.TestServiceA,TestService"> <!-- 瞬时生命周期,小写正确,可省略(默认就是transient) --> <lifetime type="transient"></lifetime> <!-- 单例写法,解开注释即用 --> <!--<lifetime type="singleton"></lifetime>--> </register> <register type="ITestService.ITestServiceB,ITestService" mapTo="TestService.TestServiceB,TestService"></register> </container> </containers> </unity> </configuration>

16.3.1,配置别名

当一个接口被多个类实现的时候,就需要在配置中指明名称,否则将以最后一个类的实例作为对象进行注入。

public class TestServiceA : ITestService.ITestServiceA    {        public int Id { get; set; } = 12;        public void PrintInfo()        {            Console.WriteLine("TestServiceA");        }    } public class TestServiceAA : ITestService.ITestServiceA    {        public int Id { get ; set ; }        public void PrintInfo()        {            Console.WriteLine("这是TestServiceAA"); ;        }    }

通过属性name指定名称

<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <!-- Unity配置节声明 --> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Unity.Configuration"/> </configSections> <!-- Unity核心配置--> <unity> <containers> <container name="Container"> <!-- ITestService.ITestServiceA:接口名;ITestService:接口所在的程序集 --> <!--通过name属性指定名称--> <register name="testServiceA" type="ITestService.ITestServiceA,ITestService" mapTo="TestService.TestServiceA,TestService"> <!-- 瞬时生命周期,小写正确,可省略(默认就是transient) --> <lifetime type="transient"></lifetime> <!-- 单例写法,解开注释即用 --> <!--<lifetime type="singleton"></lifetime>--> </register> <!--通过name属性指定名称--> <register name="testServiceAA" type="ITestService.ITestServiceA,ITestService" mapTo="TestService.TestServiceAA,TestService"> </register> <register type="ITestService.ITestServiceB,ITestService" mapTo="TestService.TestServiceB,TestService"></register> </container> </containers> </unity> </configuration>

通过名称选择注入

ExeConfigurationFileMap mapfile = new ExeConfigurationFileMap();            mapfile.ExeConfigFilename = System.IO.Path.Combine(Environment.CurrentDirectory, "config/unity.config");            Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(mapfile, ConfigurationUserLevel.None);            var section = configuration.GetSection(UnityConfigurationSection.SectionName) as UnityConfigurationSection;            Unity.UnityContainer container = new Unity.UnityContainer();            container.LoadConfiguration(section, "Container");            var serviceA = container.Resolve<ITestService.ITestServiceA>("testServiceA");            var serviceAA = container.Resolve<ITestService.ITestServiceA>("testServiceAA");

16.3.2,配置构造方法注入

默认情况下,容器实例化对象选择的无参构造方法,若要选择指定的构造方法有以下两种方法:

第一种:在需要调用的构造方法上添加[InjectionConstructor]特性,参考16.1.1节内容。

第二种:在配置文件中配置构造方法参数,在容器实例化中将根据参数类型,数量自动调用参数匹配的构造方法,这种方式不需要在构造方法上添加[InjectionConstructor]特性

<register  type="ITestService.ITestServiceA,ITestService" mapTo="TestService.TestServiceA,TestService"> <!-- 瞬时生命周期,小写正确,可省略(默认就是transient) --> <lifetime type="transient"></lifetime> <!-- 单例写法,解开注释即用 --> <!--<lifetime type="singleton"></lifetime>--> </register> <register type="ITestService.ITestServiceB,ITestService" mapTo="TestService.TestServiceB,TestService"> <!--指定构造函数--> <constructor> <!--参数1--> <!--name="id":表示构造方法的形参名为id--> <!--因为是Int32类型没有进行注入所以这里需要添加value属性值--> <param name="id" type="System.Int32" value="3"></param> <!--参数2--> <!--该参数是注入所以不需要添加value属性值,同时注册该类型的时候不能指定name否则将抛异常--> <param name="testServiceA"  ></param> </constructor> </register>

调用形参为idtestServiceA的构造方法

public class TestServiceB : ITestService.ITestServiceB    {        int id = 10;        //[InjectionConstructor]//指定注入此构造方法        public TestServiceB(ITestService.ITestServiceA testServiceA)        {        }        public TestServiceB(int id)        {            this.id = id;        }     //配置将调用这个方法        public TestServiceB(int id,ITestService.ITestServiceA testServiceA)        {            this.id = id;        }        [Dependency]        public ITestService.ITestServiceA TestServiceA { get; set; }        //进行构造方法注入时默认优先使用参数较多的构造方法,可使用[InjectionConstructor]特性指定注入构造方法        public TestServiceB(ITestService.ITestServiceA testServiceA1, ITestService.ITestServiceA testServiceA2)        {            var reuslt = Object.ReferenceEquals(testServiceA1, testServiceA2);        } }

特别注意:这里使用的ITestService.ITestServiceA配置时不能命名(即属性name赋值),命名后将抛异常。

错误示例:注册时设置了name属性

<register name="testServiceA" type="ITestService.ITestServiceA,ITestService" mapTo="TestService.TestServiceA,TestService"> <!-- 瞬时生命周期,小写正确,可省略(默认就是transient) --> <lifetime type="transient"></lifetime> <!-- 单例写法,解开注释即用 --> <!--<lifetime type="singleton"></lifetime>--> </register>

执行var serviceB = container.Resolve<ITestService.ITestServiceB>();抛出异常:

在这里插入图片描述

16.3.3,配置属性注入

<register type="ITestService.ITestServiceB,ITestService" mapTo="TestService.TestServiceB,TestService"> <!--指定构造函数--> <constructor> <!--参数1--> <!--name="id":表示构造方法的形参名为id--> <!--因为是Int32类型没有进行注入所以这里需要添加value属性值--> <param name="id" type="System.Int32" value="3"></param> <!--参数2--> <!--该参数是注入所以不需要添加value属性值,同时注册该类型的时候不能指定name否则将抛异常--> <param name="testServiceA"  ></param> </constructor> <!--配置属性注入--> <property name="TestServiceA"></property> </register>

// [Dependency] //通过配置声明注入        public ITestService.ITestServiceA TestServiceA { get; set; }

16.3.4,配置方法注入

<register type="ITestService.ITestServiceB,ITestService" mapTo="TestService.TestServiceB,TestService"> <!--指定构造函数--> <constructor> <!--参数1--> <!--name="id":表示构造方法的形参名为id--> <!--因为是Int32类型没有进行注入所以这里需要添加value属性值--> <param name="id" type="System.Int32" value="3"></param> <!--参数2--> <!--该参数是注入所以不需要添加value属性值,同时注册该类型的时候不能指定name否则将抛异常--> <param name="testServiceA"  ></param> </constructor> <!--配置属性注入--> <property name="TestServiceA"></property> <!--配置方法注入--> <method name="PrintConfigInfo"> <param name="testServiceA"></param> <param name="id" type="System.Int32" value="100"></param> </method> </register>

注入的方法

public void PrintConfigInfo(ITestService.ITestServiceA testServiceA,int id)        {            Console.WriteLine($"方法注入:在对象实例化中自动调用。TestServicA.Id={testServiceA.Id},注入的Id值为:{id}");        }

16.3.5,配置程序集位置

默认情况下,map To的程序集位于执行程序目录下,而如果不在执行程序目录下则需要进行如下配置:

注意是在app.config文件中,而不是自定义的config文件,在自定义的config文件中配置无效。

<?xml version="1.0" encoding="utf-8"?> <configuration>    <startup>        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />    </startup>  <runtime>    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <!-- probePrivatePath:指定程序集的私有探测目录,多个目录用分号;分隔 --> <!--指定 CLR 查找私有程序集的额外目录,lib 是相对于执行文件的相对路径--> <probing privatePath="lib"/>      <!-- (可选)若有版本/公钥冲突,可添加程序集重定向 --> <!--<dependentAssembly> <assemblyIdentity name="TestService" publicKeyToken="null" culture="neutral"/> <bindingRedirect oldVersion="0.0.0.0-9.9.9.9" newVersion="1.0.0.0"/> </dependentAssembly>-->        </assemblyBinding>  </runtime> </configuration>

 Demo  链接

https://download.csdn.net/download/lingxiao16888/92544537?spm=1001.2014.3001.5501

版权声明:倡导尊重与保护知识产权。未经许可,任何人不得复制、转载、或以其他方式使用本站《原创》内容,违者将追究其法律责任。本站文章内容,部分图片来源于网络,如有侵权,请联系我们修改或者删除处理。

编辑推荐

热门文章