views:

1470

answers:

6

The current system that I am working on makes use of Castle Activerecord to provide ORM (Object Relational Mapping) between the Domain objects and the database. This is all well and good and at most times actually works well!

The problem comes about with Castle Activerecords support for asynchronous execution, well, more specifically the SessionScope that manages the session that objects belong to. Long story short, bad stuff happens!

We are therefore looking for a way to easily convert (think automagically) from the Domain objects (who know that a DB exists and care) to the DTO object (who know nothing about the DB and care not for sessions, mapping attributes or all thing ORM).

Does anyone have suggestions on doing this. For the start I am looking for a basic One to One mapping of object. Domain object Person will be mapped to say PersonDTO. I do not want to do this manually since it is a waste.

Obviously reflection comes to mind, but I am hoping with some of the better IT knowledge floating around this site that "cooler" will be suggested.

Oh, I am working in C#, the ORM objects as said before a mapped with Castle ActiveRecord.


Example code:

By @ajmastrean's request I have linked to an example that I have (badly) mocked together. The example has a capture form, capture form controller, domain objects, activerecord repository and an async helper. It is slightly big (3MB) because I included the ActiveRecored dll's needed to get it running. You will need to create a database called ActiveRecordAsync on your local machine or just change the .config file.

Basic details of example:

The Capture Form

The capture form has a reference to the contoller

private CompanyCaptureController MyController { get; set; }

On initialise of the form it calls MyController.Load() private void InitForm () { MyController = new CompanyCaptureController(this); MyController.Load(); } This will return back to a method called LoadComplete()

public void LoadCompleted (Company loadCompany)
{
    _context.Post(delegate
    {
         CurrentItem = loadCompany;
         bindingSource.DataSource = CurrentItem;
         bindingSource.ResetCurrentItem();
         //TOTO: This line will thow the exception since the session scope used to fetch loadCompany is now gone.
         grdEmployees.DataSource = loadCompany.Employees;
         }, null);
    }
}

this is where the "bad stuff" occurs, since we are using the child list of Company that is set as Lazy load.

The Controller

The controller has a Load method that was called from the form, it then calls the Asyc helper to asynchronously call the LoadCompany method and then return to the Capture form's LoadComplete method.

public void Load ()
{
    new AsyncListLoad<Company>().BeginLoad(LoadCompany, Form.LoadCompleted);
}

The LoadCompany() method simply makes use of the Repository to find a know company.

public Company LoadCompany()
{
    return ActiveRecordRepository<Company>.Find(Setup.company.Identifier);
}

The rest of the example is rather generic, it has two domain classes which inherit from a base class, a setup file to instert some data and the repository to provide the ActiveRecordMediator abilities.

A: 

@FryHard

Could you post your problem with NHibernate's flushing mechanism too? I think the community would like to take a crack at it and keep you using ORM!

Anthony Mastrean
+7  A: 

I solved a problem very similar to this where I copied the data out of a lot of older web service contracts into WCF data contracts. I created a number of methods that had signatures like this:

public static T ChangeType<S, T>(this S source) where T : class, new()

The first time this method (or any of the other overloads) executes for two types, it looks at the properties of each type, and decides which ones exist in both based on name and type. It takes this 'member intersection' and uses the DynamicMethod class to emil the IL to copy the source type to the target type, then it caches the resulting delegate in a threadsafe static dictionary.

Once the delegate is created, it's obscenely fast and I have provided other overloads to pass in a delegate to copy over properties that don't match the intersection criteria:

public static T ChangeType<S, T>(this S source, Action<S, T> additionalOperations) where T : class, new()

... so you could do this for your Person to PersonDTO example:

Person p = new Person( /* set whatever */);
PersonDTO = p.ChangeType<Person, PersonDTO>();

And any properties on both Person and PersonDTO (again, that have the same name and type) would be copied by a runtime emitted method and any subsequent calls would not have to be emitted, but would reuse the same emitted code for those types in that order (i.e. copying PersonDTO to Person would also incur a hit to emit the code).

It's too much code to post, but if you are interested I will make the effort to upload a sample to SkyDrive and post the link here.

Richard

ZeroBugBounce
This would be a huge help to me - would you mind posting it?
Slee
A: 

My apologies for not really putting the details in here, but a basic OO approach would be to make the DTO a member of the ActiveRecord class and have the ActiveRecord delegate the accessors and mutators to the DTO. You could use code generation or refactoring tools to build the DTO classes pretty quickly from the AcitveRecord classes.

MattMcKnight
A: 

Actually I got totally confussed now. Because you are saying: "We are therefore looking for a way to easily convert (think automagically) from the Domain objects (who know that a DB exists and care) to the DTO object (who know nothing about the DB and care not for sessions, mapping attributes or all thing ORM)."

  1. Domain objects know and care about DB? Isn't that the whole point of domain objects to contain business logic ONLY and be totally unaware of DB and ORM?....You HAVE to have these objects? You just need to FIX them if they contain all that stuff...that's why I am a bit confused how DTO's come into picture

  2. Could you provide more details on what problems you're facing with lazy loading?

badbadboy
+3  A: 

You should automapper that I've blogged about here:

http://januszstabik.blogspot.com/2010/04/automatically-map-your-heavyweight-orm.html#links

As long as the properties are named the same on both your objects automapper will handle it.

januszstabik
+3  A: 

use ValueInjecter, with it you can map anything to anything e.g.

  • object <-> object
  • object <-> Form/WebForm
  • DataReader -> object

and it has cool features like: flattening and unflattening

the download contains lots of samples

Omu
+1 - This looks really promising
David Robbins