使用T4模板基于POCO生成多个类

时间:2013-09-13 13:23:12

标签: c# entity-framework code-generation t4

我正在寻找一种方法来在我向数据库添加表格时提高工作效率。通常,当我添加新表时,我必须执行以下步骤。

  1. 将表格添加到数据库(简单)
  2. 创建相应的EF Code First类。 (我不使用数据库迁移)
  3. 创建一个与#2中创建的EF类匹配的POCO模型。
  4. 创建存储库类
  5. 为CQRS模式创建命令和处理程序
  6. 为新创建的类创建AutoMapper地图
  7. 我最近创建了一个新网站,要求首先使用EF数据库,我看到它是如何使用tt文件生成类的。这让我想到我可以以某种方式使用这些模板(新模板)来生成基本CRUD操作的所有标准支持项。麻烦的是我没有创建这些模板的经验,也不知道从哪里开始。

    要生成的示例代码:

    存储库

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    public interface IUsersRepository : IRepository<Users>
    {
    }
    
    public class UsersRepository : RepositoryBase<Users>, IUsersRepository
    {
        public UsersRepository(IDatabaseFactory databaseFactory)
            : base(databaseFactory)
        {
        }
    
    }
    

    基于从EDMX(或代码优先)生成的实体的基本模型

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    public class User 
    {
        public int UserId { get; set; }
        public string UserRole { get; set; }
        public string UserName { get; set; }
    }
    

    命令

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    public class AddUpdateUserPayoutCommand : CommandBase, ICommand
    {
        public int UserId { get; set; }
        public string UserRole { get; set; }
        public string UserName { get; set; }
    }
    

    命令处理程序

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    public class AddUpdateUserHandler: ICommandHandler<AddUpdateUserCommand>
    {
        private readonly IUsersRepository _repository;
        private readonly IUnitOfWork _unitOfWork;
        public AddUpdateUserPayoutHandler(IUsersRepository repository, IUnitOfWork unitOfWork)
        {
            _repository = repository;
            _unitOfWork = unitOfWork;
        }
        public ICommandResult Execute(AddUpdateUserCommand command)
        {
            Users entity;
            if (command.UserId == 0)
            {
                entity = AutoMapper.Mapper.Map<Users>(command);
                _repository.Add(entity);
            }
            else
            {
                entity = _repository.Get(x=>x.UserId==command.UserId);
                entity = AutoMapper.Mapper.Map<Users>(command);
                _repository.Update(entity);
            }
            _unitOfWork.Commit(command.UserId);         
    
            return new CommandResult(true,entity.UserId);
        }
    }
    

    自动映射地图 - 放置在app_start

    Mapper.CreateMap<User, AddUpdateUserCommand>();
    

2 个答案:

答案 0 :(得分:8)

这个例子并不是一个可以切入项目的解决方案,而是作为一个例子来说明如何编写一个从数据库模式生成代码的模板。

构建一个快速模板,演示如何生成代码工件。

您可以在此处找到整个项目:https://github.com/mrange/CodeStack/tree/master/q18787460/ModelGenerator

