Generic DTO Assembler

Playing around with incorporating Data Transfer Objects into my project, I figure I can use a touch of reflection to manage most assembler cases which are called from my service layer. May be a tad slower, but worth the savings for coding a million assembler classes. My base assembler has some basic methods for mapping simple properties to my entities suchas :

using System;

using System.Collections.Generic;

using System.Text;

using Cei.eMerge.Core.Domain;

using Cei.eMerge.Core.DaoInterfaces;

using Cei.eMerge.DTO;

using Cei.eMerge.Data;

using Cei.eMerge.Data.NHibernate;

using System.Reflection;

namespace Cei.eMerge.DomainFacade.Assemblers

{

internal abstract class DTOAssemblerBase

{

public DTOAssemblerBase() { }

private IDaoFactory _daoFactory;

///

/// Wires up factory to NHibernate Dao strategy for hydration of objects. This can be

/// changed later to be a different provider.

///

public IDaoFactory DaoFactory

{

get

{

if (_daoFactory == null)

{

_daoFactory = new NHibernateDaoFactory();

}

return _daoFactory;

}

}

protected virtual void MapEntityToDto(TEntity entity,DTOBase dto) where TEntity:DomainEntityBase

{

PropertyInfo[] props = dto.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

foreach (PropertyInfo info in props)

{

PropertyInfo entityProp = entity.GetType().GetProperty(info.Name);

if (entityProp != null)

{

info.SetValue(dto, entityProp.GetValue(entity, null),null);

}

}

}

protected virtual void MapDtoToEntity(TEntity entity, DTOBase dto) where TEntity : DomainEntityBase

{

PropertyInfo[] props = entity.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

foreach (PropertyInfo info in props)

{

if (!info.Name.Equals("Id"))

{

PropertyInfo dtoProp = dto.GetType().GetProperty(info.Name);

if (dtoProp != null)

{

info.SetValue(entity, dtoProp.GetValue(dto, null), null);

}

}

}

}

protected virtual void Validate()

{

}

}

}

And then my GenericAssembler to call from my service object is:

using System;

using System.Collections.Generic;

using System.Text;

using Cei.eMerge.Core.Domain;

using Cei.eMerge.DTO;

using Cei.eMerge.Core.DaoInterfaces;

using Cei.eMerge.Data;

using Cei.NHibernateCore;

using System.Reflection;

namespace Cei.eMerge.DomainFacade.Assemblers

{

///

/// Assembler for generic fetching of entities and their corresponding DTO's.

/// Read-only access to entities is only permitted here, so any SaveOrUpdate commands

/// must be executed outside this object.

///

///

/// This operates assuming the following conventions:

///

/// DTO property names are exactly (case-sensitive) matching the Domain Entity property names.

/// The target entity has a corresponding DAO method registered in teh IDaoFactory as

/// GetclassNameDao

///

///

///

///

internal class GenericAssembler<TEntity,TDto> : DTOAssemblerBase where TEntity:DomainEntityBase,new()

where TDto:DTOBase,new()

{

internal GenericAssembler() { }

public TDto GetDTO(TEntity entity)

{

TDto dto = new TDto();

MapEntityToDto(entity, dto);

return dto;

}

///

/// Mock service locator using reflection. Since naming conventions are assumed (GetEntityDao()) we

/// can safely avoid a formal configuration framework for this...AH RUBY!

///

///

private MethodInfo GetDaoMethod()

{

string typeName = typeof(TEntity).UnderlyingSystemType.Name;

string getMethod = "Get" + typeName + "Dao";

MethodInfo method = DaoFactory.GetType().GetMethod(getMethod);

if(method!=null)

{

return method;

}

string MISSING_DAO =

"A Dao corresponding to {0} (a method named '{1}') could not be found.";

throw InvalidOperationException(string.Format(MISSING_DAO,typeName,getMethod));

}

public TEntity CreateEntity(TDto dto)

{

MethodInfo method = GetDaoMethod();

IDaoBase<TEntity, int> dao = (IDaoBase<TEntity, int>)method.Invoke(DaoFactory, null);

TEntity entity = new TEntity();

MapDtoToEntity(entity, dto);

return entity;

}

public TEntity UpdateEntity(int id, TDto dto)

{

MethodInfo method = GetDaoMethod();

IDaoBase<TEntity, int> dao = (IDaoBase<TEntity, int>)method.Invoke(DaoFactory, null);

TEntity entity = dao.GetById(id, true);

MapDtoToEntity(entity, dto);

return entity;

}

}

}

This article is part of the GWB Archives. Original Author: Mike Nichols

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.