3

I have the following StructureMap registrations that work in version 2.6.4 and I'm finally upgrading to the latest SM (3.1.2 as of this writing). And need to update it since there doesn't appear to be a IContext.BuildStack anymore.

Here is the old working version with 2.6.4:

        initialization.For(typeof(IRepository<,>))
                             .Use(context =>
                             {
                                 var genericArgs = context.BuildStack.Current.RequestedType.GetGenericArguments();

                                 return RepositoryFactory.GetInstance(genericArgs[0], genericArgs[1], repositoryName);
                             }
            );

So I figured that changing it to this would work:

        initialization.For(typeof (IRepository<,>))
            .Use("IRepository<,>", context =>
                                   {
                                       var genericArgs = context.ParentType.GetGenericArguments();

                                       return RepositoryFactory.GetInstance(genericArgs[0], genericArgs[1],
                                           repositoryName);
                                   }
            );

But context.ParentType is null. When I look at context.RootType it is set to System.Object which is obviously not what I want.

My test code to get an instance of this repository is:

var userRepository = ObjectFactory.GetInstance<IRepository<User, Guid>>();

I don't see any other property that has this information, but I'm guessing I am missing something.

Jeff Treuting
  • 13,910
  • 8
  • 36
  • 47

2 Answers2

6

You are not missing something. On GitHub someone posted a simular question: https://github.com/structuremap/structuremap/issues/288. Jeremy Miller, the author of structuremap, responded:

It's going to have to be new development. It'll have to come in a 3.2 version.

The suggested workaround is to create a custom instance and override the ClosingType method. You can do this as follows:

public class CustomInstance : Instance
{
    public override IDependencySource ToDependencySource(Type pluginType)
    {
        throw new NotImplementedException();
    }

    public override Instance CloseType(Type[] types)
    {
        var repository = RepositoryFactory.GetInstance(types[0], types[1], repositoryName);                                                           
        return new ObjectInstance(repository);
    }

    public override string Description
    {
        get { throw new NotImplementedException(); }
    }

    public override Type ReturnedType
    {
        get { return typeof (IRepository<,>); }
    }
}

Now, you only have to connect the open generic type to the closing type like this:

initialization.For(typeof (IRepository<,>)).Use(new CustomInstance());
Robin van der Knaap
  • 4,060
  • 2
  • 33
  • 48
  • Thanks for pointing that out. How come that workaround won't work? Seems like if I create a custom Instance and implement the CloseType override then I would have access to the generic types there and can call my RepositoryFactory with the proper types. I'm not sure what to return from the CloseType though since it's expecting an Instance and not an object. – Jeff Treuting Oct 06 '14 at 19:24
  • You are right. I took a better look and the workaround does work. I've updated the answer. – Robin van der Knaap Oct 06 '14 at 21:05
  • That's awesome. I will give that a shot tonight and make sure it works. I wasn't aware of the new ObjectInstance() syntax and was stuck on that. Thanks! – Jeff Treuting Oct 07 '14 at 01:47
  • 1
    Now this way is documented [here](http://structuremap.github.io/generics/) (see Generic Instance Builder) – Kirill Mar 24 '16 at 16:02
4

I added a new example to the StructureMap 3 codebase for this scenario based on your question:

https://github.com/structuremap/structuremap/blob/master/src/StructureMap.Testing/Acceptance/builder_for_open_generic_type.cs

I have to ask though, what's the purpose of something like RepositoryBuilder if you're using an IoC container?

Anyway, this implementation should be more efficient than the older Reflection-heavy approach.

  • It's for SharpRepository which is a generic repository that can be configured via config file to swap out the backend as well as caching settings. So I want to inject IRepository and have it resolved using the RepositoryFactory since that uses the configuration data to determine what actual type to implement. Therefore the user can update the config file to change things instead of updating the code. It might resolve to EfRepository or MongoDbRepository or InMemory with Redis caching or in-memory caching, etc. – Jeff Treuting Oct 07 '14 at 16:30