简体   繁体   中英

StructureMap Open Generic Types

I have the following classes:

public interface IRenderer<T> 
{
    string Render(T obj);
}

public class Generic<T> { }

public class SampleGenericRenderer<T> : IRenderer<Generic<T>>
{
    public string Render(Generic<T> obj)
    {
        throw new NotImplementedException();
    }
}

I would like to be able to call StructureMap with ObjectFactory.GetInstance<IRenderer<Generic<string>>>(); and receive SampleGenericRenderer<string> .

I'm currently using the following registration and receiving this error when I call GetInstance. "Unable to cast object of type:

ConsoleApplication1.SampleGenericRenderer'1[ConsoleApplication1.Generic'1[System.String]]' to type 'ConsoleApplication1.IRenderer'1[ConsoleApplication1.Generic'1[System.String]].

public class CoreRegistry : Registry
{
    public CoreRegistry()
    {
        Scan(assemblyScanner =>
        {
            assemblyScanner
                .AssemblyContainingType(typeof(IRenderer<>));
            assemblyScanner.AddAllTypesOf(typeof(IRenderer<>));
            assemblyScanner
                .ConnectImplementationsToTypesClosing(
                    typeof(IRenderer<>));
        });
    }
}

Is there any way to configure StructureMap so that it creates SampleGenericRenderer<string> instead of SampleGenericRenderer<Generic<string>> ?

UPDATE: I ended up doing the type construction myself for this subset of dependencies. Because they combine contextual binding with a lot of unsupported generic bindings, this turned out to be the cleanest solution.

As Pete explained, there is probably no way with StructureMap to do this. Other DI containers might yield more success, but not all contains have great support for more complex generic trickery. The only one I know for sure that allows you to do this is the Simple Injector . With the Simple Injector, you just need the following configuration:

var container = new Container();

container.Register(typeof(IRenderer<>), typeof(SampleGenericRenderer<>));

// Usage
var renderer = container.GetInstance<IRenderer<Generic<string>>>();

renderer.Render(new Generic<string>());

More info about this method can be found here .

For me this works:

class Program
{
    static void Main()
    {
        ObjectFactory.Configure(x=>x.AddRegistry<CoreRegistry>());

        var instance = ObjectFactory.GetInstance(typeof(IRenderer<string>)) as IRenderer<Generic<string>>;

        var render = instance.Render(new Generic<string>());

    }
}

This throws an exception:

ObjectFactory.GetInstance<IRenderer<Generic<string>>>();

What is your real problem?

EDIT:

Under some circumstances this could work too (If you don't know Generic<string> at design time):

static void Main()
{
    ObjectFactory.Configure(x => x.AddRegistry<CoreRegistry>());

    var instance = ObjectFactory.GetInstance(typeof(IRenderer<string>));
    var methodInfo = instance.GetType().GetMethod("Render");
    methodInfo.Invoke(instance, new[] { new Generic<string>() });
}

I don't believe StructureMap has any mechanism for closing an inner generic parameter like this.

It's not ideal, but you might try creating your own IRegistrationConvention and whenever encountering a type that is a closed type of Generic<> , do the following:

var generic_t = typeof(Generic<>).MakeGenericType(type);
var sample_renderer_t = typeof(SampleGenericRenderer<>).MakeGenericType(type);
var renderer_t = typeof(IRenderer<>).MakeGenericType(generic_t);
graph.AddType(renderer_t, sample_renderer_t);

See http://docs.structuremap.net/ScanningAssemblies.htm#section11 for more details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM