Logo

NHibernate

The object-relational mapper for .NET

How to

This page is converted from the old nhforge.org Wiki. First published by: awgneo on 01-28-2012, Last revision by: awgneo on 01-28-2012

WCF + NHibernate: Entity Serialization

I found many examples online covering how to serialize NHibernate entities for use over a WCF channel, while still using lazy and eager loading. In my application, my database models and my SOA models needed to be exactly the same; therefore, I wanted to avoid the use of DTOs. However, none of the examples I found quite worked for me.

Examples I found included:

 

 

My custom object graph walker (I hacked this out of some integrated code so it may need some tweaking):

 

using System;
using System.Collections.Generic;
using System.Collections;
using System.Reflection;
using System.Data;
using System.Linq;
using System.Configuration;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using FluentNHibernate.Automapping;
using Radiant.RQS.Services.Types;
using Radiant.RQS.Services.Core.Conventions;
using Radiant.RQS.Superbank;
using NHibernate.Proxy;
using NHibernate.Collection;
using NHibernate.Metadata;
using NHibernate.Type;
 
namespace Radiant.RQS.Services.Core
{
    public static class Resolver
    {
        public static List<T> ResolveList<T>(List<T> entityList, ISession session) where T : class
        {
            // create a resolved entities list for peace and sharing
            List<Object> resolvedEntities = new List<Object>();
            // loop over elements
            for (int entityListIndex = 0; entityListIndex < entityList.Count; entityListIndex++)
                entityList[entityListIndex] = Resolve<T>(entityList[entityListIndex], session, resolvedEntities);
 
            // done
            return entityList;
        }
 
        public static T[] ResolveArray<T>(T[] entityArray, ISession session) where T : class
        {
            // create a resolved entities list for peace and sharing
            List<Object> resolvedEntities = new List<Object>();
            // loop over elements
            for (int entityArrayIndex = 0; entityArrayIndex < entityArray.Length; entityArrayIndex++)
                entityArray[entityArrayIndex] = Resolve<T>(entityArray[entityArrayIndex], session, resolvedEntities);
 
            // done
            return entityArray;
        }
 
        public static T Resolve<T>(T entity, ISession session) where T : class
        {
            // forward to resolver
            return Resolve<T>(entity, session, new List<Object>());
        }
 
        private static T Resolve<T>(T entity, ISession session, List<Object> resolvedEntities) where T : class
        {
            // CHECKS //
 
            // if the entity is null, just skip it
            if (entity == null)
                return default(T);
            // if we have already resolved it, return that
            if (resolvedEntities.Contains(entity))
                return entity;
 
            // RESOLVE ENTITY //
 
            T resolvedEntity = default(T);
            // now lets go ahead and make sure everything is unproxied
            try { resolvedEntity = (T)session.GetSessionImplementation().PersistenceContext.Unproxy(entity); }
            catch (Exception ex) { return default(T); }
            // add entity to the list of resolved entities
            resolvedEntities.Add(resolvedEntity);
 
            // GET TYPE INFO //
 
            IClassMetadata entityMetadata = null;
            // get the entity type
            Type entityType = entity.GetType();
            // get the entity meta data from the type
            try { entityMetadata = session.SessionFactory.GetClassMetadata(entityType); }
            catch (Exception ex) { return default(T); }
 
            // PERFORM PROPERTY DIVE //
 
            String propertyName;
            Object propertyValue;
            Type propertyListType;
            IType entityPropertyType = null;
            Type propertyListInternalType;
            // get properties for this object
            PropertyInfo[] propertyInfos = entityType.GetProperties();
            // loop over source properties & compare
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                // get property name
                propertyName = propertyInfo.Name;
                // get property type
                try { entityPropertyType = entityMetadata.GetPropertyType(propertyName); }
                catch (Exception ex) { continue; }
                // get property value
                propertyValue = propertyInfo.GetValue(entity, null);
                // these are not the good kind of bags :P
                if (entityPropertyType.IsCollectionType)
                {
                    // first get the property list's internal type
                    propertyListInternalType = propertyInfo.PropertyType.GetGenericArguments()[0];
                    // create new array type based on the internal type
                    propertyListType = typeof(List<>).MakeGenericType(propertyListInternalType);
                    // create a new property list of the internal type
                    IList propertyList = (IList)Activator.CreateInstance(propertyListType);
                    // set the property list in the resolved object
                    propertyInfo.SetValue(resolvedEntity, propertyList, null);
                    // get the enumerator for this property value
                    IEnumerator enumerator = ((IEnumerable)propertyValue).GetEnumerator();
                    // loop over items to also perform resolution
                    while (enumerator.MoveNext())
                        propertyList.Add(Resolve(enumerator.Current, session, resolvedEntities));
                }
                // destroy hibernate proxies
                else if (entityPropertyType.IsEntityType)
                {
                    // set the property of the resolved entity to the child beneath us
                    propertyInfo.SetValue(resolvedEntity, Resolve(propertyValue, session, resolvedEntities), null);
                }
            }
 
            // return the resolved entity :)
            return resolvedEntity;
        }
    }
}

This works FLAWLESSLY for me. When I lazy load or eager load sub-entities before calling Resolve() it perfectly serializes only what I want and does not attempt to lazy load the entire DB.

Example:

// get the computer query

var computerQuery =

from c in context.Session.Query<Types.Computer>()

where c.ID == computerID

select c;

 

// return computer

return Resolver.Resolve<Types.Computer>(computerQuery.SingleOrDefault(), session);


Let me know if this doesn't work for you :).


Thanks,
Alex G. (awgneo@xbetanet.com)

 

© NHibernate Community 2024