We generated DTOs from LLBLGen rather than using T4.
We had a need to create DTOs from an Entity. These aren't technically POCOs because they have a ToEntity()
and FromEntity()
method, but perhaps this will work for you.
We created an IDTO<T>
interface that was then implemented by the DTO classes (one for each entity). Rather than modifying the entity templates, we added DTOExtension
methods that would convert an Entity to a DTO (as well as numerous other helper conversions).
Here are the template files you can use within LLBLGen v2.6. The template designer in version 3 is much easier to use, and you can convert much of this code for version 3 if you need to.
File: entityDTOInterface.template
using System;
using System.ComponentModel;
using System.Collections;
using System.Runtime.Serialization;
using <[RootNamespace]>.HelperClasses;
using <[RootNamespace]>.EntityClasses;
using SD.LLBLGen.Pro.ORMSupportClasses;
namespace <[RootNamespace]>.DTOClasses
{
/// <summary>
/// DTO interface.
/// </summary>
public interface IDTO<T>
{
T ToEntity(T toFill);
IDTO<T> FromEntity(T entityInstance, Hashtable seenObjects, Hashtable parents);
}
}
File: entityDTO.template
using System;
using System.ComponentModel;
using System.Collections;
using System.Runtime.Serialization;
using <[RootNamespace]>.HelperClasses;
using <[RootNamespace]>.EntityClasses;
using SD.LLBLGen.Pro.ORMSupportClasses;
namespace <[RootNamespace]>.DTOClasses
{
<[ UserCodeRegion "AdditionalNamespaces" ]>
// __LLBLGENPRO_USER_CODE_REGION_START AdditionalNamespaces
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
/// <summary>
/// DTO class for the entity '<[CurrentEntityName]>'.
/// </summary>
[Serializable]
public <[If UsePartialClasses]>partial <[EndIf]>class <[CurrentEntityName]>DTO : <[ If IsSubType ]><[ SuperTypeName ]>DTO, <[ EndIf]>IDTO<<[CurrentEntityName]>Entity><[ UserCodeRegion "AdditionalInterfaces" ]>
// __LLBLGENPRO_USER_CODE_REGION_START AdditionalInterfaces
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
{
#region Entity Field Public Properties
<[Foreach EntityField CrLf]> /// <summary>Get or set the <[EntityFieldName]> property that maps to the Entity <[CurrentEntityName]></summary>
public virtual <[If GenerateAsNullableType]><[TypeOfField]>?<[Else]><[TypeOfField]><[EndIf]> <[EntityFieldName]> { get; set; }
<[NextForeach]>
#endregion
#region Related Field Public Properties
<[ Foreach RelatedEntityField CrLf]> /// <summary>Get or set the <[MappedFieldNameRelatedField]> property that maps to the Entity <[CurrentEntityName]>'s <[ MappedFieldNameRelation ]>.<[ RelatedEntityFieldName ]></summary>
public virtual <[If GenerateAsNullableType]><[TypeOfField]>?<[Else]><[TypeOfField]><[EndIf]> <[ MappedFieldNameRelatedField ]> { get; private set; }<[NextForeach]>
#endregion
#region Custom Fields
<[ UserCodeRegion "CustomFieldCode" ]>
// __LLBLGENPRO_USER_CODE_REGION_START CustomFieldCode
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
#endregion
#region Ctors
/// <summary>
/// CTor
/// </summary>
public <[CurrentEntityName]>DTO()
{
}
/// <summary>
/// CTor which initializes the DTO with values from its corresponding entity
/// </summary>
/// <param name="entityInstance">The entity instance which holds the values for this DTO</param>
public <[CurrentEntityName]>DTO(<[CurrentEntityName]>Entity entityInstance) : this(entityInstance, new Hashtable(), new Hashtable()) { }
internal <[CurrentEntityName]>DTO(<[CurrentEntityName]>Entity entityInstance, Hashtable seenObjects, Hashtable parents)<[ If IsSubType ]> : base(entityInstance, seenObjects, parents)<[ EndIf]>
{
FromEntity(entityInstance, seenObjects, parents);
}
#endregion
/// <summary>
/// Creates a <[CurrentEntityName]>DTO object from the given entity.
/// </summary>
public virtual IDTO<<[CurrentEntityName]>Entity> FromEntity(<[CurrentEntityName]>Entity entityInstance, Hashtable seenObjects, Hashtable parents)
{
<[ If IsSubType ]>base.FromEntity(entityInstance, seenObjects, parents);
<[ EndIf]>seenObjects[entityInstance] = this;
parents = new Hashtable(parents);
parents.Add(entityInstance, null);
<[Foreach EntityField CrLf]> this.<[EntityFieldName]> = entityInstance.<[EntityFieldName]>;<[NextForeach]>
<[Foreach RelatedEntityField CrLf]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
this.<[MappedFieldNameRelatedField]> = entityInstance.<[MappedFieldNameRelatedField]>;<[NextForeach]>
<[Foreach RelatedEntity OneToMany CrLf]><[If Not MappedFieldRelationIsHidden]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
<[MappedFieldNameRelation]> = RelatedArray<<[RelatedEntityName]>DTO, <[RelatedEntityName]>Entity>(entityInstance.<[MappedFieldNameRelation]>, seenObjects, parents);<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity ManyToMany CrLf]><[If Not MappedFieldRelationIsHidden]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
<[MappedFieldNameRelation]> = RelatedArray<<[RelatedEntityName]>DTO, <[RelatedEntityName]>Entity>(entityInstance.<[MappedFieldNameRelation]>, seenObjects, parents);<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity ManyToOne CrLf]><[If Not MappedFieldRelationIsHidden]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
<[MappedFieldNameRelation]> = RelatedObject<<[RelatedEntityName]>DTO, <[RelatedEntityName]>Entity>(entityInstance.<[MappedFieldNameRelation]>, seenObjects, parents);//(new <[RelatedEntityName]>DTO(entityInstance.<[MappedFieldNameRelation]>, seenObjects);<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity OneToOne CrLf]><[If Not MappedFieldRelationIsHidden]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
<[MappedFieldNameRelation]> = RelatedObject<<[RelatedEntityName]>DTO, <[RelatedEntityName]>Entity>(entityInstance.<[MappedFieldNameRelation]>, seenObjects, parents);<[EndIf]><[NextForeach]>
return this;
}
<[ If Not IsSubType ]>
/// <summary>
/// Get a collection of DTO objects created from entities related to <[CurrentEntityName]>Entity.
/// It keeps track of entities previously seen to prevent an infinite loop.
/// <summary>
protected virtual T[] RelatedArray<T,U>(EntityCollectionBase<U> entities, Hashtable seenObjects, Hashtable parents) where T : class, IDTO<U>, new() where U : EntityBase
{
if (null == entities)
{
return null;
}
T[] arr = new T[entities.Count];
int i = 0;
foreach (U entity in entities)
{
if (parents.Contains(entity))
{
return null;
}
}
foreach (U entity in entities)
{
if (seenObjects.Contains(entity))
{
arr[i++] = seenObjects[entity] as T;
}
else
{
arr[i++] = new T().FromEntity(entity, seenObjects, parents) as T;
}
}
return arr;
}
/// <summary>
/// Creates a DTO object from the given related <[CurrentEntityName]>Entity entity.
/// This is used to populate a single DTO from a single relation of the <[CurrentEntityName]>Entity.
/// <summary>
protected virtual T RelatedObject<T,U>(U entityInstance, Hashtable seenObjects, Hashtable parents) where T : class, IDTO<U>, new() where U : EntityBase
{
if (null == entityInstance)
{
return null;
}
if (seenObjects.Contains(entityInstance))
{
if (parents.Contains(entityInstance))
{
return null;
}
else
{
return seenObjects[entityInstance] as T;
}
}
return new T().FromEntity(entityInstance, seenObjects, parents) as T;
}
<[ EndIf]>
/// <summary>
/// Get a collection of individual DTO objects from the EntityCollectionBase<<[CurrentEntityName]>Entity> collection.
/// <summary>
public static <[CurrentEntityName]>DTO[] ToDTOArray(EntityCollectionBase<<[CurrentEntityName]>Entity> entities)
{
Hashtable seenObjects = new Hashtable();
<[CurrentEntityName]>DTO[] arr = new <[CurrentEntityName]>DTO[entities.Count];
for (int i = 0; i < entities.Count; i++)
{
arr[i] = new <[CurrentEntityName]>DTO().FromEntity(entities[i], seenObjects, new Hashtable()) as <[CurrentEntityName]>DTO;
}
return arr;
}
/// <summary>
/// Creates a new entity instance and copies over the values of this DTO
/// </summary>
public <[ If IsSubType ]>override <[SuperTypeName]><[ Else]>virtual <[CurrentEntityName]><[ EndIf]>Entity ToEntity()
{
return ToEntity(new <[CurrentEntityName]>Entity());
}
/// <summary>
/// Copies over the values of this DTO into the entity passed in.
/// Readonly fields on the entity are NOT copied to the entity from this DTO.
/// </summary>
public virtual <[CurrentEntityName]>Entity ToEntity(<[CurrentEntityName]>Entity toFill)
{
<[Foreach EntityField CrLf]><[If IsReadOnly ]><[ Else ]> toFill.<[EntityFieldName]> = this.<[EntityFieldName]>;<[ EndIf ]><[NextForeach]>
<[ If IsSubType ]> base.ToEntity(toFill);<[ EndIf]>
return toFill;
}
#region Relation Fields
<[Foreach RelatedEntity OneToMany CrLf]><[If Not MappedFieldRelationIsHidden]>
/// <summary> Gets the EntityCollectionBase with the related entities of type '<[RelatedEntityName]>Entity' which are related to this entity via a relation of type '1:n'.
/// If the EntityCollectionBase hasn't been fetched yet, the collection returned will be empty.</summary>
public virtual <[RelatedEntityName]>DTO[] <[MappedFieldNameRelation]> { get; set; }<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity ManyToMany CrLf]><[If Not MappedFieldRelationIsHidden]>
/// <summary> Gets the EntityCollectionBase with the related entities of type '<[RelatedEntityName]>Entity' which are related to this entity via a relation of type 'm:n'.
/// If the EntityCollectionBase hasn't been fetched yet, the collection returned will be empty.</summary>
public virtual <[RelatedEntityName]>DTO[] <[MappedFieldNameRelation]> { get; set; }<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity ManyToOne CrLf]><[If Not MappedFieldRelationIsHidden]>
/// <summary> Gets / sets related entity of type '<[RelatedEntityName]>Entity' which has to be set using a fetch action earlier. If no related entity
/// is set for this property, null is returned. This property is not visible in databound grids.</summary>
[Browsable(false)]
public virtual <[RelatedEntityName]>DTO <[MappedFieldNameRelation]> { get; set; }<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity OneToOne CrLf]><[If Not MappedFieldRelationIsHidden]>
/// <summary> Gets / sets related entity of type '<[RelatedEntityName]>Entity' which has to be set using a fetch action earlier. If no related entity
/// is set for this property, null is returned. This property is not visible in databound grids.</summary>
[Browsable(false)]
public virtual <[RelatedEntityName]>DTO <[MappedFieldNameRelation]> { get; set; }<[EndIf]><[NextForeach]>
#endregion
#region Custom DTO code
<[ UserCodeRegion "CustomDTOCode" ]>
// __LLBLGENPRO_USER_CODE_REGION_START CustomDTOCode
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
#endregion
}
}
File: dtoExtensions.template
using System;
using System.Collections.Generic;
using System.Linq;
using <[RootNamespace]>;
using <[RootNamespace]>.EntityClasses;
using <[RootNamespace]>.CollectionClasses;
using <[RootNamespace]>.DTOClasses;
using <[RootNamespace]>.HelperClasses;
namespace <[RootNamespace]>.DTOClasses
{
/// <summary>
/// Generates extension methods for converting Entities to DTOs
/// This class is generated. Do not modify.
/// </summary>
public static <[If UsePartialClasses]>partial <[EndIf]>class Extensions
{
<[If HasEntity]><[Foreach Entity]>
/// <summary>Create a <[CurrentEntityName]> DTO from a <[CurrentEntityName]> entity</summary>
/// <returns>The DTO created</returns>
public static <[CurrentEntityName]>DTO ToDTO(this <[CurrentEntityName]>Entity entity)
{
<[CurrentEntityName]>DTO dto = null;
if (entity != null)
dto = new <[CurrentEntityName]>DTO(entity);
return dto;
}
/// <summary>Create a list of <[CurrentEntityName]>DTO from a <[CurrentEntityName]>Collection</summary>
/// <returns>The DTO list created</returns>
public static List<<[CurrentEntityName]>DTO> ToDTOs(this <[CurrentEntityName]>Collection collection)
{
List<<[CurrentEntityName]>DTO> dtoList = new List<<[CurrentEntityName]>DTO>(collection.Count);
foreach(<[CurrentEntityName]>Entity entity in collection)
dtoList.Add(new <[CurrentEntityName]>DTO(entity));
return dtoList;
}
/// <summary>Create a list of <[CurrentEntityName]>DTO from a List of <[CurrentEntityName]> entities</summary>
/// <returns>The DTO list created</returns>
public static List<<[CurrentEntityName]>DTO> ToDTOs(this List<<[CurrentEntityName]>Entity> entities)
{
return entities.ConvertAll<<[CurrentEntityName]>DTO>(e => new <[CurrentEntityName]>DTO(e));
}
/// <summary>From the queryable object, get a list of <[CurrentEntityName]>DTO</summary>
/// <returns>The DTO list created</returns>
public static List<<[CurrentEntityName]>DTO> ToDTOs(this IQueryable<<[CurrentEntityName]>Entity> queryableEntities)
{
return queryableEntities.ToList().ConvertAll<<[CurrentEntityName]>DTO>(e => new <[CurrentEntityName]>DTO(e));
}
/// <summary>From the queryable object, get a list of <[CurrentEntityName]>DTO</summary>
/// <returns>The DTO list created</returns>
public static <[CurrentEntityName]>DTO FirstDTO(this IQueryable<<[CurrentEntityName]>Entity> queryableEntities)
{
<[CurrentEntityName]>DTO dto = null;
<[CurrentEntityName]>Entity firstEntity = queryableEntities.First();
if (firstEntity != null)
dto = new <[CurrentEntityName]>DTO(firstEntity);
return dto;
}
<[NextForeach]><[EndIf]>
#region Custom code
<[ UserCodeRegion "CustomDTOCode" ]>
// __LLBLGENPRO_USER_CODE_REGION_START CustomDTOCode
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
#endregion
}
}
FILE: DTO.tasks
<?xml version="1.0" ?>
<taskGroup xmlns="http://sd/llblgen/pro/taskGroupElementDefinitions.xsd" name="DTO Templates" isOptional="false" description="General group of tasks which are used DTO templates.">
<supportedPlatforms>
<platform name=".NET 3.5" />
</supportedPlatforms>
<supportedTemplateGroups>
<templateGroup name="SelfServicing" />
</supportedTemplateGroups>
<taskGroup name="Create Directories">
<task name="DTODirectoryCreator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.DirectoryCreator">
<parameters>
<parameter name="folderToCreate" defaultValue="DTOClasses" isOptional="false" description="The folder to create" />
<parameter name="failWhenExistent" defaultValue="false" isOptional="true" description="Flag to signal what to do when the folder already exists. Overrules clearWhenExistent" valueType="boolean" />
<parameter name="clearWhenExistent" defaultValue="false" isOptional="true" description="Flag to signal if an existing folder has to be cleared first. Overruled by failWhenExistent" valueType="boolean" />
</parameters>
</task>
</taskGroup>
<taskGroup name="Create DTO Classes" description="Create DTO Classes">
<task name="DTOInterfaceCreator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.CodeEmitter">
<parameters>
<parameter isOptional="false" name="destinationFolder" defaultValue="DTOClasses" />
<parameter isOptional="false" name="failWhenExistent" defaultValue="false" />
<parameter isOptional="false" name="filenameFormat" defaultValue="IDTO.cs" />
<parameter isOptional="false" name="templateID" defaultValue="SD_DTOInterfaceTemplate" />
<parameter isOptional="false" name="emitType" defaultValue="generic" />
</parameters>
</task>
<task name="DTOClassCreator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.CodeEmitter">
<parameters>
<parameter isOptional="false" name="destinationFolder" defaultValue="DTOClasses" />
<parameter isOptional="false" name="failWhenExistent" defaultValue="false" />
<parameter isOptional="false" name="filenameFormat" defaultValue="[elementName]DTO.[extension]" />
<parameter isOptional="false" name="templateID" defaultValue="SD_DTOTemplate" />
<parameter isOptional="false" name="emitType" defaultValue="allEntities" />
</parameters>
</task>
<task name="DTOExtentionsCreator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.CodeEmitter">
<parameters>
<parameter isOptional="false" name="destinationFolder" defaultValue="DTOClasses" />
<parameter isOptional="false" name="failWhenExistent" defaultValue="false" />
<parameter isOptional="false" name="filenameFormat" defaultValue="DTOExtensions.cs" />
<parameter isOptional="false" name="templateID" defaultValue="SD_DTOExtentionsTemplate" />
<parameter isOptional="false" name="emitType" defaultValue="generic" />
</parameters>
</task>
</taskGroup>
</taskGroup>
Finally, add the following to the appropriate place in your *.preset file. This will define the code generation task above within your generation preset.
<taskGroupPreset name="DTO Templates">
<taskGroupPreset name="Create Directories">
<taskPreset name="DTODirectoryCreator" />
</taskGroupPreset>
<taskGroupPreset name="Create DTO Classes">
<taskPreset name="DTOInterfaceCreator" />
<taskPreset name="DTOEntityInterfaceCreator" />
<taskPreset name="DTOBaseClassCreator" />
<taskPreset name="DTOClassCreator" />
<taskPreset name="DTOExtentionsCreator" />
</taskGroupPreset>
</taskGroupPreset>