为同一接口的多个实现设置属性

时间:2018-06-11 16:43:35

标签: c# dependency-injection structuremap

我正在尝试注册同一个接口的多个实现......然后......使用Setter在我的Application实例上设置属性。我已经尝试了多个在线示例,并始终将SAME实例插入到2个应用程序属性中。

  • 注意:我尝试了很多在线示例,下面只是最新版本

例如......
当我在Quick Watch中查看应用程序对象时,我得到以下内容

enter image description here

MY CONFIGURATION:
显然,我已经解析了所有其他对象......

public ContainerRegistry()
{
    Scan(
        scan =>
        {
            scan.TheCallingAssembly();
            scan.WithDefaultConventions();
            scan.LookForRegistries();
            scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("Demo.Common", true, null));
            scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("XXX.XXX.MeasurementContracts", true, null));
            scan.AddAllTypesOf(typeof(IMeasurementContractsApplication));
            scan.AddAllTypesOf(typeof(IInstanceProvider));
            scan.SingleImplementationsOfInterface();
        });

    // --------
    // NAMED INSTANCES - IInstanceProvider
    For<IInstanceProvider>().Use<DistributionListProvider>();
    For<IInstanceProvider>().Add<FirstDeliveryNoticeDocumentRecallManager>().Named("firstDeliveryNoticeDocumentRecallManager");

    // --------
    // APPLICATION
    For<IMeasurementContractsApplication>().Use<MeasurementContractsApplication>()

        // Component
        .Setter(x => x.DistributionListProvider).Is<DistributionListProvider>()
        .Setter(x => x.FirstDeliveryNoticeDocumentRecallManager).IsNamedInstance("firstDeliveryNoticeDocumentRecallManager");
}

应用示例:
显然,我已经解析了所有其他对象......

public class MeasurementContractsApplication : IMeasurementContractsApplication
{
    [SetterProperty]
    public IMeasurementContractsUnitOfWork UnitOfWork { get; set; }

    [SetterProperty]
    public IInstanceProvider DistributionListProvider { get; set; }

    [SetterProperty]
    public IInstanceProvider FirstDeliveryNoticeDocumentRecallProvider { get; set; }
}

IInstanceProvider's:

public class DistributionListProvider : ProviderBase, IInstanceProvider
{
    // Purposely left-out Properties, Methods etc.
}

public class FirstDeliveryNoticeDocumentAdminUpdateProvider : ProviderBase, IInstanceProvider
{
    // Purposely left-out Properties, Methods etc.
}

public class ProviderBase
{
    [SetterProperty]
    public IMeasurementContractsUnitOfWork UnitOfWork { get; set; }

}

----------------------------------------
更新:从提出的问题
为了把事情降到极端......我决定实现MINIMAL的2个类来尝试建议:

public interface ITesting
{
    string Name();
}

public class Foo : ITesting
{
    public string Name()
    {
        return string.Empty;
    }
}

public class Bar : ITesting
{
    public string Name()
    {
        return string.Empty;
    }
}

public ContainerRegistry()
{
    Scan(
        scan =>
        {
            scan.TheCallingAssembly();
            scan.WithDefaultConventions();
            scan.LookForRegistries();
            scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("Demo.Common", true, null));
            scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("XXX.MeasurementContracts", true, null));
            scan.AddAllTypesOf(typeof(IMeasurementContractsApplication));
            scan.AddAllTypesOf(typeof(IManager<>));
            scan.AddAllTypesOf(typeof(IDocumentDependency));
            scan.AddAllTypesOf(typeof(IDataItemProviderFor<>));
            scan.AddAllTypesOf(typeof(IDatasetBuilderFor<>));
            scan.AddAllTypesOf(typeof(IXmlTransformerFor<>));
            scan.AddAllTypesOf(typeof(IWorkflowProvider));
            scan.AddAllTypesOf(typeof(IInstanceProvider));
            scan.AddAllTypesOf(typeof(IPdfConverterClient));
            scan.AddAllTypesOf(typeof(IReportFor<>));
            scan.AddAllTypesOf(typeof(IAdminUpdateCommandFor<>));
            scan.AddAllTypesOf(typeof(ITesting));
            scan.SingleImplementationsOfInterface();
        });

         Component Providers
        For<IMeasurementContractsApplication>().Use<MeasurementContractsApplication>()
            Setter(x => x.Bar).Is<Bar>()
            Setter(x => x.Foo).Is<Foo>();
}

