views:

49

answers:

2

I have a typical repository interface, IRepository<T>, and lots of concrete repositories. Most of the concrete repositories look like this:

class ConcreteRepository<T> : IRepository<T> { .. }

These are easy to register with StructureMap:

For(typeof(IRepository<>)).Use(typeof(ConcreteRepository<>));

However, a select few of my concrete repositories look like this:

class AbnormalRepository<T1, T2> : IRepository<T1>, IAbnormal<T2> { .. }

I still want to use these abnormal repositories as IRepository<T>s, so for these I'm currently using special rules:

// this sucks!
For(typeof(IRepository<Foo1>)).Use(typeof(AbnormalRepository<Foo1, Bar1>));
For(typeof(IRepository<Foo2>)).Use(typeof(AbnormalRepository<Foo2, Bar2>));

It would be nice if I could just specify a function that StructureMap could use to construct my repositories, since I know that all of my abnormal repositories implement IAbnormal<T>. Any ideas?

+1  A: 

I'm not following your use case all the way, but you can use a function (lambda actually) to construct your object. Use either of the two overloads:

// lambda with no params
For<IRepository<Foo1>>().Use(() => { ... });
// context is a StructureMap SessionContext
For<IRepository<Foo1>>().Use(context => { ... }); 

To see what is available off SessionContext, check out http://structuremap.github.com/structuremap/UsingSessionContext.htm

ADDED:

using System;
using NUnit.Framework;
using StructureMap;

namespace SMTest2
{
    public interface IRepository<T> {}
    public class AbnormalRepository<T,T2> : IRepository<T>{ }

    [TestFixture]
    public class TestOpenGeneric
    {
        private IContainer _container   ;

        [SetUp]
        public void DescribeContainer()
        {
            _container = new Container(cfg => 
                cfg.For(typeof (IRepository<>)).Use(ctx => new AbnormalRepository<String, int>()));
        }

        [Test]
        public void TestItWorks()
        {
            var stringVector = _container.GetInstance(typeof (IRepository<>));
            Assert.IsInstanceOf<AbnormalRepository<String,int>>(stringVector);
        }
    }
}
Tim Hoolihan
One problem is the `For<IRepo<>>()` method doesn't exist - open generics seem to require the `For(typeof(IRepo<>))` method. The session context overload is available, however, so that seems like a good start. Unfortunately, my test `For(typeof(IRepository<>)).Use(c => new AbnormalRepository<Foo, Bar>())` (followed by a `GetInstance<IRepository<Foo>>()`) didn't work, saying I declared no default instance of `IRepository<Foo>`.
ladenedge
@ladenedge check out the code I added, the test works
Tim Hoolihan
Whatever type you pass to GetInstance, has to match how you registered it. In your example, you said you registered IRepository<>, but asked for IRepository<Foo>.
Tim Hoolihan
I think I get your problem now, as Joshua says above, you have to create a custom convention http://structuremap.github.com/structuremap/ScanningAssemblies.htm#section8
Tim Hoolihan
+2  A: 

Create a custom IRegistrationConvention and call it from within the Scan() method of your container configuration.

You can see an example of this discussed on another stackoverflow question:

http://stackoverflow.com/questions/2379680/structuremap-iregistrationconvention-to-register-non-default-naming-convention

You can also see a number of IRegistrationConvention examples within the StructureMap source code itself.

Joshua Flanagan
I was hoping to avoid a custom registration convention because it changes how we're currently initializing StructureMap. It does work in my tests, though, which is certainly a good thing. Thanks for your help!
ladenedge