Как зарегистрировать TImpl, который имеет один более общий параметр типа, чем TService в SimpleInjector? - PullRequest
1 голос
/ 02 марта 2012

Я сейчас делаю следующее

container.Register<IDatabaseMapper<User>, DatabaseMapper<User, OracleException>>();
container.Register<IDatabaseMapper<Desk>, DatabaseMapper<Desk, OracleException>>();
container.Register<IDatabaseMapper<Commodity>, DatabaseMapper<Commodity, OracleException>>();

Но я бы хотел сделать что-то подобное

container.RegisterOpenGeneric(typeof(IDatabaseMapper<>), typeof(DatabaseMapper<,OracleException>));

Возможно ли это как-то?

Ответы [ 2 ]

2 голосов
/ 02 марта 2012

Возможно ли это? Да и нет: -)

typeof(DatabaseMapper<,OracleException>) не является допустимым кодом C #. Вы должны либо указать все аргументы общего типа, либо не указывать их вообще. Поэтому нет способа сообщить Container, что он должен заполнить отсутствующий аргумент типа TException с помощью OracleException. Так что нет, вы не можете этого сделать.

Но да, конечно, вы можете сделать это :-). Просто создайте класс OracleExceptionDatabaseMapper<T>, который наследуется от DatabaseMapper<T, OracleException>, и используйте этот тип при регистрации:

// Helper class
public class OracleExceptionDatabaseMapper<T>
    : DatabaseMapper<T, OracleException>
{
}

// Registration
container.RegisterOpenGeneric(typeof(IDatabaseMapper<>),
    typeof(OracleExceptionDatabaseMapper<>));

Таким образом, данная реализация имеет только 1 универсальный тип, который можно сопоставить с единственным аргументом универсального типа данного интерфейса службы.

UPDATE

Начиная с Simple Injector 2.4, можно регистрировать парциальные открытые универсальные типы, но, поскольку это все еще не поддерживается в C #, вам придется вручную создавать частичный открытый универсальный тип следующим образом:

Type databaseMapperType = typeof(DatabaseMapper<,>).MakeGenericType(
    typeof(DatabaseMapper<,>).GetGenericArguments().First(),
    typeof(OracleException));

container.RegisterOpenGeneric(typeof(IDatabaseMapper<>), databaseMapperType);
1 голос
/ 03 марта 2012

Для полноты, вот пример того, как сделать это с использованием незарегистрированного разрешения типа:

container.ResolveUnregisteredType += (s, e) =>
{
    var serviceType = e.UnregisteredServiceType;

    if (serviceType.IsGenericType &&
        serviceType.GetGenericTypeDefinition() == typeof(IDatabaseMapper<>))
    {
        Type argument = serviceType.GetGenericArguments()[0];

        var closedDatabaseMapperType = typeof(DatabaseMapper<,>)
            .MakeGenericType(argument, typeof(OracleException));

        var registration =
            container.GetRegistration(closedDatabaseMapperType, true);

        e.Register(registration.BuildExpression());
    }
};

Событие ResolveUnregisteredType будет вызываться контейнером всякий раз, когда запрашивается тип, который не зарегистрирован.Это дает вам последний шанс зарегистрировать этот тип.Поставляемый UnregisteredTypeEventArgs содержит две перегрузки метода Register, которые позволяют вам зарегистрировать этот тип (используя Func<T> или Expression).

Приведенный выше код проверяет, запрашивается ли запрошенная службатипом является IDatabaseMapper<T>, и если это так, он создает DatabaseMapper<T, OracleExpression>, где T заменяется фактическим типом типа службы.Используя этот тип, регистрация для этого типа запрашивается из контейнера.Используя метод BuildExpression этого объекта регистрации, мы можем построить дерево выражений, которое описывает создание нового экземпляра этого DatabaseMapper.Это выражение с идентификатором зарегистрировано с использованием метода e.Register, который фактически сопоставляет IDatabaseMapper<T> с созданием DatabaseMapper<T, OracleException>.

. ВАЖНО: я считаю, что использование незарегистрированного разрешения типов следует использовать только как запасной вариант, поскольку часто есть более простые способы решения вашей проблемы (например, тот, который я показал в моем другом ответе), но разрешение незарегистрированного типа может быть полезно в определенных расширенных сценариях (например, когда DatabaseMapper<T, TException> запечатан).

...