0

In our system we have our domain which is just a bunch of bog standard related poco's, and we use NHibernate with all the lazy loading etc to hydrate and persist the objects data

Lets say I have the structure in the domain. (This is a pure example I created on the fly)

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public int Id { get; set; }
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Postcode { get; set; }
    public Region Region { get; set; }
}

public class Region
{
    public int Id { get; set; }
    public string Name { get; set; }
    public CountryCode CountryCode { get; set; }
}

public class CountryCode
{
    public string Name { get; set; }
    public string Code { get; set; }
    public CostCode CostCode { get; set; }
}

public class CostCode
{
    public string Name { get; set; }
    public string Description { get; set; }
}

Now say the Customer wants to get the Name of the CostCode.Name value for itself, would it be better to have a method in Customer that does this

    public string GetCountryCostName()
    {
        return Address.Region.CountryCode.CostCode.Name; 
    }

or would it be better, to do it so Customer and the other objects have functions to get the information as such

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }

    public string GetCountryCostName()
    {
        return Address.GetCountryCostCodeName();
    }
}

public class Address
{
    public int Id { get; set; }
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Postcode { get; set; }
    public Region Region { get; set; }

    public string GetCountryCostCodeName()
    {
        return Region.GetCountryCostCodeName();
    }
}

public class Region
{
    public int Id { get; set; }
    public string Name { get; set; }
    public CountryCode Country { get; set; }

    public string GetCountryCostCodeName()
    {
        return Country.GetCostCodeName();
    }

}

public class CountryCode
{
    public string Name { get; set; }
    public string Code { get; set; }
    public CostCode CostCode { get; set; }

    public string GetCostCodeName()
    {
        return CostCode.Name;
    }
}

public class CostCode
{
    public string Name { get; set; }
    public string Description { get; set; }
}

now with the first way, Nhibernate will create a single sql to hydrate all the Proxies it creates, but the second way if I am correct in how lazy loading works with NHibernate it will create lots of smaller sql statements as function is accessed. So which way would be the best approach from and OO design and performance points of view?

regards

CD

  • I don't think they'd be different. Have you looked at the results in a profiler? – Andrew Whitaker Oct 17 '13 at 12:47
  • I might be wrong, but I would have said they are the same in terms of SQL. – jbl Oct 17 '13 at 12:50
  • For me it's 6 or half dozen - I prefer the property chaining. – James Oct 17 '13 at 12:51
  • Maybe a real NH query with join/eager fetching would hydrate the whole chain with one SQL query – jbl Oct 17 '13 at 12:59
  • I am going to try running it through SQL Profiler to see what SQL gets created. Ill post the answers for that shortly for that. – Mr Cecil Dooberry Oct 17 '13 at 13:03
  • i would guess 2 sql because countrycode and costcode have no id and are probably components. I prefer the dot notation – Firo Oct 17 '13 at 13:16
  • Looks like the SQL it generates is the same, or at very least similar. So no performance difference there which is cool. But just to throw a slight spin on things, with the dot notation way, what would you do if Country Property was null in Region instance? I am assuming the only way would be to handle the exception and return null. – Mr Cecil Dooberry Oct 17 '13 at 13:33

1 Answers1

2

From a SQL and a "how the code gets executed" standpoint, the two approaches are identical. NHibernate wouldn't create a special override for a method that contained Address.Region.CountryCode.CostCode.Name. That would require some pretty complicated code introspection on NHibernate's part. Instead, each of the many-to-one properties is set with a lazy loading proxy object. Whenever you access a non-id property on the proxy object, NHibernate will ensure that the object has been initialized from the database.

From a design standpoint, what you are asking about is pretty much the essence of the Law of Demeter. The Law of Demeter would say that the second option, where each class only talks to its immediate neighbor, is better. However, there are pros and cons on each side of the issue. Read the Wikipedia article for more details.

I think the main point is to minimize the amount of code that would be affected by a given change. If something about how you access that value has to change in the middle of that chain, then you only have to change half of the classes. However, let's suppose Customer is the only class that actually needs this information. In that case, I think it would be better to go with the first option, ignoring the Law of Demeter. Yes, you're giving Customer deep knowledge about the rest of the data structure, but if that structure changes, you only have to change that one method in Customer, leaving the rest alone.

For null values, especially if the database allows null for those values, you don't want to leave that to exception handling. It would be better to do this for option #1:

public virtual string GetCountryCostName()
{
    if (Address == null || Address.Region == null || Address.Region.CountryCode == null || Address.Region.CountryCode.CostCode == null)
        return null;
    return Address.Region.CountryCode.CostCode.Name; 
}

And for option #2:

public virtual string GetCostCodeName()
{
    if (Address == null)
        return null;
    return Address.GetCostCodeName();
}

// etc...

Now, if all of those relationships are required to be NOT NULL and enforced by foreign keys in the database, you may choose to skip the if (... == null) checks, because encountering a null in that scenario really would be an exceptional case, and that's what exceptions are for.

P.S. I really like this answer.

Community
  • 1
  • 1
Daniel Schilling
  • 4,829
  • 28
  • 60