开发者

Ninject Singleton for MVC Data Repository

In my MVC3 app I have an IDataRepository interface which is referenced by all my controllers to give them access to the data layer. There's also a DataRepository class which is implements IDataRepository for a particular data source (an nHydrate-derived Entity Framework, in my case). The DataRepository class takes a single argument, which is the connection string to the underlying database.

I've been successfully using nInject to to IoC with the controller classes using the following binding:

kernel.Bind<IDataRepository>()
    .To<DataRepository>()
    .WithConstructorArgument("connectionString", DataRepositoryBase.GetConnectionString());

Today I read about nInject scoping, and I thought it would be useful to arrange things so that only one instance of DatabaseRepository got created for each request (I'm thinking this will be more efficient, although with EF I'm not sure).

Unfortunately, I can't seem to figure out how to implement the pattern correctly. For example, this doesn't work:

kernel.Bind<DataRepository>()
    .ToSelf()
    .InRequestScope()
    .WithConstructorArgument("connectionString", DataRepositoryBase.GetConnectionString());

kernel.Bind<IDataRepository>()
    .To<DataRepository>();

My thinking was that this would create just a single instance of DataRepository, which would be used in all references to IDataRepository. The error message complained that no match could be found for the connectionString parameter, and DataRepository was not self-bindable. I tried some variations, but when I could get it to work the singleton pattern wasn't being followed (i.e., I could see in the debugger that multiple instances of DataRepository were being created).

I'm missing something obvious here :).

--- Addendum --- Unfortunately, the suggestion doesn't prevent multiple instances from being created within the same request.

To be clear, what I tried was:

public class BaseControllerModule : NinjectModule
{
    public override void Load()
    {
        Bind<IDataRepository>().To<DataRepository>().InRequestScope()
        .WithConstructorArgumen开发者_开发技巧t("connectionString", DataRepositoryBase.GetConnectionString());
    }
}

and what I was monitoring was the constructor:

public DataRepository( string connectionString )
    : base(connectionString)
{
}

-- More info #2 --

Here's the layout of the classes Ninject is resolving for me:

public class DataRepositoryBase
{
    protected DataRepositoryBase( string connectionString )
    {}
    public static string GetConnectionString() {}
}
public class DataRepository : DataRepositoryBase, IDataRepository
{
    public DataRepository( string connectionString )
        : base(connectionString)
    {}
}

I've left out the implementation details, but hopefully this paints a better picture.

Looking this over, I wonder if I'm causing problems by making connectionString a constructor parameter for both DataRepository and its base class DataRepositoryBase. Wouldn't Ninject resolve connectionString in the call the base class constructor?

p.s. I belatedly realized I don't need DataRepositoryBase, because its functionality can be merged into DataRepository. I've done that, but I'm still having the constructor for DataRepository called multiple times in what appears to be one request.

p2.s. For fun, I tried declaring InSingletonScope() in the Ninject binding definition. That worked -- the constructor for DataRepository now only gets called once, when the app is first accessed. But I don't think it's a good idea to have singletons in an MVC app. It seems like that would cause the "state" of the app to get "locked" in memory.

--- yet more info ---

The problem seems to be with the way I've designed my MVC app. What I assumed was a single request from the browser back to the server often results in multiple requests being processed in sequence (I'm watching the BeginRequest event being fired in the MvcApplication class). It seems like every time I transition to a different controller a new request is being generated (e.g., via a RedirectToAction). I guess this makes sense, but it means Ninject's InRequestScope won't quite do what I want.

But it also makes me wonder if I've just designed the app wrong. It seems like I should be grouping all of the action methods that might get invoked on a browser call into a single controller. Instead, I've organized the action methods by how they fit into the conceptual model for my app.


These two bindings say: When a DataRepository is requested reuse the instance for all occurances within the request and set the connection string to DataRepositoryBase.GetConnectionString().

But when a IDataRepository is requested, create a new instance for every occurance and let Ninject decide what it injects for the connection string.

What you really want is done by adding InRequestScope to the first code snippet.


Wouldn't that be sufficient to have a singleton?

kernel.Bind<IDataRepository>()
    .To<DataRepository>()
    .InSingletonScope()
    .WithConstructorArgument("connectionString", DataRepositoryBase.GetConnectionString());

RequestScope is not a singleton, it means that the objects are separate for each user's call.

By the way, I think the real repository shouldn't be singleton - it should rather follow the Unit of Work pattern, meaning that its lifetime should represent one higher-level data operation and the connection itself should be at lower level than repository.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