views:

47

answers:

2

Hi,

I would like to "extend" my domain classes without having to add data to the domain classes themselves. Concider I have the following class:

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

And I have the following table in the database:

tblPersons
---------------
Id integer
Name varchar(50)
CreatedBy varchar(50)
CreatedDate datetime

So I don't want to add "CreatedBy" and "CreatedDate" to my domain class, because this has nothing to do with the actual domain itself...

Would it be possible to get this data whenever I load an entity? I would like to use it like this:

Person person = session.Load(1);

person.CreatedBy(); <-- CreatedBy is an Extension function
person.CreatedDate(); <-- CreatedDate is an Extension function

Can anyone point me in which direction to go in order to implement this?

I have thought about the following possibilities:

  • Implementa a custom ProxyFactory, where I inject a custom "interface" such as IUpdateable, howver it seems like NHibernate doesn't create the proxies consistently, sometimes it loads a my "proxy class" class, and sometimes it loads the "normal class":

Person person = session.Load(2); //this will load my Proxy class of Person just fine

Address address = person.Address; //Somehow this doesn't load a Proxy for Address, but instead loads it normally - seems like it's evaluating "ImmediateLoad", which doesn't load proxies, due to lazy loading... not sure how to make this behave as I want.

  • Using a custom PropertyAccessor, I have read something about this - but it seems I must actually map this to a property that EXITS on the domain class... so that wouldn't work right?

  • Just as NHibernate "injects" code to the runtime when creating the Proxy classes - perhaps I could do the same but inject the "interface" to the original Person class instead?

Would appreciate any help on this, thank you! :)

+1  A: 

You can easily do this using a base class or a component mapping. I would not use extension methods for this purpose. I use a base class:

public abstract class Auditable : IAuditable
{
    public virtual string CreatedBy { get; set; }
    public virtual DateTime CreatedDate { get; set; }
}

public class Person : Auditable {}

Fluent mapping:

public class AuditableClassMap<T> : ClassMap<T> where T: IAuditable
{
    public AuditableClassMap()
    {
        Map(x => x.CreatedBy);
        Map(x => x.CreatedDate);
    }
}

public class PersonMap : AuditableClassMap<Person> {}

If you are adamant about keeping audit properties out of your classes you could map the audit properties as a component.

Jamie Ide
Nice, thank you - I really can't change the domain classes, however as you say - I wouldn't mind mapping it some other way. Could you explain how mapping the audit properties as a component would help?I thought that "component mapping" was just a way of grouping flattened database columns into a deeper structure within the domain classes? - I will read up on components in Nhibernate.Thanks for the help! :)
Bilsa
If you can't change the domain classes then I don't see any reasonable way to implement this.
Jamie Ide
A: 

Here is one idea. I've never implemented this, so take it with a grain of salt until you've tested it.

Create a different class that encapsulates the audit data for Person -- PersonCreation or something.

Give it an identifier, a created date, and created-by property, and a property for the Person id (I see no need actually reference the Person, unless the identifier is non-public, in which case you may want a WeakReference so you don't keep every Person instance in memory for the life of the application).

You'll need to create a mapping for NHibernate to get PersonCreation objects from the Person table.

From here, you could simply have the extension methods fetch data when called. This may or may not be reasonable depending on your usage. You'll have to create a new session every time or synchronize a static session.

Or…

In the static class that contains your CreatedBy() and CreatedDate() extension methods, create a static IDictionary<int, PersonCreation> to hold the details for each Person. Since creation data is presumably immutable, we don't really have to worry about this becoming stale.

You'll want to batch queries for the PersonCreation with your Person queries. For example, you could do something like:

var creation = session.CreateCriteria() .Add(Restrictions.Eq("PersonId", 1)) .Future();

var person = session.CreateCriteria() .Add(Restrictions.IdEq(1)) .UniqueResult();

By calling Future<T>(), you're telling NHibernate not to execute that query until the session is already going to database anyway.

Once you get results, you can take the first creation result and add it to the dictionary (so the extension methods have access to it), and return the person.

When you call person.CreatedDate(), the method can just pull the data from the dictionary using the id of the passed Person paramater, or the Person itself.

Jay
Hey Jay, thanks! - I will defenitely play around with this idea, Thanks a bunch! :)
Bilsa