2016-04-13 11 views
6

с помощью EF 7: 1.0.0-RC1-финал,Entity Framework 7: Создание Invalid Column Name

У меня возникли проблемы с EF генерацией запроса должным образом, с использованием базы данных первого подхода - использование эф подмостей для генерировать некоторые свойствам модели, перечисленные в DbContext - как таблицы содержат большое количество столбцов, мне нужно всего лишь несколько, чтобы работать на WebAPI поэтому они колонок отображаются

у меня есть 3 лица, брэнды, Событие и Sessions

Бренды содержит много событий и событий, содержащих множество сеансов

мои модели:

[Table("tblBranding")] 
public class Brand 
{ 
    [Key] 
    [Column("brandingId")] 
    public int BrandId { get; set; } 
    [Column("BrandingActive")] 
    public bool Active { get; set; } 
    [JsonIgnore] 
    [Column("DeadBrand")]   
    public bool DeadBrand { get; set; } 
    [Column("BrandingSiteTitle")] 
    public string Name { get; set; } 

    //navigation properties 
    public virtual ICollection<Event> Events { get; set; } 
} 

[Table("tblEvents")] 
public class Event 
{   
    public int EventId { get; set; } 
    [Column("eventActive")] 
    public bool Active { get; set; } 
    [Column("eventName")] 
    public string Name { get; set; }   
    public DateTime EventCloseDate {get;set;}   
    public int PaxAllocationLimit { get; set; } 

    //navigation properties    
    [JsonIgnore]   
    [Column("brandingId")] 

    public virtual int BrandId { get; set; } 
    [JsonIgnore] 
    [ForeignKey("BrandId")] 
    public virtual Brand Brand { get; set; } 
    public virtual ICollection<Session> Sessions { get; set; } 
} 

[Table("tblEventsDates")] 
public class Session 
{  
    [Column("EventDateID")] 
    public int SessionId { get; set; } 
    [Column("EventDateName")] 
    public string Name { get; set; }    
    [Column("EventDate")] 
    public DateTime SessionDate { get; set; }   
    [Column("EventDateTime")] 
    public DateTime SessionTime { get; set; } 
    [Column("EventDateMinutes")] 
    public decimal? SessionDurationInMinutes { get; set; } 
    [Column("EventDateArrival")] 
    public DateTime? ArrivalTime { get; set; } 
    [Column("EventCapacity")] 
    public int SessionCapacity { get; set; } 

    //navigation properties   
    [JsonIgnore]   
    public virtual int EventId { get; set; } 
    [JsonIgnore]   
    public virtual Event Event { get; set; }    
} 

Мои DbContext

protected override void OnModelCreating(ModelBuilder modelBuilder) 
{  
    modelBuilder.Entity<Event>() 
     .HasOne(e => e.Brand) 
     .WithMany(b => b.Events).HasForeignKey(e=>e.BrandId); 

    modelBuilder.Entity<Event>() 
     .HasMany(s => s.Sessions) 
     .WithOne(e => e.Event).HasForeignKey(s => s.EventId); 

    modelBuilder.Entity<Event>(entity=> { 
     entity.Property(e => e.EventId).HasColumnName("EventID"); 
     entity.HasKey(e => new{ e.EventId, e.EventCloseDate}); 
     entity.HasIndex(e => e.EventId).HasName("For Full Text Indexing").IsUnique(); 
     entity.Property(e => e.Active).HasDefaultValue(false); 
     entity.Property(e => e.EventCloseDate) 
      .HasColumnType("datetime") 
      .HasDefaultValueSql("'1/1/2038'"); 
     entity.Property(e => e.Name).HasMaxLength(1024); 
     entity.Property(e => e.PaxAllocationLimit).HasDefaultValue(10000); 
    }); 

    modelBuilder.Entity<Brand>(entity => { 
     entity.HasKey(e => e.BrandId);     

     entity.Property(e => e.Active).HasDefaultValue(false);    

     entity.Property(e => e.Name) 
      .IsRequired() 
      .HasMaxLength(150) 
      .HasColumnType("varchar");     
    }); 

    modelBuilder.Entity<Session>(entity => { 
     entity.HasKey(e => e.SessionId); 

     entity.Property(e=>e.Name) 
      .HasMaxLength(250) 
      .HasColumnType("varchar") 
      .HasDefaultValue(""); 

     entity.Property(e => e.SessionDurationInMinutes) 
      .HasColumnType("numeric") 
      .HasDefaultValue(0m);     
     }); 
    } 

    public virtual DbSet<Brand> Brands { get; set; } 
    public virtual DbSet<Event> Events { get; set; }   
    public virtual DbSet<Session> Sessions { get; set; } 
} 

