Ninject 中基于类型约束的条件泛型绑定

Conditional generic binding in Ninject based on type constraint

我需要在 Ninject 上执行通用 binding,但我遇到了一些问题。这些是 classes:

public class BaseBL<TEntity> : IDisposable, IBaseBL<TEntity> 
        where TEntity : class
{
     ....
}
public class BaseLogAuditoriaBL<TEntity> : BaseBL<TEntity>, IBaseBL<TEntity> 
    where TEntity : BaseLogAuditoriaEntity
{
  ....
}

我希望当我的 TEntity 继承自 BaseLogAuditoriaEntity 时它调用 BaseLogAuditoriaBL,否则它调用 BaseBL

在这种情况下,我该如何以通用方式配置我的 bindings

如果我把它放在每个 class 中就可以了,但是我需要一个通用的解决方案来解决这个问题。

示例:

kernel.Bind(typeof(IBaseBL<>)).To(typeof(BaseBL<>));
kernel.Bind(typeof(IBaseBL<Loja>)).To(typeof(BaseLogAuditoriaBL<>));

您可以使用 When(..) 语法创建满足您需求的条件绑定:

public static class BaseBLBindingExtensions
{
    public static IBindingInNamedWithOrOnSyntax<object> WhenEntityMatchesType<TEntityType>(
        this IBindingWhenSyntax<object> syntax)
    {
        return syntax.When(request => DoesEntityMatchType(request, typeof(TEntityType)));
    }

    private static bool DoesEntityMatchType(IRequest request, Type typeToMatch)
    {
        return typeToMatch.IsAssignableFrom(request.Service.GenericTypeArguments.Single());
    }
}

然后像这样使用:

kernel.Bind(typeof(IBaseBL<>)).To(typeof(BaseBL<>));
kernel.Bind(typeof(IBaseBL<>)).To(typeof(BaseLogAuditoriaBL<>))
    .WhenEntityMatchesType<BaseLogAuditoriaEntity>();

包含单元测试的完整示例(使用 XUnit 和 FluentAssertions):

using FluentAssertions;
using Ninject;
using Ninject.Activation;
using Ninject.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace NinjectTest.SO40310046
{
    public interface IBaseBL<TEntity>
        where TEntity : class
    { }

    public class BaseBL<TEntity> : IBaseBL<TEntity>
        where TEntity : class
    { }

    public class SimpleEntity { }

    public class BaseLogAuditoriaEntity
    { }

    public class ChildBaseLogAuditorialEntity: BaseLogAuditoriaEntity
    { }

    public class BaseLogAuditoriaBL<TEntity> : BaseBL<TEntity>, IBaseBL<TEntity>
        where TEntity: BaseLogAuditoriaEntity
    { }

    public static class BaseBLBindingExtensions
    {
        public static IBindingInNamedWithOrOnSyntax<object> WhenEntityMatchesType<TEntityType>(this IBindingWhenSyntax<object> syntax)
        {
            return syntax.When(request => DoesEntityMatchType(request, typeof(TEntityType)));
        }

        private static bool DoesEntityMatchType(IRequest request, Type typeToMatch)
        {
            return typeToMatch.IsAssignableFrom(request.Service.GenericTypeArguments.Single());
        }
    }

    public class UnitTest
    {
        [Fact]
        public void Test()
        {
            var kernel = new StandardKernel();

            kernel.Bind(typeof(IBaseBL<>)).To(typeof(BaseBL<>));
            kernel.Bind(typeof(IBaseBL<>)).To(typeof(BaseLogAuditoriaBL<>))
                .WhenEntityMatchesType<BaseLogAuditoriaEntity>();


            kernel.Get<IBaseBL<SimpleEntity>>()
                .Should().BeOfType<BaseBL<SimpleEntity>>();

            kernel.Get<IBaseBL<BaseLogAuditoriaEntity>>()
                .Should().BeOfType<BaseLogAuditoriaBL<BaseLogAuditoriaEntity>>();

            kernel.Get<IBaseBL<ChildBaseLogAuditorialEntity>>()
                .Should().BeOfType<BaseLogAuditoriaBL<ChildBaseLogAuditorialEntity>>();
        }
    }
}

备选

考虑到您的 BaseLogAuditoriaBL 已经定义了类型约束,它可能是使用基于约定的绑定的可行替代方案(例如使用 Ninject.Extensions.Conventions 创建,它扫描继承自 [=16 的类型=],检查类型约束,然后扫描遵守约束的所有类型,然后为这些类型创建特定的、无条件的绑定。 生成的绑定应该类似于:

kernel.Bind(typeof(IBaseBL<>)).To(typeof(BaseBL<>));
kernel.Bind(typeof(IBaseBL<BaseLogAuditoriaEntity>))
    .To(typeof(BaseLogAuditoriaBL<BaseLogAuditoriaEntity>));
kernel.Bind(typeof(IBaseBL<ChildBaseLogAuditoriaEntity>))
    .To(typeof(BaseLogAuditoriaBL<ChildBaseLogAuditoriaEntity>));

亲:

  • 分辨率比使用 When(...) 约束
  • 时更快

缺点:

  • 仅当包含实体的程序集在启动时已知时才有效 - 当应用约定时
  • 增加启动时间