Prism IOC的依赖注入

IOC的依赖注入
一、构造函数方式的依赖注入
默认情况下Prism框架的项目,在打开窗口时会自动匹配主窗口的视图模型类(PrismApplication启动),这里是MainWindowViewModel,匹配到之后Container自动创建MainWindowViewModel的实例,创建时会先检查MainWindowViewModel有没有无参构造函数,如果没有则会检查构造函数的参数类型,并在Container内部列表中寻找对应类型并创建参数对象,然后使用该参数对象来创建MainWindowViewModel。
设置好PrismApplication启动方式后,做以下处理实现IOC的构造函数依赖注入:

创建业务接口及实现类

public interface IDataAccessBase
{
    DataTable GetDatas(string condition);
}

public class DataAccess : IDataAccessBase
{
    public DataTable GetDatas(string condition)
    {
        //做数据访问业务
        return null;
    }
}
public class MainWindowViewModel
{
    public MainWindowViewModel(IDataAccessBase dataAccess)
    {
        var data = dataAccess.GetDatas("模拟一下");
    }
}

类型注册
RegisterTypes函数中注册依赖类型

public partial class App : PrismApplication
{
    ......

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.Register<IDataAccessBase, DataAccess>();
    }
}
    ```
    

## 二、特性方式的依赖注入

注意,特性方式的注入时机要比构造函数方式的注入时机晚,因此如果需要在对象构造时使用依赖对象的话,最好用构造函数依赖注入的方式。

 **类型注册**
在`RegisterTypes`函数中注册依赖类型
```csharp
public partial class App : PrismApplication
{
    ......

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.Register<IDataAccessBase, DataAccess>();
    }
}
特性标注
使用Prism.Unity命名空间下的Dependency标注属性成员,Prism框架会从IOC容器中寻找对应的类型并注入到属性中。如果在IOC容器中找不到,则会报错。
public class MainWindowViewModel
{
    [Dependency]
    public IDataAccessBase DataAccess{ get; set; }
}
三、注册&注入已经存在的实例对象
有些情况下希望直接将一个已经存在的实例对象注册到IOC容器中;或者某个接口只返回接口类型的对象时,无法通过接口+实现类的方式注册到IOC容器中,
只能先创建该实例对象,然后将实例对象注册到IOC容器中(例如Log4net)。这个时候可以使用IContainerRegistry对象的RegisterInstance方法。
1、注册
public partial class App : PrismApplication
{
    ......

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterInstance(LogManager.GetLogger("Test"));
                //可以指定名称
        //containerRegistry.RegisterInstance(LogManager.GetLogger("Test"), TestLog);
    }
}

2、注入

注入方式,跟上文的一样,可以通过特性或者构造参数。
需要注意的是,如果指定了名称,就只能通过特性指定名称注入或者使用IUnityContainerIContainerExtension对象实例来获取了。

public MainWindow(ILog log)
{
    InitializeComponent();
    log.Error("错误", new Exception("发生了一个异常"));//错误
    log.Fatal("严重错误", new Exception("发生了一个致命错误"));//严重错误
    log.Info("信息"); //记录一般信息
    log.Debug("调试信息");//记录调试信息
    log.Warn("警告");//记录警告信息
}

IUnityContainer
一、获取注册信息
IUnityContainer接口的实例中存放了IOC容器中的注册信息,其中包括Prism框架默认注册的以及我们自己注册的。
以下为常用的三种注册方式后,IUnityContainer实例中存放注册信息的不同状态。

注册接口与类型,没有设置名称

public partial class App : PrismApplication
{
    ......
    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
         containerRegistry.Register<ITestClass, TestClass>();
    }
}

不同的注册方式会导致注册信息的状态有所不同,其中导航、视图、类型的注册根据不同情况也有所不同,在实际开发过程中可以先监控一下IUnityContainer实例中的注册信息再根据实际情况来使用。

需要注意的是,如果在注册过程中设置了name属性,那么在向容器中获取对象时,也要使用对应的name来获取,如果还使用类名来获取,会直接报错。

二、从容器中获取对象
除了能在IUnityContainer实例中获取到IOC容器的注册信息外,还能通过实例方法Resolve<T>()从容器中获取指定的依赖对象。
此外,从IContainerProvider实例的Resolve<T>()方法中也可以从容器中获取指定的依赖对象。