Я использую проект как WebAPI, когда я вызываю Brands, он генерирует следующий SQL:

SELECT [e].[brandingId], [e].[BrandingActive], [e].[DeadBrand], [e].[BrandingSiteTitle] 
FROM [tblBranding] AS [e] 
WHERE [e].[BrandingActive] = 1 
ORDER BY [e].[BrandingSiteTitle], [e].[brandingId] 
Microsoft.Data.Entity.Storage.Internal.RelationalCommandBuilderFactory: Information: Executed DbCommand (75ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] 
SELECT [t].[EventId], [t].[EventCloseDate], [t].[eventActive], [t].[brandingId], [t].[EventId1], [t].[eventName], [t].[PaxAllocationLimit] 
FROM [tblEvents] AS [t] 
INNER JOIN (
    SELECT DISTINCT [e].[BrandingSiteTitle], [e].[brandingId] 
    FROM [tblBranding] AS [e] 
    WHERE [e].[BrandingActive] = 1 
) AS [e] ON [t].[brandingId] = [e].[brandingId] 
ORDER BY [e].[BrandingSiteTitle], [e].[brandingId] 

где [t]. [EventId1] имя столбца недопустимо Обратите внимание, что если я прокомментирую следующий код в DbContext - эта ошибка исчезнет, ​​и запрос будет сгенерирован правильно :

modelBuilder.Entity<Event>() 
      .HasMany(s => s.Sessions) 
      .WithOne(e => e.Event).HasForeignKey(s => s.EventId); 

Я попытался добавить [ForeignKey], [InverseProperty] атрибутов в то время как «пустячный» - это, кажется, не делают разницы

Я также попытался добавить имя столбца в явном виде, как указано here

Я не уверен, что еще попробовать - это начинает происходить только тогда, когда я определяю отношение к сеансам в FluidUI - предложения?

+0

если добавить '.ForSqlServerHasConstraintName ("EventId")' после '.HasForeignKey (s => s.EventId)' что это меняет? – Thomas

+0

все еще генерирует недопустимое имя столбца после добавления ограничения - проверяется добавлением обеих связей с брендами и сеансами – Danish

+0

Звучит глупо, но я столкнулся с этой проблемой после рефарингового имени таблицы Resharper, и я этого не заметил. Переименовано 'ProductCategory' в' ProductCategories' и Resharper реорганизовано также '.ToTable (« ProductCategory »)' to' .ToTable («ProductCategories») ' – guneysus

ответ

3

Просто для удовольствия. попробуйте сделать эту базу данных. Я удалил много «беспорядок» EF, как ASP.NET MVC, основанный на концепции условной конфигурации.

Вы сделали конфигурацию до 2 раз (атрибуты и/или FluentApi), в то время как в действительности вы могли бы сделать это 0 раз.

