1

I have a situation where I want custom code to be able to override existing registrations. For example I have an interface,

public interface IInterface{ int Num {get;set;}

That by default resolves to,

public class CoreClass: IInterface{...}

(NB. this registration is done by SMs Scan() functionality (WithDefaultConventions, SingleImplementationsOfInterface)

Now I want a piece of custom code to join SMs Initialize process and override the existing IInterface registration with its own implementation,

public class CustomClass: IInterface {...}

Here's the issue. If I do this registration by also doing a Scan() on my custom assembly and using same SM conventions as above (WithDefaultConventions, SingleImplementationsOfInterface) then at runtime when I do a GetInstance I get the exception,

No Default Instance defined for PluginFamily IInterface

But when I add the overriding registration by using,

x.For<IInterface>().Use<CustomClass>()

It all works.

Also, when I interrogate the container using,

ObjectFactory.WhatDoIHave()

Then I get slightly different results depending on the method of registration. WhatDoIHave() basically shows you for a PLUGINTYPE which things are registered. The Name column shows the concrete types registered. When I Scan() and there are multiple implementations then I see the .Net type name of the concrete types found. But when I use the For<>() method I see the multiple implementations as above but also a GUID next to the concrete type used in the For<>() statement (I'm assuming this is indicating the default registration?)

Now a lot of SO posts indicate that last registration wins (which is the behaviour I want) but what I want to know is if that only applies to explicit registrations (i.e. using For<>). When I Scan() can I assume that SM is acting in a more "non-deterministic" way and not guaranteeing the last found implementation wins or am I missing something?

Thanks & sorry for the lengthy post!

Edit: [Sample registration code as requested]

            ObjectFactory.Initialize(x =>
            {
                x.AddRegistry<CoreRegistry>();
                x.AddRegistry<ClientRegistry>();
            });


    public class CoreRegistry : Registry
{
    public CoreRegistry()
    {
        //Presentation
        Scan(x =>
        {
            x.AssemblyContainingType<SomeCoreType>();
            x.WithDefaultConventions();
            x.SingleImplementationsOfInterface();
            x.AddAllTypesOf<ConfigurableEntityViewModel>();
        });

        // Domain
        Scan(x =>
        {
            x.AssemblyContainingType<AnotherCoreType>();
            x.WithDefaultConventions();
            x.SingleImplementationsOfInterface();
        });
...
}

    public class ClientRegistry : Registry
    {
        public ClientRegistry()
        {
            //Presentation
            Scan(x =>
            {
                x.AssemblyContainingType<SomeClientType>();
                x.WithDefaultConventions();
                x.SingleImplementationsOfInterface();
            });
    }
}

So I was hoping that concrete implementations (e.g. of IInterface) from ClientRegistry would be registered last in the container and would therefore be the "default" registration and in turn be returned when calling GetInstance(). But this is not the case, it throws an error as described above.

Matt Cotton
  • 732
  • 9
  • 23

1 Answers1

1

I believe the last always wins applies to WithDefaultConventions as well. As far as scanning goes, I don't know there are an guarantees that the assemblies will always be scanned in the same order. I'm not sure if it sorts the assemblies by filename first, or do some other logic, or just relies on the order the OS returns the files in the folder.

We've had a ton of issues trying to use WithDefaultConventions, to the point we've banned its use at my company and require developers to create registries with explicit registrations.

Also, it appears there might be a bug with SingleInterfaceImplementations. I've not used that method myself, so I'm not sure if it should wire your IInterface to CustomClass. I know WithDefaultConventions won't do that.

A final note; you don't show your scanning code. If you're using Initialize multiple times, it will clear any previous registrations from StructureMap. We have some issues where some of our registries were trying to scan and add specific interfaces, but because they were doing a Initialize it cleared the applications initialize. We now only allow Configure from within a Registry.

Andy
  • 8,432
  • 6
  • 38
  • 76
  • Thanks for reply. No we're using Initialize() only once. We're using a number of classes that implement Registry and do their relevant Scan() adding their types to the container. What I'm finding is that it doesn't matter how many concrete types are registered for an interface it always throws the exception mentioned when you call GetInstance<> unless you explicitly register a type with For<>(). – Matt Cotton Sep 05 '13 at 13:01
  • @macon I've only seen that when using WithDefaultConventions if it couldn't match IFoo to Foo (because you have Bar : IFoo). Do you think you could post code such as your Initialize and an example registry? – Andy Sep 06 '13 at 19:44