Search code examples
c#lifecyclesimple-injector

Simple Injector 3 sees a registration as transient, even with `RegisterSingleton()`


I'm building an application (Xamarin.Forms, PCL and iOS, in case it's relevant) that uses Simple Injector for dependency injection throughout. So far, it's been great to work with, and with the recent release of Simple Injector 3 I'm updating my code to work with the new API. Unfortunately, I'm having a little trouble with one particular registration.

Here's the basic flow:

  • register an ISQLitePlatform (Windows, iOS, Android, etc) to handle the platform-specific bits (SQLite API, file I/O etc.)
  • register a bootstrapper which will be responsible for creating the database file, setting up all the table/type mappings etc.
  • register a lambda that will create our data access provider, use the bootstrapper to set up the database, and return the access provider

The app can then use the data access provider to open transactions on the database - the IDataAccessProvider interface just has one method, NewTransaction(), which returns a unit-of-work object with generic CRUD methods. The IDataAccessProvider also implements IDisposable, and the Dispose() methods handle the cleanup, closing of open connections/transactions, etc.

The issue is with these registrations:

container.RegisterSingleton<ISQLitePlatform, SQLitePlatformIOS>();
container.RegisterSingleton<ILocalDataBootstrapper, SQLiteDataBootstrapper>();
container.RegisterSingleton<IDataAccessProvider>(
    () => container.GetInstance<ILocalDataBootstrapper>().DataAccessProvider);

For some reason, on startup I get the diagnostic warning that SQLiteDataAccessProvider is IDisposable and has been registered as transient. I can handle this - the disposal is being handled in the correct place - but what's confusing me is that it's definitely not being registered as transient, given that I'm using RegisterSingleton().

Is this a quirk of using RegisterSingleton() with a creation lambda instead of an instance? Is it a bug? Or am I missing something I'm supposed to do?


Solution

  • The most likely reason why this is happening is that somewhere in your code you make the following call:

    container.GetInstance<SQLiteDataAccessProvider>();
    

    Or you have a constructor that depends on this concrete type instead of the abstraction (in which case you can expect other warnings about this as well).

    Since SQLiteDataAccessProvider is not registered 'as itself' (but merely by its IDataAccessProvider abstraction), Simple Injector will resolve this type for you and by default Simple Injector will make this type transient.

    By doing so, a new registration is added to Simple Injector; you will be able to see that in the debugger. There is a singleton registration for IDataAccessProvider and a transient registration for SQLiteDataAccessProvider.

    Now because there is a transient registration for SQLiteDataAccessProvider, Simple Injector will warn you that this instance will not get disposed automatically.

    The solution is to remove the GetInstance<SQLiteDataAccessProvider>() call (or changge the ctor argument to use the abstraction) and replace it with a call to GetInstance<IDataAccessProvider>().