views:

103

answers:

2

I'm trying to write a Compare method to compare properties in some POCOs using Reflection to ensure that they've been persisted to the database correctly. For example, let's say I have this POCO:

public class NoahsArk
{
    public string Owner { get; set; }
    public ICollection<Animal> Animals { get; set; }
}

What I want to do is this:

[Test]
public class Saves_Correctly_To_Database()
{
    var noahsArk = new NoahsArk { // some setup code here };
    db.Save(noahsArk);
    var dbNoahsArk = db.Get<NoahsArk>(noahsArk.Id);

    Assert.That(Compare(noahsArk, dbNoahsArk), Is.True);
}

The ORM I'm using is NHibernate. My Compare method looks like this so far:

public static bool EqualsProperties<T>(this T x, T y)
{
    var xType = x.GetType();

    foreach (var property in xType.GetProperties())
    {
        if (property.GetValue(x, null).Implements(typeof(ICollection<>)))
        {
            var xValue = property.GetValue(x, null) as ICollection<T>;
            var yValue = property.GetValue(y, null) as ICollection<T>;
        }

Object.Implements() is an extension method I wrote to determine if a type implements an interface. As you can see, the method is incomplete. The problem I'm running into is that when I use property.GetValue(x, null), it returns an object, and I don't know how to cast it into its specific generic ICollection type. I need to be able to do this so I can use LINQ to do a x.Contains(y) to compare the two collections for equality. Any idea on how to do this?

P.S. I tried using Compare .NET Objects, but it's giving me a null reference exception somewhere deep within NHibernate. It doesn't properly handle how NHibernate proxies the ICollection for lazy loading. To make matters worse, NHibernate modifies the POCO to support lazy-loading, but this is all done at runtime. In the source code, it looks like you're just working with a regular ICollection, but NHibernate changes this to NHibernate.Collections.Generic.PersistentSet at runtime, and this is what's causing the comparer to fail.

+2  A: 

Your question is a bit confusing because you don't need the type parameter T in the declaration of your EqualsProperties method. You just need

public static bool EqualsProperties(this object x, object y)

You then go on to use the same parameter T to cast properties of x and y to ICollection<T>; however, the objects in these collections obviously may have a different type than x and y.

Now to answer your question: you don't need to cast to the correct generic type to use the LINQ Contains method. You can do something like this:

xValue = property.GetValue(x, null);
yValue = property.GetValue(y, null);
if (typeof(IEnumerable).IsInstanceOf(x))
{
   IEnumerable<object> xEnumerable = (x as IEnumerable).Cast<object>();
   IEnumerable<object> yEnumerable = (y as IEnumerable).Cast<object>();
   // use any LINQ method you like now
}

You should also make sure you use the LINQ overloads that take an equality comparer, as your domain objects obviously do not override the Equals method themselves. Otherwise you wouldn't be writing this unit testing code to compare them.

Wim Coenen
Thanks for your answer. My thinking behind using the type parameter T in the declaration was that this serves as an implicit first check in that you have to pass two objects of the same type, otherwise the compiler will say that it can't implicitly convert one of them to type T.
Daniel T.
A: 

Sharp architecture framework use attribute to decor properties which should be taken into the equals method. See the source code of DomainSignatureAttribute class and EntityWithTypedId<>.Equals method.

Petr Felzmann