This project is read-only.

Integrating Simple Injector with PetaPoco

Dec 22, 2011 at 5:31 AM

I'm new to simple injector (and to DI in general) and trying to use this with Petapoco (Database object). I get the error "For the container to be able to create Database, it should contain exactly one public constructor, but it has 5.Parameter name: TImplementation".  What im trying to do is similar to the top answer to this question on stack overflow (question not important).  

http://stackoverflow.com/questions/7429628/using-petapocos-shared-connection-with-structuremap

Dec 22, 2011 at 8:19 AM
Edited Jul 18, 2012 at 5:33 PM

The general advice is to let your services have a single definition of the dependencies they need. In other words, have a single public constructor to inject dependencies into. For this reason (and to make migration easier) the Simple Injector only allows automatic constructor injection on types that contain a single public constructor.

This is of course a problem for types that you don't create yourself, such as Petapoco's Database class. For this scenario, Simple Injector allows you to register a delegate as factory:

string conStr = ConfigurationManager.ConnectionStrings["mycon"]
.ConnectionString; container.Register<Database>(() => new Database(conStr));

However, since Petapoco's database class implements IDisposable, the Database class should be disposed after use. You've got two options. Either you don't inject a Database class itself but use an IDatabaseFactory class with a CreateNew method and let consumers create and distroy instances themselves, or you register a delegate that allows the object to be disposed after some scope ends.

The first option might look like this:

// Abstract factory definition
// Accessible for the whole application
public interface IDatabaseFactory
{
    Database CreateNew();
}

// Implementation
// Part of the Composition Root
private class DatabaseFactory : IDatabaseFactory
{
    public Database CreateNew()
    {
        string constr = ConfigurationManager
            .ConnectionStrings["myCon"]
                .ConnectionString;
                
        return new Database(constr);
    }
}

// DI configuration
container.RegisterSingle<IDatabaseFactory, DatabaseFactory>();

// Usage
public class SomeConsumer
{
    private readonly IDatabaseFactory factory;
    
    public SomeConsumer(IDatabaseFactory factory)
    {
        this.factory = factory;
    }
    
    public void SomeOperation()
    {
        using (var db = this.factory.CreateNew())
        {
            // Do something useful with db.
        }
    }
}

Your other option is to register a delegate that knows when to dispose such type. For instance, when working with ASP.NET applications, such instance can often be reused during a single web request and be disposed afterwards. You can use the per web request lifestyle:

container.RegisterPerWebRequest<Database>(() => new Database(conStr));

The RegisterPerWebRequest extension method automatically disposes the instance when the web request ends. In this case you can simply inject Database types into your consumers, instead of injecting an IDatabaseFactory.

If you've got some other type of scope or other type of application, you can use lifetime scoping.

I hope this helps.

Dec 22, 2011 at 4:11 PM

Thanks for reply, I'm trying to use the second option.

My scenario is I have multiple databases, I mean One for App Data(like customer data), Another for site infrastructure (like logging), One more for App services (like membership). And each web request will have to deal with these databases. So there will be 3 database object per request. And Connection string of database may very per request but Database object needs to  be same for the life of request. Whats the best way to handle this?  

I'm thinking of following, not sure if it works even for single database object with varying connection string?

container.RegisterPerWebRequestWithDisposal<Database>(() =>
{
    string constr = "AppDB";
    if (HttpContext.Current != null && HttpContext.Current.Items["AppDBConnectionStringName"] != null)
    {
        constr = HttpContext.Current.Items["AppDBConnectionStringName"].ToString();
    }
    return new Database(constr);

});

I read about there may be bug in RegisterPerWebRequestWithDisposal, and left a msg there yesterday to check if there was any. 

Dec 22, 2011 at 4:33 PM

You can't safely register a single (Database) type and expect the correct instance (app, membership, logging) to be injected, since the container has no way of knowing which version should be injected where. If you have multiple databases, it is better to give each database its own interface. This way there will never be a problem resolving such a database. For instance, you can call them: LoggingDatabase, MembershipDatabase and AppDatabase. You can register them separately and simply let consumers depend on one of those (or multiple).

Logging and Membership however, seem like stuff that is only used by a few classes (a DatabaseLogger of some sort and a MyOwnMembershipProvider class). In that case, you can let the Database registration point at the Application database. You could still register a LoggingDatabase and MembershipDatabase, but you can also inject them manually in the types that need it.

Thank you for leaving a message at the RegisterPerWebRequestWithDisposal page. I get notified by mail when someone leaves a comment, but somehow I wasn't notified about Joco's message. It's not clear to me whether there is a bug and I'm looking into it.

Marked as answer by dot_NET_Junkie on 11/3/2013 at 11:46 PM