T4 для генерации POCO над объектами LLBLGen Pro? - PullRequest
6 голосов
/ 27 октября 2011

Насколько я понимаю, LLBLGen Pro не может генерировать POCO по своим собственным сущностям (см. Здесь: http://www.llblgen.com/Pages/featuresLLBLGenPro.aspx).

Кто-нибудь написал T4, который будет генерировать классы POCO, соответствующие сущностям LLBLGen Pro, и генерировать соответствующее преобразованиелогика перехода к объекту и POCO и от него? Кто-нибудь еще придумал решение, которое не предполагает ручного написания тонны кода преобразования?

1 Ответ

6 голосов
/ 23 января 2012

Мы генерировали DTO из LLBLGen вместо того, чтобы использовать T4.

Нам нужно было создать DTO из сущности. Это не технически POCO, потому что они имеют метод ToEntity() и FromEntity(), но, возможно, это будет работать для вас.

Мы создали IDTO<T> интерфейс, который затем был реализован классами DTO (по одному для каждой сущности). Вместо того, чтобы изменять шаблоны сущностей, мы добавили DTOExtension методы, которые преобразовывали бы сущность в DTO (а также многочисленные другие вспомогательные преобразования).

Вот файлы шаблонов, которые вы можете использовать в LLBLGen v2.6. Конструктор шаблонов в версии 3 намного проще в использовании, и вы можете преобразовать большую часть этого кода для версии 3, если вам нужно.

Файл: 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);
    }
}

Файл: 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
    }
}

Файл: 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
    }
}

ФАЙЛ: 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>

Наконец , добавьте следующее в соответствующее место в вашем файле * .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>
...