0

我有一个使用统一固件来解析对象的应用程序。我对框架和类进行了一些更改,这些更改可以在代码注释中看到为“NEW CHANGE”

包装类看起来像

public static class ContractResolver
{
        public static T Resolve<T>() //This is been used in many places in application
        {
            IUnityContainer container = new UnityContainer();
            var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            section.Containers.Default.Configure(container);
            return container.Resolve<T>();
        }

        //NEW CHANGE: This is the new function that suppose to return the instance of parameterised constructor
        public static T Resolve<T>(ParameterOverride[] parameterOverrides)
        {
            IUnityContainer container = new UnityContainer();
            var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            section.Containers.Default.Configure(container);
            return container.Resolve<T>(parameterOverrides);
        }
}

配置看起来像

  <unity>
    <containers>
      <container>
        <types>
          <type type ="UnityTest.IImageRepositoryService, UnityTest" mapTo="UnityTest.ImageRepositoryService, UnityTest"/>
        </types>
      </container>
    </containers>
  </unity>

类和接口看起来像

public interface IImageRepositoryService
{
    bool Exists(string imageName);
}

public class ImageRepositoryService : IImageRepositoryService
{
    private readonly string mFilterName = "StandardImageFilter";

    //[InjectionConstructor]
    public ImageRepositoryService()
    {
        DatabaseQueryProvider.Query("Image", mFilterName);
    }

    //NEW CHANGE. A CONSTRUCTOR THAT ACCEPTS A PARAMETER    
    //[InjectionConstructor]
    public ImageRepositoryService(string filterName)
    {
        mFilterName = filterName;
        DatabaseQueryProvider.Query("Image", filterName);
    }

    public bool Exists(string imageName)
    {
        Console.WriteLine("The image " + imageName + " found in filter " + mFilterName);
        return true;
    }

}

用法看起来像

var serviceDefault = ContractResolver.Resolve<IImageRepositoryService>();
serviceDefault.Exists("myimage.bmp");

新的变化打破了旧的用法。IE

var serviceDefault = ContractResolver.Resolve<IImageRepositoryService>();

引发异常 依赖项解析失败,type = "UnityTest.IImageRepositoryService",name = "(none)"。异常发生时:解决时。异常是: InvalidOperationException - 无法构造类型 String。您必须配置容器以提供此值。

我想拥有新功能的同时又不想破坏旧功能。

 var serviceDefault = ContractResolver.Resolve<IImageRepositoryService>();
 serviceDefault.Exists("myimage.bmp");

应在控制台中显示消息“过滤器 StandardImageFilter 中找到的图像 myimage.bmp”

var parameterOverride1 = new ParameterOverride("filterName", "filter1");
var servicefilter1 = ContractResolver.Resolve<IImageRepositoryService>(new[] { parameterOverride1 });
servicefilter1.Exists("myimage.bmp");

应在控制台中显示消息“过滤器 filter1 中找到的图像 myimage.bmp”

var parameterOverride2 = new ParameterOverride("filterName", "filter2");
var servicefilter2 = ContractResolver.Resolve<IImageRepositoryService>(new[] { parameterOverride2 });
servicefilter2.Exists("myimage.bmp");

应在控制台中显示消息“过滤器 filter2 中找到的图像 myimage.bmp”

如何解决这个问题?

4

1 回答 1

1

如果您想解析相同的类型(在这种情况下IImageRepositoryService)但对 Resolve 的不同调用调用不同的构造函数,那么您将需要使用命名注册。

在您的情况下,您可以在 XML 配置中执行此操作:

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
  <container>
    <register type ="UnityTest.IImageRepositoryService, UnityTest" mapTo="UnityTest.ImageRepositoryService, UnityTest">
      <constructor />
    </register>
    <register name="ParameterizedRepository" 
              type="UnityTest.IImageRepositoryService, UnityTest" 
              mapTo="UnityTest.ImageRepositoryService, UnityTest">
      <constructor>
        <param name="filterName" value="dummyValue" />
      </constructor>
    </register>
  </container>
</unity>

请注意,我使用了 Unity 2(和 3)配置样式。

所以这告诉 Unity 在使用名称“ParameterizedRepository”解析时调用带有名为“filterName”的参数的构造函数。我在这里使用了一个虚拟值,因为无论如何我们都将在运行时覆盖该值:

var imageRepositoryService = container.Resolve<IImageRepositoryService>(
    "ParameterizedRepository", 
    new ParameterOverride("filterName", "filter2"));

这就是使用 Unity 获得所需内容的方法,因此就包装类而言,您应该添加一个名称参数:

public static class ContractResolver
{
    //NEW CHANGE: This is the new function that suppose to return the instance of parameterised constructor
    public static T Resolve<T>(string name, params ParameterOverride[] parameterOverrides)
    {
        IUnityContainer container = new UnityContainer();
        var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
        section.Containers.Default.Configure(container);
        return container.Resolve<T>(name, parameterOverrides);
    }
}

一些不请自来的评论(本着试图提供帮助的精神):

  • 看起来您使用的是 Unity 版本 1。如果是这样,您可能需要考虑升级(最近发布了版本 3),如果您没有使用 Unity 版本 1,那么您可能需要考虑更改 XML 配置语法以使用较新的方法以及使用LoadConfiguration()扩展方法。

  • 我不确定为什么每次调用ContractResolver.Resolve()都会创建一个新的 Unity 容器然后加载配置。这可能会影响性能。通常,您会创建一个容器并加载一次配置,然后在应用程序的整个生命周期中使用该实例。

  • 我可以理解你会多么想将容器实现隐藏在后面,ContractResolver但是随着ParameterOverride(Unity 特定的)的添加,抽象变得有点泄漏。

于 2013-08-25T04:45:24.353 回答