3

I need to create DTOs from NHibernate POCO objects. The problem is that the POCO objects contain dynamic proxies, which should not be copied to the DTO. I eager load all the collections and references I need to transfer in advance, I don't want NHibernate to start loading referenced collections which I did not load in advance.

Several similar questions on SO received answers which either:

  1. Suggest Session.GetSessionImplementation().PersistenceContext.Unproxy();
  2. Suggest turning off Lazy Loading.

In my case the first suggestion is irrelevant, as according to my understanding it causes eager loading to replace the proxies. In reality, it doesn't even work - it doesn't remove the proxies in my objects. (Any explanation why?)

The second suggestion, turning off lazy loading seems to cause all references and collections to eager load, basically loading the entire DB. My expectation was that if lazy loading is off, and I have not requested a collection, it will not be loaded. (Am I correct that NHibernate offers no such option?)

I am using NHibernate 3.3.1 with fluent configuration.

To reiterate my main question, I need to create DTOs clean of proxies, copied from POCOs which contain proxies, and I don't want to load the data behind those proxies.

Any helpful suggestion which includes example code and automates the process with ValueInjecter / AutoMapper will be immensely helpful.

Edit #1:

Following Roger Alsing's suggestion to use projections, I realized that what I'm actually looking for is a ValueInjecter-like convention based mapping. Here is why. Initially, my DTOs will be defined the same as the model's POCOs. This is due to a large code base which depends on the existing POCOs being transferred on the client-side project.

Using projections, I will have to specify which subset of fields have to be copied, and this subset may be different in each context (as, ideally, a DTO would differ). This will mean a lot of new code introduced to the server side, when there should be the second option.

Using ValueInjecter, I will be able to populate the DTOs by convention in one call, without writing specific projections, or having to maintain those into the future. That is, if I am able to have ValueInjecter ignore Proxy objects.

Given that using projections is a good but not ideal solution in my situation, is there a way to configure something like ValueInjecter to copy POCOs without copying proxies or triggering eager/lazy loads on copy?

Roman
  • 957
  • 8
  • 17
  • 1
    Can't you just select the data you need through a projection? in EF4 you can do things like ... from c in customers select new CustomerDTO { Name = c.Name , Orders = c.Orders.Select (o => new OrderDTO {...} ) }; this way you don't need to resort to reflection magic or any other fancy stuff. and the query fetches exactly what you need in one go. – Roger Johansson Aug 05 '12 at 13:27
  • I actually switched to NH from EF, in EF I never even had the problem of non-serializing proxies... To the point, your suggestion is valid. NH uses IQueryable as well hence Select projections are possible, as would using an AutoMapper and specifying manually which fields to copy. I still wonder if there is a way to do this "automatically", i.e. a ValueInjecter/AutoMapper configuration which will "skip" dynamic proxies? – Roman Aug 05 '12 at 13:49
  • Roger I think you should post your comment as an answer, I want to upvote it and assuming I don't get an answer to the updated question, I will mark yours as accepted. – Roman Aug 05 '12 at 14:26
  • Ty, I've posted the comment as an answer now. – Roger Johansson Aug 05 '12 at 15:08

4 Answers4

5

I solve this by selecting DTO's as projections using Linq or whatever query language the O/R Mapper may have.

e.g.

return from c in customers 
       select new CustomerDTO 
       {
          Name = c.Name , 
          Orders = c.Orders.Select (o => new OrderDTO {...} ) 
       };

This way, you don't need to resort to reflection magic or any other fancy stuff. And the query fetches exactly what you need in one go, thus, this is usually much more efficient than fetching entities and then transforming them to DTO's in mem. (it can be less efficient in some cases incase the resulting SQL query contains extra joins for whatever reason..)

Roger Johansson
  • 22,764
  • 18
  • 97
  • 193
  • 1
    Why did someone downvote this answer? It was in fact a correct answer to my original question. – Roman Aug 07 '12 at 11:03
2

I'm using the following ValueResolver with AutoMapper:

/// <summary>
/// ValueResolver that will set NHibernate proxy objects to null, instead of triggering a lazy load of the object
/// </summary>
public class IgnoreNHibernateProxyValueResolver : IValueResolver
{
    public ResolutionResult Resolve(ResolutionResult source)
    {
        var prop = source.Type.GetProperty(source.Context.MemberName).GetValue(source.Value, null);
        var proxy = prop as INHibernateProxy;
        if (proxy != null && proxy.HibernateLazyInitializer.IsUninitialized)
        {
            return source.Ignore();
        }

        return source.New(prop);
    }
}
RyanHerbert
  • 88
  • 1
  • 6
  • Your code is always getting proxy = null despite of using .Not.LazyLoad() or .LazyLoad(), do you have any idea? Thanks. – Edgar Apr 19 '14 at 07:32
  • The proxy variable comes from "prop as INHibernateProxy". If the type of the property doesn't implement INHibernateProxy, it will return null. I would try stopping the code in the debugger and seeing what type it is reporting for prop. – RyanHerbert Apr 21 '14 at 14:51
1

Take a look on Projections in Introduction to QueryOver in NH 3.0

CatSummary summaryDto = null;
IList<CatSummary> catReport =
    session.QueryOver<Cat>()
        .SelectList(list => list
            .SelectGroup(c => c.Name).WithAlias(() => summaryDto.Name)
            .SelectAvg(c => c.Age).WithAlias(() => summaryDto.AverageAge))
        .TransformUsing(Transformers.AliasToBean<CatSummary>())
        .List<CatSummary>();
GSerjo
  • 4,725
  • 1
  • 36
  • 55
  • Following Roger Alsing's comment above, I do consider using projections to populate the DTO. However this approach is a bit complex in my situation, I am going to edit the question to explain why I am looking for something like a ValueInjecter-way of populating the DTO. – Roman Aug 05 '12 at 14:13
1

for ValueInjecter solution I recommend using SmartConventionInjection (you need to copy the code from the linked page into your solution)

and after specify a convention that won't touch the proxy properties

here's a start:

public class MapPoco: SmartConventionInjection
{
     protected override bool Match(SmartConventionInfo c)
     {
         return c.SourceProp.Name == c.TargetProp.Name;
     }
}
Omu
  • 69,856
  • 92
  • 277
  • 407
  • OK I followed this path and created a ValueInjection as you suggested. Now my problem is the circular references I have in my entities (i.e. SomeOffice === SomeOffice.Users[0].Office). I am trying to work this out and will write back. – Roman Aug 06 '12 at 16:02
  • Well I seem to be nearing a solution based on SmartConventionInjection (which provides better performance) and lots of custom code of mine. I will post the resulting code once I iron out all the issues. Thank you for the tip. – Roman Aug 07 '12 at 11:02
  • I published the code which strips the proxies: https://github.com/RomansWorks/NHibernate-Unproxify – Roman Sep 13 '12 at 17:31