如何注册通用对象工厂



我有以下两个类:

public class KeyedEntity<TEntity>
{
    internal KeyedEntity() { }
    public Identifier Key { get; set; }
    public TEntity Entity { get; set; }
}
public static class KeyedEntity
{
    public static KeyedEntity<TEntity> Create<TEntity>(Identifier key, TEntity entity)
    {
        return new KeyedEntity<TEntity>
        {
            Key = key,
            Entity = entity,
        };
    }
}

构造函数是internal,而第二个类存在的原因是我想强制使用更高可维护性的KeyedEntity.Create(x, y)语法,而不是new KeyedEntity<T>{ Key = x, Entity = y }。(请注意,类型是用以前的语法推断的。)

我想告诉AutoFixture如何创建KeyedEntity的实例。然而,Register方法似乎只允许注册单个类型,而不允许注册开放泛型类型。

如何将KeyedEntity.Create<TEntity>注册为KeyedEntity<TEntity>的创建函数?

为了支持您的开放泛型类型,您可以编写一个自定义的样本生成器:

public class KeyedEntityBuilder : ISpecimenBuilder
{
    private readonly static MethodInfo createMethod =
        typeof(KeyedEntity).GetMethod("Create");
    public object Create(object request, ISpecimenContext context)
    {
        var t = request as Type;
        if (t == null ||
            !t.IsGenericType ||
            t.GetGenericTypeDefinition() != typeof(KeyedEntity<>))
            return new NoSpecimen(request);
        var entityType = t.GetGenericArguments().Single();
        var key = context.Resolve(typeof(Identifier));
        var entity = context.Resolve(entityType);
        return createMethod
            .MakeGenericMethod(entityType)
            .Invoke(null, new[] { key, entity });
    }
}

(为了清晰起见,省略了防御编码。)

以下单元测试通过:

public class Tests
{
    [Fact]
    public void CreateKeyedEntity()
    {
        var fixture = new Fixture();
        fixture.ResidueCollectors.Add(new KeyedEntityBuilder());
        var actual = fixture.Create<KeyedEntity<Foo>>();
        Assert.NotNull(actual.Key);
        Assert.NotNull(actual.Entity);
    }
}

为了更好地维护,您应该将KeyedEntityBuilder封装在自定义中。

假设您有一组派生类型,例如:

public class A: KeyedEntity<A>
{
}
public class B: KeyedEntity<B>
{
}

由于上面的对象图包含一个循环引用(在T上),您需要配置Fixture实例以省略第一次递归时的赋值:

然后您创建一个通用方法,该方法将自定义KeyedEntity<T>:的创建算法
internal void CustomizeKeyedEntity<T>(IFixture fixture)
{
    fixture.Customize<KeyedEntity<T>>(c =>
        c.FromFactory(() =>
            KeyedEntity.Create(
                fixture.Create<Identifier>(),
                fixture.Create<T>())));
}

您可以使用上述方法作为:

this.CustomizeKeyedEntity<A>(fixture);
this.CustomizeKeyedEntity<B>(fixture);

示例

[Fact]
public void Test()
{
    var fixture = new Fixture();
    this.CustomizeKeyedEntity<A>(fixture);
    this.CustomizeKeyedEntity<B>(fixture);
    var actualA = fixture.Create<A>();
    var actualB = fixture.Create<B>();
}

相关内容

  • 没有找到相关文章

最新更新