views:

62

answers:

6

I've seen 2 types of entities, like this:

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

and like this:

public class Person
{
    public int Id {get;set;}
    public string Name {get;set;}
    public int CountryId {get;set;}
}

I think that the 2nd approach is more lightweight, and you get related data only if you needed;
which one do you think is better?

+1  A: 

It depends what you want. If you only want to get the Country's ID then go for the second option. If you actually want to make use of navigation properties and/or lazy loading, then go for the first option.

Personally, I use Entity Framework and combine options one and two:

public class Person
{
    public int Id {get;set;}
    public string Name {get;set;}
    public int CountryId {get;set;}
    public Country Country {get;set;}
}

So I have a choice when it comes to returning data from my repositories. This also means that when I come to save, I can just populate the actual value type properties, instead of having to load the country object and assign it to the person.

GenericTypeTea
+1  A: 

Taken at face value, the first is an example of a rich domain model, and the second is a data driven approach. Allowing rich domain models is one of the main benefits of ORM.

The only reason I would include the CountryId (either in place of the Country, or in addition to it) would be for optimization for some very specific performance problem. Even then I would think twice. And optimization is something you shouldn't be thinking about too much at the initial design stage. Whats wrong with Person.Country.Id? (Assuming you need the id at all, and it's not just infrastructure).

If you are looking at this from any other angle than performance optimisation, then you are probably taking the wrong approach by including 'foreign keys' in your domain model. I had the same problem when first using NHibernate, coming from an ADO type background. I would almost certainly go with the first example.

UpTheCreek
+1  A: 

There are two considerations, Platforms and Traffic, outlined below...

All in Microsoft Platform

In multi tier solutions, where end client is Silverlight and you are going to share your generated code via RIA services, or you have WPF client with WCF RIA services, first solution gives you better design.

Non Microsoft End client

If your end client is non microsoft client like Flex/Flash, Java or any ajax based smart clients, then first model will not be useful as it needs track itself (self tracking objects). Second model is preferred here.

Low Traffic applications

If network traffic is not much of issue and your design of software is more important, or you have highly scalable middle tires for caching etc, like App Fabric etc, first solution good one which will give you better design.

High Traffic applications

First model will serialize more data then necessary, and that can be a real performance issue in high traffic applications. So in that case, second model will work better because only if user is requesting more data of reference, then only it will be loaded.

This is quite a tradeoff issue between "Better Design" vs "Better Performance", and it needs to be selected based on parameters mentioned above and there can be more parameters depending upon complexity of project, team size, documentation and more.

Akash Kava
+1  A: 

Good question! For me

public List<Person> GetPersonsLivingIn(int countryId) {
  return ObjectContext.Persons.Where(x => x.CountryId == countryId).ToList();
}

just looks like it works that way without knowing about all the magic (leaky) abstractions that may be present in the ORM that would make x => x.Country == country work. I came from Linq2Sql where I had some problems with the first one when passing around objects created in different object contexts.

But I would do as GenericTypeTea said and include both the id and the navigation property. After all, you'll want a navigable object graph at some point. And that way you can still make

public List<Person> GetPersonsLivingIn(Country country) {
  return ObjectContext.Persons.Where(x => x.CountryId == country.CountryId).ToList();
}

which has a more OO feeling interface, but still looks like it would work without magic.

DonAndre
That thing you call "magic" is the heart of an ORM. Not using it creates an anemic domain model, which is an ORM anti-pattern (IOW, why use an ORM if you don't use its features?)
Diego Mijelshon
Thank you for your comment. From my point of view, exposing the entity objects in the method interface creates a problem for the method developer: Does the entity come from the same object context? Can I reattach it to my own? What part of the object graph is loaded in the entity? Can I lazy-load other parts of the entity / is the entity's object context still alive? ORM adds a lot of complexity and I've burnt my fingers several times on things that I thought would work, but didn't.
DonAndre
@DonAndre, all the problems you describe derive from wrong session management. I'm not saying it's absolutely intuitive for everyone to grasp the underlying concepts, but a car analogy would be in place.
Diego Mijelshon
+1  A: 

Except in some weird edge cases, there are no good reasons for the second design.

They are both equally lightweight (references are lazily loaded by default), but the second one doesn't give you navigational capabilities, which restricts and complicates your queries.

Diego Mijelshon
+1  A: 

STOP!

In NHibernate, there is NO need to specify the foreign key in your domain model, not even for performance reasons.

Assuming you have lazy loading enabled (it's enabled by default), calling:

int countryId = person.Country.Id;

...won't incur a database hit to retrieve the Country entity. NHibernate will return a dynamic proxy of your Customer, not an actual Customer. Because of the proxy, a database hit will only occur on first access to a Property on your Customer entity, but NHibernate is smart enough to realise that 'person.Country.Id' is the same as accessing the customer ID foreign key in your Person table, which gets loaded in anyway.

However, the following code:

string countryName = person.Country.Name;

...will hit the database, the call to the 'Name' property will load the entire Customer instance.

This behavior assumes you have set-up your mapping like so:

<many-to-one name="Country" class="Country" column="Country_ID" lazy="proxy" />

(note that lazy="proxy" is the default).

Simply put, there is no need to map foreign keys in your domain model with NHibernate.

Sunday Ironfoot