模板本身使用T4Include.Schema来获取数据库模式。 SMO也是完全合法的使用,我只是喜欢T4Include.Schema因为性能而且它只依赖于SqlConnection(T4Include.Schema是https://www.nuget.org/packages/T4IncludeTemplate/的一部分)。

模板的基本策略是获取所有表并迭代它们以生成代码工件。

<#@ include file="$(SolutionDir)\packages\T4IncludeTemplate.1.0.3\T4\Schema.ttinclude"#>

<#

    // The namespace surrounding the code
    var namespaceName               = "ModelGenerator";
    var connectionString            = @"Data Source=localhost\SQLEXPRESS;Initial Catalog=TestDB;Integrated Security=True";
    var schema                      = GetSchema (connectionString);
    Func<string, string> justify    = s => LeftJustify (s, 40);

    var tables                      = schema
        .SchemaObjects
        .Where (so => so.Type == SchemaObject.SchemaObjectType.Table)
        .ToArray ()
        ;
#>

namespace <#=namespaceName#>
{
<#
    foreach (var table in tables)
    {
#>
    /// <summary>
    /// Repository interface for <#=table.Name#>
    /// </summary>
    partial interface I<#=table.Name#>Repository : IRepository<<#=table.Name#>>
    {
    }

    /// <summary>
    /// Repository class for <#=table.Name#>
    /// </summary>
    partial class <#=table.Name#>Repository : RepositoryBase<<#=table.Name#>>, I<#=table.Name#>Repository
    {
    }

    /// <summary>
    /// Poco class for <#=table.Name#>
    /// </summary>
    partial class <#=table.Name#>
    {
<#
    foreach (var column in table.Columns)
    {
#>
        public <#=justify (column.CsTypeName)#> <#=justify (column.Name)#>{ get; set; }
<#
    }
#>
    } 

    /// <summary>
    /// Command class for <#=table.Name#>
    /// </summary>
    partial class <#=table.Name#>Command : CommandBase, ICommand
    {
<#
    foreach (var column in table.Columns)
    {
#>
        public <#=justify (column.CsTypeName)#> <#=justify (column.Name)#> { get; set; }
<#
    }
#> 
    }

    /// <summary>
    /// Command handler class for <#=table.Name#>
    /// </summary>
    partial class <#=table.Name#>CommandHandler : ICommandHandler<<#=table.Name#>Command>
    {
        private readonly IUsersRepository _repository;
        private readonly IUnitOfWork _unitOfWork;
        public <#=table.Name#>CommandHandler(IUsersRepository repository, IUnitOfWork unitOfWork)
        {
            _repository = repository;
            _unitOfWork = unitOfWork;
        }

        public ICommandResult Execute(<#=table.Name#>Command command)
        {
            <#=table.Name#> entity;

<#
    var identityColumn = table.Columns.FirstOrDefault (c => c.IsIdentity);
    if (identityColumn == null)
    {
#>
@@@ ERROR__NO_IDENTITY_COLUMN_FOUND_FOR: <#=table.FullName#>
<#
    }
    else
    {
#>
            if (command.<#=identityColumn.Name#> == 0)
            {
                entity = AutoMapper.Mapper.Map<<#=table.Name#>>(command);
                _repository.Add(entity);
            }
            else
            {
                entity = _repository.Get(x=>x.UserId==command.<#=identityColumn.Name#>);
                entity = AutoMapper.Mapper.Map<<#=table.Name#>>(command);
                _repository.Update(entity);
            }
            _unitOfWork.Commit(command.<#=identityColumn.Name#>);         

            return new CommandResult(true,entity.<#=identityColumn.Name#>);
<#
    }
#>
        }
    }
<#
    }
#>
}

<#+

    static Schema GetSchema (string connectionString) 
    {
        using (var connection = new SqlConnection (connectionString))
        {
            connection.Open ();

            return new Schema (connection);
        }
    }


#>

最后生成的代码看起来像这样(对于我的测试数据库只有一个表:CUS_Customer)

// ############################################################################
// #                                                                          #
// #        ---==>  T H I S  F I L E  I S   G E N E R A T E D  <==---         #
// #                                                                          #
// # This means that any edits to the .cs file will be lost when its          #
// # regenerated. Changes should instead be applied to the corresponding      #
// # template file (.tt)                                                      #
// ############################################################################











namespace ModelGenerator
{
    /// <summary>
    /// Repository interface for CUS_Customer
    /// </summary>
    partial interface ICUS_CustomerRepository : IRepository<CUS_Customer>
    {
    }

    /// <summary>
    /// Repository class for CUS_Customer
    /// </summary>
    partial class CUS_CustomerRepository : RepositoryBase<CUS_Customer>, ICUS_CustomerRepository
    {
    }

    /// <summary>
    /// Poco class for CUS_Customer
    /// </summary>
    partial class CUS_Customer
    {
        public System.Int64                             CUS_ID                                  { get; set; }
        public System.String                            CUS_FirstName                           { get; set; }
        public System.String                            CUS_LastName                            { get; set; }
        public System.DateTime                          CUS_Born                                { get; set; }
        public System.DateTime                          CUS_Created                             { get; set; }
    } 

    /// <summary>
    /// Command class for CUS_Customer
    /// </summary>
    partial class CUS_CustomerCommand : CommandBase, ICommand
    {
        public System.Int64                             CUS_ID                                   { get; set; }
        public System.String                            CUS_FirstName                            { get; set; }
        public System.String                            CUS_LastName                             { get; set; }
        public System.DateTime                          CUS_Born                                 { get; set; }
        public System.DateTime                          CUS_Created                              { get; set; }

    }

    /// <summary>
    /// Command handler class for CUS_Customer
    /// </summary>
    partial class CUS_CustomerCommandHandler : ICommandHandler<CUS_CustomerCommand>
    {
        private readonly IUsersRepository _repository;
        private readonly IUnitOfWork _unitOfWork;
        public CUS_CustomerCommandHandler(IUsersRepository repository, IUnitOfWork unitOfWork)
        {
            _repository = repository;
            _unitOfWork = unitOfWork;
        }

        public ICommandResult Execute(CUS_CustomerCommand command)
        {
            CUS_Customer entity;

            if (command.CUS_ID == 0)
            {
                entity = AutoMapper.Mapper.Map<CUS_Customer>(command);
                _repository.Add(entity);
            }
            else
            {
                entity = _repository.Get(x=>x.UserId==command.CUS_ID);
                entity = AutoMapper.Mapper.Map<CUS_Customer>(command);
                _repository.Update(entity);
            }
            _unitOfWork.Commit(command.CUS_ID);         

            return new CommandResult(true,entity.CUS_ID);
        }
    }
}

如果从github拉出项目并将连接字符串更新为与您相关的内容,则应为您生成代码。如果您遇到任何问题,请回复此帖子。

答案 1 :(得分:0)

您想要的是实体接口生成器的路线图

https://entityinterfacegenerator.codeplex.com

该项目包含自定义的T4模板,可以为DbContext类和实体类生成接口和属性。 它不会,但很快就会生成通用存储库。