views:

356

answers:

4

I got an interface like this

public interface IWriter
{
  ...
}

and a class

public class WriterMerger
{
  public WriterMerger(IEnumerable<IWriter> writers)
  ...
}

I want structuremap to fill out the constructor argument on WriterMerger with all registered IWriter's. I registered the different writers with

StructureMap.ObjectFactory.Initialize(x =>
{
  x.ForRequestedType<IWriter>().AddConcreteType<MemoryWriter>();
  x.ForRequestedType<IWriter>().AddConcreteType<FlatFileWriter>();
  x.ForRequestedType<IWriter>().AddConcreteType<DbWriter>();
}

however calling

ObjectFactory.CreateInstance<WriterMerger>();

Returns an exception "No Default Instance defined for PluginFamily System.Collections.Generic.IEnumerable`1[[IWriter..]]" (class name edited)

Any trick for doing this automatically? or will i have to make a custom factory method?

+1  A: 

I think what this is telling you is that you need to define one of these IWriters as the default. Imagine a different class had the following contructor:

public class Test{
  public Test(IWriter writer){}
}

Without a default IWriter it would not know which writer to assign. So you probably need to say which is the default even if you never user it. See this post:

http://stackoverflow.com/questions/144272/whats-the-difference-between-addconcretetype-and-thedefaultisconcretetype-in-str

Michael Edwards
+4  A: 

When you want all concrete instances of a type to be injected, you MUST declare the parameter as an array. You can fix your problem by changing your constructor to:

public WriterMerger(IWriter[] writers)

Be aware that code cleanup tools like ReSharper may highlight the array in your constructor and suggest you replace it with IEnumerable, if that is all that is required by your WriterMerger. However, these tools are not aware of StructureMap's requirement to use an array, so you have to ignore it.

You may also be interested in knowing that you can automatically register all of your IWriters, instead of explicitly listing them. Just change your container configuration to:

StructureMap.ObjectFactory.Initialize(x =>
{
    x.Scan(scan =>
    {
        scan.TheCallingAssembly();
        scan.AddAllTypesOf<IWriter>();
    });
});

There are a number of different options for scanning that allow you to scan different assemblies, exclude certain types, etc.

Joshua Flanagan
+1  A: 

I duplicated your problem and I believe that your code will work if you change your IWriter consuming class to have a ctor that takes an array of IWriter.

public class WriterMerger {  public WriterMerger(IWriter[] writers) }

Josh is right on in recommending a better way to register all types of IWriter with StructureMap. Scanners FTW.

KevM
Thanks, that solved my problem
AndreasN
+2  A: 

Actually this can be done without changing your constructor

Change your container configuration to:

StructureMap.ObjectFactory.Initialize(x =>
{
    x.Scan(scan =>
    {
        scan.TheCallingAssembly();
        scan.AddAllTypesOf<IWriter>();
    });
    x.ForRequestedType<IEnumerable<IWriter>>()
        .TheDefault.Is.ConstructedBy(x => ObjectFactory.GetAllInstances<IWriter>());
});
Evil Andy