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(...)
约束 时更快
缺点:
- 仅当包含实体的程序集在启动时已知时才有效 - 当应用约定时
- 增加启动时间
我需要在 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(...)
约束 时更快
缺点:
- 仅当包含实体的程序集在启动时已知时才有效 - 当应用约定时
- 增加启动时间