2017-01-09 27 views
0

Оказывается, простое изменение шаблона T4 по умолчанию было действительно очень простым. Внутри GetTypeName есть проверка is StructuralType, которая обрабатывает все непримитивные типы. Это довольно хорошо фиксировало большинство моих проблем. Тогда это был вопрос Ctrl-F для ключевых слов. Мое расстройство просто получало лучшее из меня, когда я изначально разместил этот вопрос.Использование шаблона T4 для принудительной установки PascalCase (TitleCase) для объектов в Entity Framework 6

-

я был обеспечен с базой данных, содержащей все имена таблиц и имена столбцов в snake_case. База данных не может быть изменена. Я надеюсь использовать мощь шаблонов T4 для автоматического генерации всех классов, членов, свойств, свойств навигации и т. Д. В PascalCase (TitleCase).

До сих пор я прилично закрываю, но я начинаю застревать.

namespace PokeDB 
{ 
    using System; 
    using System.Collections.Generic; 

    public partial class Ability 
    { 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] 
     public ability() 
     { 
      this.ability_changelog = new HashSet<ability_changelog>(); 
      this.ability_flavor_text = new HashSet<ability_flavor_text>(); 
      this.ability_names = new HashSet<ability_names>(); 
      this.ability_prose = new HashSet<ability_prose>(); 
      this.conquest_pokemon_abilities = new HashSet<conquest_pokemon_abilities>(); 
      this.pokemon_abilities = new HashSet<pokemon_abilities>(); 
     } 

     public long id { get; set; } 
     public string identifier { get; set; } 
     public long generation_id { get; set; } 
     public bool is_main_series { get; set; } 

