Como combinar afirmações de coleção e propriedade usando afirmações fluentes?

Gostaria de "combinar" as declarações de coleção e declarações de propriedade da Fluent Assertion, p. afirmar que doisIEnumerable são iguais aos pares usando a comparação propriedade a propriedade (possivelmente "aninhada") (ou seja, igualdade estrutural, na linguagem da linguagem funcional

Exemplo de concreto:

var dic = new Dictionary<int, string>() { {1, "hi"}, {2, "bye" } };
var actual = dic.ToSelectListItems(0).OrderBy(si => si.Text);

var expected = new List<SelectListItem>() {
    new SelectListItem() {Selected = false, Text="bye", Value="2"},
    new SelectListItem() {Selected = false, Text="hi", Value="1"}
};

Aqui eu escrevi um método de extensãoToSelectListItems que converte umDictionary para umIEnumerable doSelectListItems (do ASP.NET MVC). Eu quero afirmar queactual eexpected são "estruturalmente" iguais, observando que o tipo de referênciaSelectListItem não substituiEquals e, portanto, usa a igualdade de referência por padrã

Atualiza

Atualmente, usando a seguinte solução enrolada à mão, ainda esperando algo melhor incorporado ao FluentAssertions:

public static void ShouldBeStructurallyEqualTo<T, U>(this IEnumerable<T> actual, IEnumerable<U> expected) {
    actual.Should().HaveCount(expected.Count());
    actual.Zip(expected).ForEach(pair => pair.Item1.ShouldHave().AllProperties().IncludingNestedObjects().EqualTo(pair.Item2));
}

(Nota:Zip aqui é o meu próprioIEnumerable extensão que usaTuple.Create como a projeção padrão)

Update 2

Aqui estão dois exemplos mínimos:

public class FooBar {
    public string Foo { get; set; }
    public int Bar { get; set; }
}

public class TestClass {
    [Test]
    public void MinimalExample() {
        List<FooBar> enumerable1 = new List<FooBar>() { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } };
        List<FooBar> enumerable2 = new List<FooBar>() { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } };

        enumerable1.ShouldHave().SharedProperties().IncludingNestedObjects().EqualTo(enumerable2);

        //Test 'TestClass.MinimalExample' failed: System.Reflection.TargetParameterCountException : Parameter count mismatch.
        //    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
        //    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
        //    at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
        //    at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
        //    at FluentAssertions.Assertions.PropertyEqualityValidator.AssertSelectedPropertiesAreEqual(Object subject, Object expected)
        //    at FluentAssertions.Assertions.PropertyEqualityValidator.Validate(UniqueObjectTracker tracker, String parentPropertyName)
        //    at FluentAssertions.Assertions.PropertyEqualityValidator.Validate()
        //    at FluentAssertions.Assertions.PropertyAssertions`1.EqualTo(Object otherObject, String reason, Object[] reasonArgs)
        //    at FluentAssertions.Assertions.PropertyAssertions`1.EqualTo(Object otherObject)
        //    MiscAssertions.cs(32,0): at TestClass.MinimalExample()
    }

    [Test]
    public void MinimalExample2() {
        IEnumerable<FooBar> enumerable1 = (new List<FooBar>() { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } }).Cast<FooBar>();
        FooBar[] enumerable2 = new [] { new FooBar() { Foo = "x", Bar = 1 }, new FooBar() { Foo = "y", Bar = 2 } };

        enumerable1.ShouldHave().SharedProperties().IncludingNestedObjects().EqualTo(enumerable2);

        //Test 'TestClass.MinimalExample2' failed: System.InvalidOperationException : Please specify some properties to include in the comparison.
        //    at FluentAssertions.Assertions.PropertyEqualityValidator.Validate(UniqueObjectTracker tracker, String parentPropertyName)
        //    at FluentAssertions.Assertions.PropertyEqualityValidator.Validate()
        //    at FluentAssertions.Assertions.PropertyAssertions`1.EqualTo(Object otherObject, String reason, Object[] reasonArgs)
        //    at FluentAssertions.Assertions.PropertyAssertions`1.EqualTo(Object otherObject)
        //    MiscAssertions.cs(52,0): at TestClass.MinimalExample2()
    }
}

questionAnswers(4)

yourAnswerToTheQuestion