如何在SimpleInjector中注册一个比Service更多泛型类型参数的TImpl?

时间:2012-03-02 15:36:31

标签: generics dependency-injection registration simple-injector

我现在正在做以下事情

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 个答案:

答案 0 :(得分:2)

这可能吗?是和否: - )

typeof(DatabaseMapper<,OracleException>)不是有效的C#代码。您要么必须提供所有泛型类型参数,要么根本不提供。因此,无法通知Container它应使用TException填写缺少的OracleException类型参数。所以不,你不能这样做。

但是,当然你可以这样做:-)。只需创建一个继承自OracleExceptionDatabaseMapper<T>的{​​{1}}类,并在注册中使用该类型:

DatabaseMapper<T, OracleException>

这样,给定的实现只有一个泛型类型,可以映射到给定服务接口的单个​​泛型类型参数。

<强>更新

由于Simple Injector 2.4可以注册parial open泛型类型,但由于C#仍然不支持,你必须手动创建部分开放泛型类型,如下所示:

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

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

答案 1 :(得分:1)

为了完整性,以下是使用未注册的类型解析如何执行此操作的示例:

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的新实例的创建。此表达式ID使用e.Register方法进行注册,该方法有效地将IDatabaseMapper<T>映射到DatabaseMapper<T, OracleException>的创建。

重要提示:我认为使用未注册的类型解析应仅用作后备选项,因为通常有更简单的方法来解决您的问题(例如我在其他答案中显示的方法),但未注册的类型解析可能很有用在某些高级场景中(例如,当DatabaseMapper<T, TException>被密封时)。