Register boolean in container

I am currently rewriting my company's DAO library. Due to our solution characteristic, we need to switch ADO (Oracle / SQL Server) libraries based on the client. Since static links are the main source of our problems (feOracle package taken from CI instead of SQL Server), I decided to go with the plugin architecture and I am trying to load the required DLLs dynamically.

I'm new to Simple Injector (I used Ninject, but in this case, we need something really fast). I used the article https://simpleinjector.readthedocs.org/en/latest/advanced.html#registering-plugins-dynamically and was able to load the correct DLLs into Domain.

I am currently facing some strange (in my opinion) error while checking the container (container.Verify ()):

The constructor for GenericDAO contains a Boolean parameter named isInUserContext that is not registered. Make sure Boolean is registered with the container or changed the constructor of GenericDAO.

My constructor looks like this:

public class GenericDAO : DBHelper
{

    public GenericDAO(Boolean isInUserContext, string connectionString, string providerName, UniversalDataAccess universalDataAccess)
        : base(isInUserContext, connectionString, providerName, universalDataAccess)
    {
    }

      

My registration:

var pluginDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");

var pluginAssemblies =
    from file in new DirectoryInfo(pluginDirectory).GetFiles()
    where file.Extension.ToLower() == ".dll"
    select Assembly.LoadFile(file.FullName);

var pluginTypes =
    from assembly in pluginAssemblies
    from type in assembly.GetExportedTypes()
    where typeof (IDBHelper).IsAssignableFrom(type)
    where !type.IsAbstract
    where !type.IsGenericTypeDefinition
    select type;

_kernel.RegisterAll<IDBHelper>(pluginTypes); 

      

Do you know how to successfully initialize all the required types? I would like to expose a public property of type GenericDAO.

+3


source to share


1 answer


The error message is a little misleading as Simple Injector prevents you from registering primitive types (for example Boolean

) with the container, so the container is actually telling you something that cannot be done.

The reason Simple Injector prevents you from registering primitives like int, bool, string is because these types are ambiguous. DI in .NET injection based on type information, but it's unlikely that all the components in your codebase that need string

their constructor actually need the same value. Some expect a connection string, others expect some file path. But if you can register string

with the container, you can only provide one value (like a connection string), which makes it impossible to insert another value (like a file path).

However, this problem is not specific to a simple injector, these are common problems. Other DI containers, however, may allow you to allow components that take primitive values ​​in the constructor, and may fill that value with some default value. This would hardly be useful, because if you only want the default value of that primitive type, why would you expose this type through the constructor anyway?

It is difficult to say what you should do to fix this problem. Typically, if a type requires some configuration values, you must explicitly tell the container how the type should be resolved. With Simple Injector it looks like this:

container.Register<GenericDAO>(() => new GenericDAO(
    isInUserContext: true, 
    connectionString: "con str", 
    providerName: "System.Sql.Client",
    universalDataAccess: container.GetInstance<UniversalDataAccess>()));

      

However, if you have multiple (or many) components that require the same configuration values, this usually means that you are missing an abstraction. For example, isInUserContext

it is probably a value that should be hidden behind abstraction IUserContext

and smell connectionString

and providerName

as an abstraction IDbConnectionFactory

or something similar. In this latter case, the class GenericDAO

will be responsible for creating connections in another class, allowing you to focus on what is in charge.

In this case, the constructor GenericDAO

will look like this:

public GenericDAO(IUserContext userContext, IDbConnectionFactory connectionFactory,
    UniversalDataAccess universalDataAccess)

      

Now, since the constructor no longer has primitive arguments, the class can now be automatically included by your container. In Simple Injector, you can register it like this:

container.Register<GenericDAO>();

      



The advantage becomes especially clear in your case, since you register the package GenericDAO

as a plugin. All types registered in batch mode must be auto-pluggable to work successfully.

Of course, you will also need to register IUserContext

and IDbConnectionFactory

, and they may still depend on some primitive values:

container.RegisterSingle<IUserContext>(new AspNetUserContext());
container.RegisterSingle<IDbConnectionFactory>(
    new SqlServerDbConnectionFactory("con str"));

      

Abstractions IUserContext

and IDbConnectionFactory

should be defined in some central assembly, which includes both the boot block and plugin assemblies.

Another option is to let the plugin assemblies register the plugins themselves. The downside to this is that these plugin assemblies have to depend on your DI container. A simple injector contains a SimpleInjector.Packaging

NuGet project that allows you to create a "package" in your plugin assembly. For example:

public class Plugin1Bootstrapper : IPackage
{
    public void RegisterServices(Container container) {
        container.Register<GenericDAO>(() => new GenericDAO(
            isInUserContext: true, 
            connectionString: "con str", 
            providerName: "System.Sql.Client",
            universalDataAccess: container.GetInstance<UniversalDataAccess>()));

        // other registrations here.
    }
}

      

On the way to launch the application, you can call:

container.RegisterPackages(pluginAssemblies);

      

This will load all implementations IPackage

for the supplied assemblies and call RegisterServices

for each one.

Note that we are registering again GenericDAO

by its specific type. You can mix this with RegisterAll<IDBHelper>(...)

whatever you are already using. Registration RegisterAll

will return to the container for every item in the collection, so by registering a specific type with Register<GenericDAO>

, it is possible to explicitly tell how Simple Injector should resolve that specific type. Otherwise Simple Injector will try to set that particular type automatically and resolve it as transient.

+6


source







All Articles