开发者

NHibernate mapping domain "extension properties"?

开发者 https://www.devze.com 2023-01-09 14:05 出处:网络
I would like to \"extend\" my domain classes without having to add data to the domain classes themselves. Consider I have the following class:

I would like to "extend" my domain classes without having to add data to the domain classes themselves. Consider 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 = 开发者_Go百科session.Load<Person>(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:

  • Implement 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<Person>(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?


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.


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<PersonCreation>()
    .Add(Restrictions.Eq("PersonId", 1))
    .Future<PersonCreation>();

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

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号