总是会发生两种结果之一

  1. 属性为NULL
  2. 我收到以下错误消息
  3.   

    “未注册默认实例,无法自动生成   确定类型'IInstanceProvider'没有默认实例   指定“。

    问:目标实施位于何处?

    XXX.MeasurementContracts.Business
    包含“ContainerRegistry”以及所有类,接口等。

    XXX.MeasurementContracts.Web
    包含“StructuremapMvcConfig”,“I​​oC”初始化程序及其“自己的”DefaultRegistry“

    MeasurementContracts.UnitTests
    在其“IoC”初始化程序中添加Business“ContainerRegistry”...然后添加自己的“ContainerRegistry”。

    尝试:尝试命名注册 我将以下内容添加到“ContainerRegistry”中,同时,BOTH已填充...它们的类型为“Bar”

    // Component Providers
    For<ITesting>().Use<Bar>().Named("Bar");
    For<ITesting>().Add<Foo>().Named("Foo");
    
    // Component Providers
    For<IMeasurementContractsApplication>().Use<MeasurementContractsApplication>()
         .Setter(x => x.Bar).Is<Bar>()
         .Setter(x => x.Foo).Is<Foo>();
    

    我如何解决“Foo”? 我也试过“.Setter(x =&gt; x.Foo).Is(c =&gt; c.GetInstance(”Foo“));”

    分析:使用container.WhatDoIHave() 好的,使用“WhatDoIHave”显示我已正确配置“ITesting”实例。

    • Transient - XXX.MeasurementContracts.Business.Providers.Bar('bar') - bar(默认)
    • Transient - XXX.MeasurementContracts.Business.Providers.Foo('foo') - foo

    我如何解决“Foo”&amp; “吧”进入他们各自的属性?

1 个答案:

答案 0 :(得分:1)

删除DistributionListProviderFirstDeliveryNoticeDocumentRecallProvider属性的显式setter属性。

  

Explicit Setter Injection with [SetterProperty] Attributes

     

如果没有[SetterProperty]属性来装饰setter,StructureMap会在构建DistributionListProvider对象时忽略FirstDeliveryNoticeDocumentRecallProviderMeasurementContractsApplication属性。使用这些属性,StructureMap将尝试构建和附加两个属性的值,作为对象构造的一部分。

public class MeasurementContractsApplication : IMeasurementContractsApplication {
    [SetterProperty]
    public IMeasurementContractsUnitOfWork UnitOfWork { get; set; }


    public IInstanceProvider DistributionListProvider { get; set; }


    public IInstanceProvider FirstDeliveryNoticeDocumentRecallProvider { get; set; }
}

由于它无法区分哪个接口用于哪个属性,因此它将第一个注册接口应用于两个属性。

这就是为什么,在这种情况下,您应该只应用内置的setter

  

Inline Setter Configuration

     

如果内联依赖项配置为匹配该setter属性,则任何未使用[SetterProperty]配置的setter属性或下一节中的setter策略仍可由StructureMap填充,如下例所示:

For<IMeasurementContractsApplication>()
    .Use<MeasurementContractsApplication>()
    // Component
    .Setter(x => x.DistributionListProvider)
        .Is<DistributionListProvider>()
    .Setter(x => x.FirstDeliveryNoticeDocumentRecallManager)
        .Is<FirstDeliveryNoticeDocumentAdminUpdateProvider>();

以下Minimal Complete和Verifiable示例演示了上述内容并在测试时通过。

namespace StructureMap.Unit_Tests.Misc {
    [TestClass]
    public class StructureMapTests {
        [TestMethod]
        public void _Inline_Setter_Should_Populate_Multiple_Implementations() {
            //Arrange
            var registry = new StructureMap.Registry();
            registry.IncludeRegistry<ContainerRegistry>();
            // build a container
            var container = new StructureMap.Container(registry);

            //Act
            var application = container.GetInstance<IMeasurementContractsApplication>();

            //Assert
            application.Should().NotBeNull();
            application.Foo.Should().BeOfType<Foo>();
            application.Bar.Should().BeOfType<Bar>();
        }
    }

    class ContainerRegistry : StructureMap.Registry {
        public ContainerRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
                    scan.LookForRegistries();
                    scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("Demo.Common", true, null));
                    scan.AssembliesFromApplicationBaseDirectory(f => f.FullName.StartsWith("XXX.MeasurementContracts", true, null));
                    scan.AddAllTypesOf(typeof(IMeasurementContractsApplication));                    
                    scan.AddAllTypesOf(typeof(ITesting));
                    scan.SingleImplementationsOfInterface();
                });

            //Component Providers
            For<IMeasurementContractsApplication>()
                .Use<MeasurementContractsApplication>()
                .Setter(x => x.Bar)
                    .Is<Bar>()
                .Setter(x => x.Foo)
                    .Is<Foo>();
        }
    }

    public interface IMeasurementContractsApplication {
        ITesting Foo { get; set; }
        ITesting Bar { get; set; }
    }

    public class MeasurementContractsApplication : IMeasurementContractsApplication {
        public ITesting Foo { get; set; }
        public ITesting Bar { get; set; }
    }

    public interface ITesting {
        string Name();
    }

    public class Foo : ITesting {
        public string Name() {
            return string.Empty;
        }
    }

    public class Bar : ITesting {
        public string Name() {
            return string.Empty;
        }
    }
}

Quick watch