4

I am trying to register multiple implementations of the same interface...and then...use the Setter to set the property on my Application instance. I have tried multiple online examples and always get the SAME instance plugged-into the 2 Application properties.

  • NOTE: I have tried numerous online examples, below is just the latest version

For example...
When I look at the application object in the Quick Watch, I get the following

enter image description here

MY CONFIGURATION:
Obviously, I have parsed-out all the other objects...

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");
}

APPLICATION Example:
Obviously, I have parsed-out all the other objects...

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; }

}

----------------------------------------
UPDATES: From Questions Posed to Me
In order to bring things down to an EXTREMELY BASIC level...I decided to implement a MINIMAL set of 2 classes to try suggestions-on:

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>();
}

One of 2 outcomes always happens

  1. The property is NULL
  2. I get the following error message

"No default Instance is registered and cannot be automatically determined for type 'IInstanceProvider' No default instance is specified."

Q: Where are the target implementations located?

XXX.MeasurementContracts.Business
Contains the "ContainerRegistry" and all classes, interfaces etc.

XXX.MeasurementContracts.Web
Contains "StructuremapMvcConfig", the "IoC" initializer and its' own "DefaultRegistry"

MeasurementContracts.UnitTests
Adds the Business "ContainerRegistry" inside its "IoC" initializer...and then adds its own "ContainerRegistry" afterwards.

ATTEMPT: trying Named Registrations I added the following to the "ContainerRegistry", and while, BOTH are populated...they are of type "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>();

HOW DO I RESOLVE "Foo"? I have also tried ".Setter(x => x.Foo).Is(c => c.GetInstance("Foo"));"

ANALYSIS: Using container.WhatDoIHave() Okay, using "WhatDoIHave" displays that I have correctly configured the "ITesting" instances.

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

HOW DO I RESOLVE "Foo" & "Bar" into their respective Properties?

Prisoner ZERO
  • 13,848
  • 21
  • 92
  • 137

1 Answers1

1

Remove the explicit setter attributes for the DistributionListProvider and FirstDeliveryNoticeDocumentRecallProvider properties.

Explicit Setter Injection with [SetterProperty] Attributes

Without the [SetterProperty] attributes decorating the setters, StructureMap would ignore the DistributionListProvider and FirstDeliveryNoticeDocumentRecallProvider properties when it builds up a MeasurementContractsApplication object. With the attributes, StructureMap will try to build and attach values for the two properties as part of object construction.

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

   
    public IInstanceProvider DistributionListProvider { get; set; }

   
    public IInstanceProvider FirstDeliveryNoticeDocumentRecallProvider { get; set; }
}

And since it is unable to distinguish which interface to use for which property it applies the first registered interface on to both properties.

Which is why, in this case, you should just apply the setters inline

Inline Setter Configuration

Any setter property not configured with [SetterProperty] or the setter policies in the next section can still be filled by StructureMap if an inline dependency is configured matching that setter property as shown in the example below:

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

The following Minimal Complete and Verifiable example demonstrates the above and passes when tested.

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

Community
  • 1
  • 1
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • @PrisonerZERO Nah I'm sure it probably something simple we are missing in your example. I literally copied your updated example into a test, fixed minor syntax and ran. Passed. – Nkosi Jun 25 '18 at 16:51
  • @PrisonerZERO sigh. I removed `scan.AddAllTypesOf(typeof(ITesting));` and instead used `For().Use().Named("Bar"); For().Add().Named("Foo");` and it worked. So there is definitely something up with your environment. – Nkosi Jun 25 '18 at 17:13
  • @PrisonerZERO that new one looks very similar to this problem. Did my suggestions for this one eventually work? – Nkosi Jun 27 '18 at 13:36
  • Where you were right was, my environment was wrong. Once I went the new direction...the SETTER/IS syntax above worked...which is why I awarded the points. – Prisoner ZERO Jun 27 '18 at 14:09
  • Hey dood...could you possibly look at this? https://stackoverflow.com/questions/52996330/single-app-consuming-multiple-ioc-registries-and-has-conflicts – Prisoner ZERO Oct 26 '18 at 18:12