Ниже приведены некоторые базовые правила соглашения (соглашения не чувствительны к регистру для случая).

  • Чтобы сделать ключ (так называемый первичный ключ), вы должны назвать его как Id или [имя_класса] Id
  • Для того, чтобы внешний ключ вы называете его [foreighClassName (Парта)] Id (вам не нужно это, если вы добавляете следующий вместо этого (и ofc вы можете иметь оба одновременно, но тогда имя должно быть одинаковым (partA)
  • Чтобы получить доступ к внешнему объекту «тело», вы просто добавляете свойство такого типа, как это public Brand Brand { get; set; } , имя не важно, если есть только одна «ссылка».
  • На основной стороне вы можете использовать какую-то коллекцию для обертывания всех детей и да public ICollection<Event> Events { get; set; } - это путь. Можно спросить, что насчет IEnumerable или IList (хорошо подумайте об этом таким образом, IEnumerable не может сделать .Add(), так что он более или менее доступен для чтения. IList хорошо все делает и все, и было бы неплохо, если бы оно не для него вещи, которые непереводимы к SQL. так что в середине мы имеем ICollection.

при использовании виртуального ключевого слова? Ну в EF7 вы не используете его, так как его для обеспечения отложенной загрузки, и EF7 нет что (пока), и мы не знаем, добавить ли их. Github EF7 feature request lacking lazyload

Почему я удалял атрибуты [JsonIgnore]? НИКОГДА не отправляйте модели сущностей для клиента. Создайте правильный DTO (обычно называемый моделью в ASP.NET MVC)

Не забудьте сделать миграцию и (для удовольствия) сначала попробуйте без каких-либо «жестко запрограммированных» требований в FluentAPI и посмотрите на код миграции, вы увидите PK/FK, индексы и несколько других бобов и булавок добавлены для вас.

public class Brand 
{ 
    public int Id { get; set; } 
    public bool Active { get; set; } 
    public bool DeadBrand { get; set; } 
    public string Name { get; set; } 

    //navigation properties 
    public ICollection<Event> Events { get; set; } 
} 

public class Event 
{   
    public int Id { get; set; } 
    public bool Active { get; set; } 
    public string Name { get; set; }   
    public DateTime EventCloseDate {get;set;}   
    public int PaxAllocationLimit { get; set; } 

    //navigation properties    
    public Brand Brand { get; set; } 
    public ICollection<Session> Sessions { get; set; } 
} 

public class Session 
{  
    public int Id { get; set; } 
    public string Name { get; set; } 
    //Datetime contains date and time   
    public DateTime Time { get; set; } 
    //TimeSpan is for duration, allowing access to seconds, minutes, hours etc. 
    public TimeSpan Duration { get; set; } 
    public DateTime? ArrivalTime { get; set; } 
    public int SessionCapacity { get; set; } 

    //navigation properties   
    public Event Event { get; set; }    
} 

Контекст класс

class DbContex{ 

    public virtual DbSet<Brand> Brands { get; set; } 
    public virtual DbSet<Event> Events { get; set; }   
    public virtual DbSet<Session> Sessions { get; set; } 

    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    {  
    //Personally I would not have many requirements in the database unless I    
    //was completely sure it had to be that way. 
    //They will ALWAYS bite you in the ass in the long run. 

     modelBuilder.Entity<Event>(entity=> { 
      entity.Property(e => e.Active).HasDefaultValue(false); 
      entity.Property(e => e.EventCloseDate) 
       .HasColumnType("datetime") 
       .HasDefaultValueSql("'1/1/2038'"); 
      entity.Property(e => e.Name).HasMaxLength(1024); 
      entity.Property(e => e.PaxAllocationLimit).HasDefaultValue(10000); 
     }); 

     modelBuilder.Entity<Brand>(entity => { 
      entity.Property(e => e.Active).HasDefaultValue(false);  
      entity.Property(e => e.Name) 
       .IsRequired() 
       .HasMaxLength(150) 
       .HasColumnType("varchar");     
     }); 

     modelBuilder.Entity<Session>(entity => { 
      entity.Property(e=>e.Name) 
       .HasMaxLength(250) 
       .HasColumnType("varchar") 
       .HasDefaultValue("");  
      entity.Property(e => e.SessionDurationInMinutes) 
       .HasColumnType("numeric") 
       .HasDefaultValue(0m);     
      }); 
     } 
    } 
} 
+0

Спасибо за подробное объяснение, что касается требований к БД, к сожалению, я застрял с существующей базой данных, которую используют многие другие приложения, поэтому я просто добавил те, что мне помогли - спасибо за советы по DTO, в этом случае однако я не думаю, что мне это понадобится, так как модели здесь написаны вручную и уже уменьшены представления таблиц, а не полные таблицы - спасибо за ваше время! – Danish

+1

Модели баз данных/сущности обычно пишутся, они называются Entity Framework Code First (что означает, что вы создаете объекты модели объекта (DTO ish)) и переносите их в базу данных. Если вы внесете изменения, вам нужно добавить новую миграцию, чтобы убедиться, что db синхронизирован с моделями. Я бы не рекомендовал использовать EF 7 (Core), но для других целей, чем тестирование на его еще не выпущенном, и скоро получит довольно крупное обновление в RC 2 (ef 7 переименует в ядро, изменив все пространства имен, основное ускорение и dnu/dnx изменения в dotnet) и некоторые дополнительные изменения в финальной версии. –

+0

Дорожная карта: https://github.com/aspnet/Home/wiki/Roadmap Скорость: https://blogs.msdn.microsoft.com/dotnet/2015/11/18/entity-framework-7-rc1-available/ Новый CLI: http://dotnet.github.io/getting-started/ –

1

Отвечая на мой собственный вопрос - похоже, что это может быть ошибка в EF 7 1.0.0-RC1

в свойствах сущностей для события в DbContext

modelBuilder.Entity<Event>(entity=> { 
    entity.Property(e => e.EventId).HasColumnName("EventID"); 
    entity.HasKey(e => new{ e.EventId, e.EventCloseDate}); 
    entity.HasIndex(e => e.EventId).HasName("For Full Text Indexing").IsUnique(); 
    entity.Property(e => e.Active).HasDefaultValue(false); 
    entity.Property(e => e.EventCloseDate) 
     .HasColumnType("datetime") 
     .HasDefaultValueSql("'1/1/2038'"); 
    entity.Property(e => e.Name).HasMaxLength(1024); 
    entity.Property(e => e.PaxAllocationLimit).HasDefaultValue(10000); 
}); 

Обратите внимание, что она имеет 2 ключи, которые были сгенерированы из строительных лесов, таблица имеет составной первичный ключ

Однако для моих требований с API мне нужен только первичный идентификатор - удаление составного ключа устраняет ошибку генерации недопустимого столбца

обновленный код:

modelBuilder.Entity<Event>(entity=> { 
    entity.Property(e => e.EventId).HasColumnName("EventID"); 
    entity.HasKey(e => e.EventId); 
    entity.HasIndex(e => e.EventId).HasName("For Full Text Indexing").IsUnique(); 
    entity.Property(e => e.Active).HasDefaultValue(false); 
    entity.Property(e => e.EventCloseDate) 
     .HasColumnType("datetime") 
     .HasDefaultValueSql("'1/1/2038'"); 
    entity.Property(e => e.Name).HasMaxLength(1024); 
    entity.Property(e => e.PaxAllocationLimit).HasDefaultValue(10000); 
}); 
+0

Это пока не исправлено. Думаю, я все равно получаю ту же ошибку. Действительно печально. –

+0

в то время, когда я просто сталкивался с множеством проблем и неполной документацией (около 3 месяцев до выпуска) - думаю, я буду придерживаться EF6 на некоторое время – Danish

+0

После 3 месяцев также все не изменилось. Принято решение вчера откат EF 6. Грустно видеть, что они говорят о финале RC 2, даже когда основные вещи, такие как 1 для многих, работают некорректно. –

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