Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Structuremap, constructor that takes a list of plugins

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?

like image 371
AndreasN Avatar asked Sep 17 '09 10:09

AndreasN


3 Answers

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>());
});
like image 119
Evil Andy Avatar answered Nov 17 '22 10:11

Evil Andy


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.

like image 10
Joshua Flanagan Avatar answered Nov 17 '22 10:11

Joshua Flanagan


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.

like image 2
KevM Avatar answered Nov 17 '22 08:11

KevM