How to register base type as a service type?

Mar 3, 2015 at 1:27 PM
Hi,

I have a lookup class that fill from published events and initialize at system setup.

When I call resolve method for "IMessageHandler<ProductNameChanged>" event, SimpleInjector throws that exception "IDictionary<Guid, ProductReference> products not registered in InMemoryProductLookup".It throws exception because SimpleInjector cannot find any implementation for dictionary when resolving "IMessageHandler<ProductNameChanged>"


I need to something like this:
if  "IMessageHandler<ProductNameChanged>"  implementation is InMemoryProductLookup, resolve and return IProductLookup

How can I register my IProductLookup as singleton instance, will return singleton instance for IProductLookup and IMessageHandler<T> types?


public class ProductReference
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string CatalogId {get;set;}
}

public interface IProductLookup
    :   IMessageHandler<ProductCreated>,
    IMessageHandler<ProductNameChanged>
{
    ProductReference GetProduct(Guid id);
}


public class InMemoryProductLookup
        : IProductLookup
{
    private readonly IDictionary<Guid, ProductReference> _products;

    public InMemoryProductLookup(IDictionary<Guid, ProductReference> products)
    {
        _products = products;
    }

    public void Handle(ProductCreated message)
    {
        var p = GetProduct(message.Id);
        if (p != null)
            return;

        _products.Add(message.Id,new ProductReference(){ Id = message.Id,Name=message.Name,CatalogId=message.CatalogId });
    }

    public void Handle(ProductNameChanged message),
    {
        var p = GetProduct(message.Id);
        if (p == null)
            return;
        p.Name = message.Name;
    }

    public ProductReference GetProduct(Guid id)
    {
        ProductReference product = null;
        _products.TryGetValue(id, out product);
        return product;
    }
}

public class DatabaseLookupInitializer
{
    private readonly Func<ISession> _sessionFunc;

    public DatabaseLookupInitializer(Func<ISession> sessionFunc)
    {
        if (sessionFunc == null)
            throw new ArgumentNullException("sessionFunc");
        _sessionFunc = sessionFunc;
    }

    public IProductLookup Initialize()
    {
        var session = _sessionFunc();
        var products = LoadProductsFromDatabae(session);

        return new InMemoryProductLookup(products);
    }
}

public class LogEventHandler
    :   IMessageHandler<ProductNameChanged>
{
    IProductLookup _lookup;
    public class LogEventHandler(IProductLookup lookup) {
        _lookup = lookup;
    }

    public void Handle(ProductNameChanged message)
    {
       Log(_lookup.GetProduct(message.Id).CatalogId);
    }
}

public class MailEventHandler
    :   IMessageHandler<ProductNameChanged>
{
    IProductLookup _lookup;
    public class LogEventHandler(IProductLookup lookup) {
        _lookup = lookup;
    }

    public void Handle(ProductNameChanged message)
    {
       Mail(_lookup.GetProduct(message.Id));
    }
}

container.Register<IProductLookup>(() =>
            {
                return new DatabaseLookupInitializer(container.GetInstance<Func<ISession>>()).Initialize();
            }, Lifestyle.Singleton);

 container.RegisterManyForOpenGeneric(typeof(IMessageHandler<>),
                (closedServiceType, implementations) =>
                {
                    container.RegisterAll(closedServiceType, implementations);
                },
                applicationAssembly);
Coordinator
Mar 3, 2015 at 8:01 PM
I'm sorry, but I don't understand the question, what you are trying to achieve and the problem you are facing. Could you rephrase the question? The InMemoryProductLookup class looks like something that's part of your Unit test suite; is that correct? Are you trying to use your DI container inside a unit test?
Mar 4, 2015 at 2:44 PM
Hi,

InMemoryProductLookup is not unit test or part of unit test. It's simple projection for convert events to simple lookup.

SimpleInjector register IProductLookup, IMessageHandler<ProductCreated> and IMessageHandler<ProductNameChanged> as different instances. I want to register InMemoryProductLookup as signleton for all interfaces that class (IProductLookup, IMessageHandler<ProductCreated> and IMessageHandler<ProductNameChanged>)


container.Register<IProductLookup>(() =>
        {
            return new DatabaseLookupInitializer(container.GetInstance<Func<ISession>>()).Initialize();
        }, Lifestyle.Singleton);
I want to simpleinjector return above singleton instace of InMemoryProductLookup object when I call below methods:
container.GetInstance<IProductLookup>(); // return InMemoryProductLookup
container.GetAllInstances<IMessageHandler<ProductCreated>>(); // return InMemoryProductLookup, 
container.GetAllInstances<IMessageHandler<ProductNameChanged>>(); // return LogEventHandler,MailEventHandler and InMemoryProductLookup
I solved that problem add one level abstraction for ProductReference storage like below but I didn't like this solution.
public class InMemoryProductLookup
        : IProductLookup
{
    private ProductReferenceStorage _storage;

    public InMemoryProductLookup(ProductReferenceStorage storage)
    {
        _storage = storage;
    }

    public void Handle(ProductCreated message)
    {
        var p = GetProduct(message.Id);
        if (p != null)
            return;

        _products.Add(message.Id,new ProductReference(){ Id = message.Id,Name=message.Name,CatalogId=message.CatalogId });
    }

    public void Handle(ProductNameChanged message),
    {
        var p = GetProduct(message.Id);
        if (p == null)
            return;
        p.Name = message.Name;
    }

    public ProductReference GetProduct(Guid id)
    {
        ProductReference product = null;
        _storage.Products.TryGetValue(id, out product);
        return product;
    }
}

public class ProductReferenceStorage{
    public IDictionary<Guid, ProductReference> Products {get;set;}
}
Coordinator
Mar 4, 2015 at 7:28 PM
Edited Mar 4, 2015 at 7:44 PM
I think this is what you are looking for:
container.RegisterSingle<IProductLookup>(
    () => new DatabaseLookupInitializer(container.GetInstance<Func<ISession>>()).Initialize());

container.RegisterSingle<IMessageHandler<ProductCreated>>(
    container.GetInstance<IProductLookup>);

container.RegisterSingle<IMessageHandler<ProductNameChanged>>(
    container.GetInstance<IProductLookup>);