¿Cómo puedo usar ISpecimenBuilders personalizados con OmitOnRecursionBehavior?

¿Cómo puedo usar la costumbre?ISpecimenBuilder instancias junto con elOmitOnRecursionBehavior ¿Qué deseo aplicar globalmente a todos los objetos creados por aparatos?

Estoy trabajando con un modelo de EF Code First con una referencia circular de olor desagradable que, para los fines de esta pregunta, no se puede eliminar:

public class Parent {
    public string Name { get; set; }
    public int Age { get; set; }
    public virtual Child Child { get; set; }
}

public class Child {
    public string Name { get; set; }
    public int Age { get; set; }
    public virtual Parent Parent { get; set; }
}

Estoy familiarizado con la técnica para referencias circulares de pasos laterales, como en esta prueba que pasa:

[Theory, AutoData]
public void CanCreatePatientGraphWithAutoFixtureManually(Fixture fixture)
{
    //fixture.Customizations.Add(new ParentSpecimenBuilder());
    //fixture.Customizations.Add(new ChildSpecimenBuilder());
    fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
                     .ForEach(b => fixture.Behaviors.Remove(b));
    fixture.Behaviors.Add(new OmitOnRecursionBehavior());
    fixture.Behaviors.Add(new TracingBehavior());
    var parent = fixture.Create<Parent>();
    parent.Should().NotBeNull();
    parent.Child.Should().NotBeNull();
    parent.Child.Parent.Should().BeNull();
}

Pero si cualquiera o ambas personalizaciones no tienen comentarios, obtendré una excepción:

System.InvalidCastException: Unable to cast object of type
'Ploeh.AutoFixture.Kernel.OmitSpecimen' to type 'CircularReference.Parent'.

El elenco defectuoso está ocurriendo en miISpecimenBuilder implementaciones (que se muestran en la parte inferior de esta pregunta) cuando llamo alISpecimenContext resolverParent y la solicitud viene de laChild siendo resuelto Podría protegerme contra la petición que venía de laChild Resolviendo la operación de esta manera:

//...
&& propertyInfo.ReflectedType != typeof(Child)
//...

Pero, eso parece contaminar elISpecimenBuilder Implementación con conocimiento de 'quién' podría estar haciendo la solicitud. Además, parece duplicar el trabajo que el 'global'OmitOnRecursionBehavior está destinado a hacer

Quiero usar elISpecimenBuilder instancias porque tengo otras cosas para personalizar además de manejar la referencia circular. He pasado mucho tiempo buscando ejemplos de un escenario como este aquí en SO y también enPloeh pero no he encontrado nada todavía que discute laCombinación de comportamientos y personalizaciones.. Es importante que la solución sea una que pueda encapsular conICustomization, en lugar de líneas y líneas en la configuración de prueba de

//...
fixture.ActLikeThis(new SpecialBehavior())
       .WhenGiven(typeof (Parent))
       .AndDoNotEvenThinkAboutBuilding(typeof(Child))
       .UnlessParentIsNull()
//...

... porque en última instancia quiero extender un[AutoData] atributo para pruebas.

Lo que sigue son misISpecimenBuilder implementaciones y la salida de laTracingBehavior para la prueba que falla

public class ChildSpecimenBuilder : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var propertyInfo = request as PropertyInfo;
        return propertyInfo != null
               && propertyInfo.PropertyType == typeof(Child)
                   ? Resolve(context)
                   : new NoSpecimen(request);
    }

    private static object Resolve(ISpecimenContext context)
    {
        var child = (Child) context.Resolve(typeof (Child));
        child.Name = context.Resolve(typeof (string)).ToString().ToLowerInvariant();
        child.Age = Math.Min(17, (int) context.Resolve(typeof (int)));
        return child;
    }
}

public class ParentSpecimenBuilder : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var propertyInfo = request as PropertyInfo;
        return propertyInfo != null
               && propertyInfo.PropertyType == typeof (Parent)
                   ? Resolve(context)
                   : new NoSpecimen(request);
    }

    private static object Resolve(ISpecimenContext context)
    {
        var parent = (Parent) context.Resolve(typeof (Parent));
        parent.Name = context.Resolve(typeof (string)).ToString().ToUpperInvariant();
        parent.Age = Math.Max(18, (int) context.Resolve(typeof (int)));
        return parent;
    }
}

CanCreatePatientGraphWithAutoFixtureManually(fixture: Ploeh.AutoFixture.Fixture) : Failed  Requested: Ploeh.AutoFixture.Kernel.SeededRequest
    Requested: CircularReference.Parent
      Requested: System.String Name
        Requested: Ploeh.AutoFixture.Kernel.SeededRequest
          Requested: System.String
          Created: 38ab48f4-b071-40f0-b713-ef9d4c825a85
        Created: Name38ab48f4-b071-40f0-b713-ef9d4c825a85
      Created: Name38ab48f4-b071-40f0-b713-ef9d4c825a85
      Requested: Int32 Age
        Requested: Ploeh.AutoFixture.Kernel.SeededRequest
          Requested: System.Int32
          Created: 9
        Created: 9
      Created: 9
      Requested: CircularReference.Child Child
        Requested: Ploeh.AutoFixture.Kernel.SeededRequest
          Requested: CircularReference.Child
            Requested: System.String Name
              Requested: Ploeh.AutoFixture.Kernel.SeededRequest
                Requested: System.String
                Created: 1f5ca160-b211-4f82-871f-11882dbcf00d
              Created: Name1f5ca160-b211-4f82-871f-11882dbcf00d
            Created: Name1f5ca160-b211-4f82-871f-11882dbcf00d
            Requested: Int32 Age
              Requested: Ploeh.AutoFixture.Kernel.SeededRequest
                Requested: System.Int32
                Created: 120
              Created: 120
            Created: 120
            Requested: CircularReference.Parent Parent
              Requested: CircularReference.Parent
              Created: Ploeh.AutoFixture.Kernel.OmitSpecimen

System.InvalidCastException: Unable to cast object of type 'Ploeh.AutoFixture.Kernel.OmitSpecimen' to type 'CircularReference.Parent'.

Respuestas a la pregunta(2)

Su respuesta a la pregunta