Using LINQ to write constraints in OCL style v4

Based on much appreciated feedback from my earlier posts, here is my latest version.  This one is more compact and expressive, and the Duplicates function scales roughly linearly so I don't think that any performance has been sacrificed for expressive power.

[ValidationState(ValidationState.Enabled)]

public partial class ExampleElement

{

  [ValidationMethod(ValidationCategories.Menu | ValidationCategories.Save)]

  private void TestExampleElement(ValidationContext context)

  {

    var nonUniquePropNames = this.Properties.Select(p => p.Name).Duplicates();

    nonUniquePropNames.IfAny(pns =>

      context.LogError(String.Format("Non-unique property names: {0}", pns.CommaSeparatedList()), "Error 1", this));

    var nonUniqueSubPropNames = this.Properties.SelectMany(p => p.SubProperties).Select(p => p.Name).Duplicates();

    nonUniqueSubPropNames.IfAny(spns =>

      context.LogError(String.Format("Non-unique sub property names: {0} ", spns.CommaSeparatedList()), "Error 2", this));

  }

}

public static class C

{

  public static HashSet<T> Duplicates<T>(this IEnumerable<T> source)

  {

    HashSet<T> items = new HashSet<T>();

    HashSet<T> duplicates = new HashSet<T>();

    foreach (T item in source)

    {

      if (!items.Add(item))

          duplicates.Add(item);

    }

    return duplicates;

  }

  public static string CommaSeparatedList(this IEnumerable<string> source)

  {

    // source is not empty

    return source.Aggregate((agg, s) => agg + ", " + s);

  }

  public static void IfAny<T>(this IEnumerable<T> source, Action<IEnumerable<T>> act)

  {

      if (source.Count() > 0) act(source);

  }

}