     public virtual generation Generation { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<AbilityChangelog> AbilityChangelog { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<AbilityFlavorText> AbilityFlavorText { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<AbilityNames> AbilityNames { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<AbilityProse> AbilityProse { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<ConquestPokemonAbilities> ConquestPokemonAbilities { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<PokemonAbilities> PokemonAbilities { get; set; } 
    } 
} 

Как было показано выше, я получил имя класса, имена навигационных свойств, некоторые из возвращаемых значений навигационных свойств, а также имя файла. Там, где я застрял, - это имя конструктора, назначения конструктора, свойства и оставшиеся свойства свойств навигации.

Прямо сейчас, я просто заменяю все ссылки строки на CultureInfo.CurrentCulture.TextInfo.ToTitleCase(string).Replace("_", "").

Для примера, для имени класса:

public string EntityClassOpening(EntityType entity) 
    { 
     return string.Format(
      CultureInfo.InvariantCulture, 
      "{0} {1}partial class {2}{3}", 
      Accessibility.ForType(entity), 
      _code.SpaceAfter(_code.AbstractOption(entity)), 
      _code.Escape(entity), 
      _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); 
    } 

изменено на:

public string EntityClassOpening(EntityType entity) 
    { 
     return string.Format(
      CultureInfo.InvariantCulture, 
      "{0} {1}partial class {2}{3}", 
      Accessibility.ForType(entity), 
      _code.SpaceAfter(_code.AbstractOption(entity)), 
      CultureInfo.CurrentCulture.TextInfo.ToTitleCase(_code.Escape(entity)).Replace("_", ""), 
      _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); 
    } 

Это был долгий и утомительный процесс, но это лучше, чем абсолютно ничего. Я надеялся, что у вас, ребята, может быть несколько советов для меня или немного более чистого решения? Это мой первый раз, используя любой тип настраиваемого T4-шаблона. Похоже, я должен был сбить некоторые из них одним махом в некоторых методах TypeManager. Я просто не совсем уверен, где.

Я нашел пару вопросов StackOverflow и пару сообщений в блогах об использовании шаблонов T4 для этого, но, к сожалению, шаблоны никогда не были разделены.

Даже если это не идеальное решение, я был бы признателен за любое руководство.


using System.Linq; 

namespace PokeDB 
{ 
    public class ConsoleDriver 
    { 
     public static readonly PokeDBContainer PokeDB = new PokeDBContainer(); 

     public static void Main(string[] args) 
     { 
      System.Diagnostics.Debug.WriteLine(PokeDB.Pokemon); 
      var x = PokeDB.Pokemon.ToList(); 
     } 
    } 
} 

Выше текущий драйвер тест, который я написал, чтобы проверить мой DbContext. Самый первая строка завершается с следующим сообщением: An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll Additional information: The entity type Pokemon is not part of the model for the current context.

namespace PokeDB 
{ 
    using System; 
    using System.Collections.Generic; 
    using System.ComponentModel.DataAnnotations.Schema; 

    [Table("pokemon")] 
    public partial class Pokemon 
    { 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] 
     public Pokemon() 
     { 
      this.Encounters = new HashSet<Encounter>(); 
      this.PokemonAbilities = new HashSet<PokemonAbilities>(); 
      this.PokemonForms = new HashSet<PokemonForms>(); 
      this.PokemonGameIndices = new HashSet<PokemonGameIndices>(); 
      this.PokemonItems = new HashSet<PokemonItems>(); 
      this.PokemonMoves = new HashSet<PokemonMoves>(); 
      this.PokemonStats = new HashSet<PokemonStats>(); 
      this.PokemonTypes = new HashSet<PokemonTypes>(); 
     } 

     public long Id { get; set; } 
     public string Identifier { get; set; } 
     public Nullable<long> SpeciesId { get; set; } 
     public long Height { get; set; } 
     public long Weight { get; set; } 
     public long BaseExperience { get; set; } 
     public long Order { get; set; } 
     public bool IsDefault { get; set; } 

     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<Encounter> Encounters { get; set; } 
     public virtual PokemonSpecies PokemonSpecies { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<PokemonAbilities> PokemonAbilities { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<PokemonForms> PokemonForms { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<PokemonGameIndices> PokemonGameIndices { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<PokemonItems> PokemonItems { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<PokemonMoves> PokemonMoves { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<PokemonStats> PokemonStats { get; set; } 
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 
     public virtual ICollection<PokemonTypes> PokemonTypes { get; set; } 
    } 
} 

Это мой текущим Pokemon.cs файл, который был создан с помощью Т4. И это моя DbSet декларация внутри PokeDBContext.cs:

public virtual DbSet<Pokemon> Pokemon { get; set; }

Я получаю правильный Intellisense, так как классы настроены должным образом. Я просто получаю мгновение InvalidOperationException всякий раз, когда я пытаюсь получить доступ к объектам.Обратите внимание, что истинный объект базы данных равен pokemon.

+3

Почему бы вам не рассмотреть возможность использования EF ПОКО по этой ссылке https://marketplace.visualstudio.com/items?itemName=SimonHughes.EntityFrameworkReversePOCOGenerator – Aravind

+0

@Aravind Этот инструмент выглядит действительно удивительный и кажется, что он должен делать именно то, что мне нужно. Однако при настройке строки подключения у меня возникают проблемы. Строка подключения, созданная мастером EF, не работает. Я все время получаю сообщение об ошибке, когда поставщик System.Data.EntityClient не загружается. Но он работает для нормальных преобразований. –

+0

Ник. Пожалуйста, обновите сообщение с скриншотом ошибки вместе с соответствующим кодом, где возникает ошибка – Aravind

ответ

0

Вы на правильном пути, но то, что вам нужно, - это простой случай с змеей для преобразования PascalCase. Я обеспокоен тем, что EF потеряет сопоставления столбцов (если вы сначала используете модель/базу данных) только с этими изменениями.

Чтобы сделать то, что вы просите, переместите свою отличную CultureInfo.CurrentCulture.TextInfo.ToTitleCase (имя) .Replace ("_", ""); для многократного использования метода в классе CodeStringGenerator

public string PascalCase(string name) 
{ 
    return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(name).Replace("_", ""); 
} 

Затем в шаблоне T4 для изменения конструктора:

public <#=code.Escape(codeStringGenerator.PascalCase(entity))#>() 
{ 
<# 
    foreach (var edmProperty in propertiesWithDefaultValues) 
    { 
#> 
    this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; 
<# 
    } 

    foreach (var navigationProperty in collectionNavigationProperties) 
    { 
     // for readability hold the type name in a variable 
     var typeName = typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType()); 
#> 
    // pascal case here 
    this.<#=code.Escape(codeStringGenerator.PascalCase(navigationProperty))#> = new HashSet<<#=code.Escape(codeStringGenerator.PascalCase(typeName))#>>(); 
<# 
    } 

    foreach (var complexProperty in complexProperties) 
    { 
#> 
    this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); 
<# 
    } 
#> 
} 

Сделав все это, ваш код будет компилироваться, но запросы, скорее всего, не из-за из-за у edmx все еще есть имена для змеиного случая, поэтому нет сопоставлений с вашей фактической базой данных. Поэтому вам также потребуется отредактировать файл edmx. Здесь есть несколько примеров: How to force pascal case with Oracle's Entity Framework support?, но на этом этапе вы начинаете задаваться вопросом, стоит ли это усилий.

EDIT Изменение EDMX вместо (лучший вариант)

Отмените изменения изменения T4, вам они больше не нужны. Затем используйте пример в link. Я немного изменил его, чтобы использовать ваш заголовок. Я буду использовать это для подобной задачи:

// In the main method get your edmx/designer 
//EDMX File location 
string pathFile = @"c:\Path\To\DbModel.edmx"; 
//Designer location for EF 5.0 
string designFile = @"c:\Path\To\DbModel.edmx.diagram"; 
// ... 
// replace the PascalCase method with this 
public static string PascalCase(string name, bool sanitizeName = true, bool pluralize = false) 
{ 

    // if pascal case exists 
    // exit function 

    Regex rgx = new Regex(@"^[A-Z][a-z]+(?:[A-Z][a-z]+)*$"); 

    string pascalTest = name; 

    if (name.Contains(".")) 
    { 
     string[] test = new string[] { }; 
     test = name.Split('.'); 

     if (rgx.IsMatch(test[1].ToString())) 
     { 
      return name; 
     } 

    } 
    else 
    { 

     if (rgx.IsMatch(name)) 
     { 
      return name; 
     } 

    } 

    //Check for dot notations in namespace 
    string result; 
    bool contains = false; 
    string[] temp = new string[] { }; 
    var namespc = string.Empty; 

    if (name.Contains(".")) 
    { 
     contains = true; 
     temp = name.Split('.'); 
     namespc = temp[0]; 

    } 

    if (contains) 
    { 
     name = temp[1]; 
    } 

    name = name.ToLowerInvariant(); // this may or may not be required 
    // Here's the simplified snake to pascal case 
    result = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(name).Replace("_", ""); 

    if (contains) 
    { 
     result = namespc.ToString() + "." + result; 
    } 

    if (pluralize) 
    { 
     result = Pluralize(result); 
    } 
    return result; 
} 
+0

Я считаю, что столкнулся с этой проблемой. У меня было все отлично. От имени моего класса до моего DbSets. Все выглядело точно, как я этого хотел. Но потом я получил сообщение об ошибке, связанной с тем, что мой объект не существует в текущем контексте. Мне сразу стало ясно, что LINQ to SQL не сможет найти EntityX внутри БД с помощью entity_x. Я попытался добавить аннотацию таблицы к каждому сгенерированному классу, но это, похоже, не устранило мою проблему. Любые комментарии относительно правильного отображения LINQ to SQL? Я * действительно * не хочу использовать сумасшедшие сущности в C#. –

+0

Вы редактируете файл edmx, который содержит имена для сопоставления сущностей в элементах EntitySetMapping. Вопрос в ответе является одним из небольшого числа примеров, посвященных этой проблеме (http://stackoverflow.com/questions/6505088/entity-framework-capitalizing-first-property-name-letter), но все они связаны либо с кодом сначала, или отредактируйте edmx. – reckface

+0

Большое спасибо за попытку помочь мне. Я обновил свой OP с некоторыми дополнительными подробностями относительно моего InvalidOperationException.Я искал внутри '.edmx', где мне, возможно, понадобится изменить' EntitySetMapping', но все это выглядело так, как я ожидал. Любые дальнейшие комментарии? –