IOC容器StructureMap在WPF程序中的基本使用方法

StructureMap类库主要应用于IOC方面,为了在WPF程序中充分利用依赖注入的优势,可以使用依赖注入容器StructureMap来简化开发工作。其基本的使用步骤为:

(1)下载

(2)创建BootStrapper

(3)通过继承Registry类来创建配置类 XXXRegistry

(4)在程序中应用

以下分别介绍:

(一)下载

直接利用Nuget进行下载。

(二)IOC的入口(我们用BootStrapper类来建立入口点)尽量靠近程序的启动位置,对于简单的MVVM WPF程序,通常的入口点在View层。因此我们在View层中创建类BootStrapper如下:

using IContainer = StructureMap.IContainer;
using Container = StructureMap.Container;

namespace View

{

    public class BootStrapper

    {
        public MainWindowViewModel MainWindowViewModel
        {
            get
            {
                return _container.GetInstance<MainWindowViewModel>();  //获取对象用Container.GetInstance<T>()泛型方法。对于需要输入自定义构造参数的情况,用

//Container.With("参数名称").EqualTo(参数实例).GetInstance<T>()来获取
            }
        }
        StructureMap.IContainer _container;
        public BootStrapper()
        {
            _container = Container.For<RepositoryRegistry>();            //直接用Container类的静态方法For<T>来创建容器实例,这个T需要是继承于Registry类的派生类
        }
    }
}

以上BootStrapper中有一个构造方法和一个普通方法。通过构造方法,可以建立起StructureMap容器。另一个方法是利用容器获取MainWindow的ViewModel对象的普通方法,可以根据自己的程序自由设置这种类似的getter方法。

(三)通过继承Registry类来创建注册配置类 XXXRegistry

           注册配置类主要是用于配置容器的,可以让其对程序集进行扫描,可以直接指定接口及其实现类,可以指定某个类为单例类等等。由于注册配置类需要依赖于ViewModel和Model以及其他一些服务相关的程序集,因此可将其放在ViewModel项目中(如果放在View项目中,则需要View引用Model项目,会造成View对Model的依赖)。以下是RepositoryRegistry的代码,主要利用Scan方法来扫描指定命名空间中的类,并为接口指定实现类,并采用Singleton()方法指定单实例对象。这里关键用到的是For<InterfaceT>().Singleton().User<T>()系列方法。

  using StructureMap;        

namespace ViewModel
{
    public class RepositoryRegistry : Registry
    {
        public RepositoryRegistry()
        {
            Scan(scan =>
            {
                scan.IncludeNamespace("ViewModel");                              //对命名空间ViewModel中进行扫描注册
                scan.IncludeNamespace("Application");      //对命名空间Application中进行扫描注册
                scan.IncludeNamespace("Model");
                scan.WithDefaultConventions();
            });
            For<IToolManager>().Singleton().Use<ToolManager>();                          //为接口IToolManager指定实现类ToolManager,并指定其为单实例对象
            For<IUIDataProvider>().Singleton().Use<UIDataProvider>();           
            For<IOrdersViewModelFactory>().Singleton().Use<OrdersViewModelFactory>();
            For<ICustomerDetailsViewModelFactory>().Singleton().Use<CustomerDetailsViewModelFactory>();
            For<IOrderViewModelFactory>().Singleton().Use<OrderViewModelFactory>();
            For<IOrderDetailsViewModelFactory>().Singleton().Use<OrderDetailsViewModelFactory>();
        }
    }
}

(四)应用

以上就是一个简单的StructureMap的配置,下面就可以进行实际应用。分为两步,第一,在Application.xaml中将BootStrapper定义为资源。第二,在MainWindow.xaml中利用BootStrapper来获取MainWindow对象实例并将其设置为DataContext。

<Application x:Class="NorthWind.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
    <Application.Resources>
        <ResourceDictionary>
            <ObjectDataProvider x:Key="BootStrapper" ObjectType="local:BootStrapper" />   //在Application.xaml中将BootStrapper定义为资源
        </ResourceDictionary>
  </Application.Resources>
</Application>

//以下是MainWindow.Xaml文件

<Window x:Class=" MainWindow"

       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

       xmlns:ViewModel="clr-namespace:ViewModel;assembly=ViewModel"

       DataContext="{Binding Path=MainWindowViewModel,Source={StaticResource BootStrapper}}">

<Grid>

</Grid>

</Window>

到目前为止,StructureMap已经可以成功地提供MainWindowViewModel实例对象。当然,仅仅为了获取MainWindowViewModel对象来使用StructureMap肯定是得不偿失的,但这仅仅是简单例子,来介绍以下StructureMap的最基本使用方式。实际上,本例子中写的ICustomerDetailsViewModelFactory、IOrderViewModelFactory、IOrderDetailsViewModelFactory都是工厂类,在工厂类的构造函数中可以写入IContainer参数,这样每一个工厂类实例对象都包含了容器对象的引用,在创建对象时就可以直接利用容器来获取对象了(IContainer.GetInstance<T>())。


工厂类构造函数如下:

        public CustomerDetailsViewModelFactory(IContainer container)
        {
            _container = container;
        }

利用工厂类实例化对象(实际上最终还是委托容器来创建对象的):

        public CustomerDetailsViewModel CreateInstance(string customerID)
        {
            return _container.With("customerID").EqualTo(customerID).GetInstance<CustomerDetailsViewModel>();   

//请求容器创建对象,并将创建对象所需要的customerID

//参数传给对象。这里最神奇的就是,在创建CustomerDetailsViewModel时依赖的其它服务或工具类(如xxxService,XXXManager),

//如果已经在容器中注册了,则此处不用为期注入参数,而是由容器(_container)自动注入
        }

距离实例类的构造函数原型如下:

       public CustomerDetailsViewModel(IUIDataProvider dataProvider, 
                                        string customerID, IOrdersViewModelFactory ordersViewModelFactory,
                                        IToolManager toolManager) : base(toolManager)
        {
        .........
        }

需要指出的是,该构造函数依赖多个参数,包括dataProvider,ordersViewModelFactory,toolManager和customerID,其中前三个都是服务的类对象,只有最后一个CustomerID是数据对象。在上面工厂类中,创建实例对象时使用_container.With("customerID").EqualTo(customerID).GetInstance<CustomerDetailsViewModel>()语法,它将创建对象所需要的数据对象customerID直接指出来,但没有告知其它几个参数对象。这里就是神奇的DI容器发挥作用的时候,由于其它几个类在RepositoryRegistry类中已经注册了(如For<IToolManager>().Singleton().Use<ToolManager>()的几个语句   ),因此容器就直接从容器内部取出这些对象来作为构造参数,完成对象的实例化工作。

当然还有几个问题需要考虑:

(1)如果把BootStrapper中的IContainer作为全局静态字段,是否可以不用再各个工厂类的构造函数中注入IContainer,直接使用全局静态对象来实例化具体类呢?

(2)由Container创建的Singleton好像都是由容器管理的,而非Singleton似乎是直接交给应用程序,而容器不会继续管理这些非Singleton对象的?如果对于需要作为Singleton对待的工具类,注册时没有指明其为单实例,则容器每次使用到的时候都会重新创建一个,会造成程序的异常。

猜你喜欢

转载自blog.csdn.net/jiuzaizuotian2014/article/details/78946728