2013-09-13 5 views
3

Im ищет способ повысить производительность при добавлении таблицы в мою базу данных. Как правило, когда я добавляю новую таблицу, я должен выполнить следующие шаги.Использование шаблонов T4 для генерации нескольких классов на основе POCO

  1. Добавить таблицу в базу данных (простой)
  2. Создать соответствующий EF Code First класса. (я не использую миграции db)
  3. Создайте модель POCO, которая соответствует классу EF, созданному в # 2.
  4. Создать класс репозитория
  5. Создание команд и обработчиков для CQRS шаблон
  6. Создание карт AutoMapper для вновь созданных классов

Я недавно создал новый веб-сайт, где требования должны были использовать базу данных EF первым и я видел, как он использовал файлы tt для генерации классов. Это заставило меня подумать, что я могу каким-то образом использовать эти шаблоны (новые) для создания всех стандартных элементов поддержки для основных операций CRUD. Проблема в том, что у меня нет опыта создания этих шаблонов, и я понятия не имею, с чего начать.

Пример кода, которые будут созданы:

Repository

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 (или Code First)

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; } 
} 

Command Handler

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); 
    } 
} 

Automapper Карты - Размещенные в app_start

Mapper.CreateMap<User, AddUpdateUserCommand>(); 
+0

Хотя вы можете сделать это с помощью T4 (почему бы не копировать существующий шаблон T4 и не мешать изменениям?), Я не рекомендую этот подход, потому что вы закончите переписывать любые изменения, которые вы делаете эти классы POCO. Вы рассматривали несколько иной подход к архитектуре? Даже что-то вроде шаблона GenericRepository, созданного из ваших классов EF, может сэкономить много работы. –

+1

Целью создания первого поколения всех поддерживающих классов, соответствующих требованиям к проектам. Что касается перезаписи, использование частичных классов позаботится об этой проблеме. – jason

+0

Я думаю, вы думаете по правильной линии, если вы можете привести пример того, как код, который вы делаете вручную, будет выглядеть так, что мне легче создать что-то, что будет иметь для вас смысл. – FuleSnabel

ответ

8

Этот пример не означает, что решение, которое можно cutnpaste в проект но в качестве примера того, как можно написать шаблон, который генерирует код из схемы базы данных.

Создал быстрый шаблон, чтобы продемонстрировать, как вы можете генерировать артефакты кода.

Вы можете найти весь проект здесь: 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 и обновить строку подключения к чему-то относящийся к вам, он должен генерировать код для вас. Если вы столкнулись с какими-либо проблемами, просто ответьте на этот пост.

+3

Совет: частичные классы/методы - ваши друзья при генерации кода – FuleSnabel

0

Что вы хотите в дорожной карте генератор Entity интерфейс

https://entityinterfacegenerator.codeplex.com

Этот проект содержит настраиваемые шаблоны T4, которые могут генерировать интерфейсы и атрибуты для классов и сущностей классов DbContext. Это не так, но скоро создадим общие хранилища.

Смежные вопросы