public partial class App : PrismApplication
{
    ......
    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterSingleton<ITestClass, TestClass>();
    }
}
public class MainWindowViewModel
{
    public MainWindowViewModel(IUnityContainer unityContainer,  IContainerProvider containerProvider)
    {
        var a = unityContainer.Resolve<ITestClass>();
        var b = containerProvider.Resolve<ITestClass>();
        var result = a == b;    //true
    }
}

三、实例-Region中删除指定视图

public class FileUploadViewModel:BindableBase
{
    //页签关闭命令
        public ICommand CloseCommand { get; set; }

    public FileUploadViewModel(IRegionManager regionManager, IUnityContainer unityContainer)
    {
         CloseCommand = new DelegateCommand<string>(uri =>
         {
                        //从IUnityContainer 实例中获取指定的注册信息对象
            var obj = unityContainer.Registrations.Where(r => r.Name == uri || r.MappedToType == Type.GetType("FileUploadView")).FirstOrDefault();
            var name = obj?.MappedToType.Name;
            if (!string.IsNullOrEmpty(name))
            {
                var region = regionManager.Regions["MainViewRegion"];
                var view = region.Views.Where(v=> v.GetType().Name == name).FirstOrDefault();
                if (view != null)
                    region.Remove(view);
            }
         });
        }
}

IContainerExtension
Prism的IOC容器中默认注册的还有IContainerExtension接口,该接口实现了IContainerProvider和IContainerRegistry接口,因此其实现类的实例对象同时具有注册和从容器中获取对象的功能,如果希望在App类中的RegisterTypes方法之外的地方向容器注册的话,可以通过IContainerExtension的实例对象来完成。

public class MainWindowViewModel
{
    public MainWindowViewModel(IUnityContainer unityContainer,  IContainerExtension containerProvider)
    {
        containerProvider.Register<TestClass>();
        var test = containerProvider.Resolve<TestClass>();
    }
}
来源:https://blog.csdn.net/jjailsa/article/details/141994503
扩展:

通过配置文件app.config注入类型到容器
通过配置文件注册,需要引用Unity.Configuration

具体通过配置文件注入,请参考Unit.Configuration里的测试用例:

https://github.com/unitycontainer/configuration/tree/master/tests/ConfigFiles

我们在app.config中配置如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <container>
      <register type="SimplePrismAppTest.Model.IPerson,SimplePrismAppTest"
                mapTo="SimplePrismAppTest.Model.Man,SimplePrismAppTest" name="A"></register>
      <register type="SimplePrismAppTest.Model.IPerson,SimplePrismAppTest"
               mapTo="SimplePrismAppTest.Model.Woman,SimplePrismAppTest" name="B"></register>
      <register type="SimplePrismAppTest.Model.Animal,SimplePrismAppTest"
                mapTo="SimplePrismAppTest.Model.Animal,SimplePrismAppTest">
        <constructor>
          <param name="owner" >
            <dependency name="A" />
          </param>
        </constructor>
      </register>
    </container>
  </unity>
</configuration>

在RegisterTypes的代码如下:

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    UnityConfigurationSection section (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
    section.Configure(containerRegistry.GetContainer());
}

取值的时候:

public MainWindow(IContainerExtension container)
{
    InitializeComponent();
    this._container = container;

    IPerson person1 = container.Resolve<IPerson>("A");//man
    IPerson person2 = container.Resolve<IPerson>("B");//woman
    Animal animal = container.Resolve<Animal>();
    bool result = animal.BelongPerson.Sex == person1.Sex;//true,animal的BelongPerson注入的是A
}

4.通过其他配置文件注入类型到容器
当通过app.config注入类型到容器时,我们通过ConfigurationManager来获取配置文件内容

当是其他配置文件的时候,我们通过如下方式去获取:

假定我们在程序目录下有一个otherConfig.config文件,获取代码如下:

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "otherConfig.config");
    //加载配置文件
    Configuration config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap() { ExeConfigFilename=configPath}, ConfigurationUserLevel.None);
    UnityConfigurationSection section=(UnityConfigurationSection)config.GetSection("unity");
    section.Configure(containerRegistry.GetContainer());
}

 


posted @ 2024-09-29 17:11  【君莫笑】  阅读(155)  评论(0编辑  收藏  举报