Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configuring profiles with StructureMap

important; I'm really looking for a StructureMap answer here. Please don't say how to do it with Windsor, Spring, Unity, or any of the others.

I'm playing with StructureMap for IoC - and basically my aim is to have a "default" profile that defines the core types, and a number of named profiles that override/extend this. I think that profiles can do this, but I simply can't get it to work either through the xml or the code APIs. In particular, if I try to load a container for a profile:

container = new Container();
container.SetDefaultsToProfile(profile);

Then I get "Requested Profile {name} cannot be found", despite the fact that I clearly called CreateProfile in the initialize (with that name).

Am I barking up the wrong tree?

(also posted to user-group)


What I ideally want is to be able to define the standard (/default) types, and then for a range of different named configurations, override some of the settings - i.e. if I had

  • global: IFoo => Foo, IBar => Bar
  • configA: (no changes)
  • configB: IFoo => SpecialFoo

I believe this could map to 2 containers, loaded using named profiles. The purpose being that if I ask either container for an IBar, I get a Bar - but configA returns a Foo (for IFoo), where-as configB returns a SpecialFoo.

Can somebody give me a clue how I can configure this? Either xml or code is fine... I just want it to work. All I need is interface-to- concrete-type mappings (no special config/property settings).

like image 281
Marc Gravell Avatar asked Feb 19 '09 18:02

Marc Gravell


2 Answers

The trick is to make sure every single profile as at least a rule defined in it. If you don't specify a rule (configA) it won't create/see the profile.

Given these classes:

public interface IFoo { string SayHello(); }
public class Foo : IFoo { public string SayHello() { return "Hello"; } }
public class SpecialFoo : IFoo { public string SayHello() { return "Hello Special"; } }

public interface IBar { }
public class Bar : IBar { }

public interface IDummy { }
public class Dummy : IDummy{ }

You can define this registry:

public class MyRegistry : Registry
{
    protected override void configure()
    {
        ForRequestedType<IBar>().TheDefault.Is.OfConcreteType<Bar>();
        ForRequestedType<IFoo>().TheDefault.Is.OfConcreteType<Foo>();
        CreateProfileNotEmpty("configA");
        CreateProfileNotEmpty("configB")
            .For<IFoo>().UseConcreteType<SpecialFoo>();
    }
    StructureMap.Configuration.DSL.Expressions.ProfileExpression CreateProfileNotEmpty(string profile)
    {
        return CreateProfile(profile)
            .For<IDummy>().UseConcreteType<Dummy>();
    }
}

And it will work with these tests:

[TestMethod]
public void TestMethod1()
{
    var container = new Container(new MyRegistry());
    Assert.IsNotNull(container.GetInstance<IBar>());
    Assert.AreEqual("Hello", container.GetInstance<IFoo>().SayHello());

    container.SetDefaultsToProfile("configB");
    Assert.IsNotNull(container.GetInstance<IBar>());
    Assert.AreEqual("Hello Special", container.GetInstance<IFoo>().SayHello());

    container.SetDefaultsToProfile("configA");
    Assert.IsNotNull(container.GetInstance<IBar>());
    Assert.AreEqual("Hello", container.GetInstance<IFoo>().SayHello());
}

if you replace CreateProfileNotEmpty with simple CreateProfile, it will fail on the line that sets the default to configA.

like image 173
eglasius Avatar answered Nov 04 '22 03:11

eglasius


Watch this video, he shows another way of doing the "default" profile and having other named profiles that would act as the variations.

http://www.dimecasts.net/Casts/CastDetails/135

like image 1
JeremyWeir Avatar answered Nov 04 '22 03:11

JeremyWeir