2016-04-13 17 views
6

utilizzando EF 7: 1.0.0-rc1-finale,Entity Framework 7: Generazione valido Nome colonna

Ho problemi con EF generare la query correttamente, utilizzando database di primo approccio - utilizzando impalcature ef per generare alcune proprietà modello elencate nella DbContext - come le tabelle contengono un gran numero di colonne ho solo bisogno di un paio di lavorare per il WebAPI in modo che siano colonna mappati

ho 3 soggetti, Marchi, eventi e sessioni

Marchi contiene molti eventi ed eventi contiene molte sessioni

miei modelli:

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

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

sto usando il progetto come un WebAPI, quando chiamo fino Marchi, esso genera il seguente 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] 

dove [t]. [EventId1] nome colonna non valido nota che Se commento il seguente codice in DbContext - questo errore scompare e la query viene generata correttamente :

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

Ho provato ad aggiungere [ForeignKey], [] InverseProperty attributi po 'armeggiare' - non sembra fare la differenza

Ho anche provato ad aggiungere il nome della colonna in modo esplicito come detto here

Non so cos'altro provare - inizia a verificarsi solo quando definisco la relazione con Sessioni in FluidUI - suggerimenti?

+0

se si aggiunge '.ForSqlServerHasConstraintName ("EventId")' 'dopo .HasForeignKey (s => s.EventId)' Non mi cambia niente? – Thomas

+0

continua a generare un nome di colonna non valido dopo aver aggiunto il vincolo - verificato aggiungendo su entrambe le relazioni con Brands and Sessions – Danish

+0

Suoni stupidi ma ho affrontato questo problema dopo il nome della tabella refactored di Resharper e non l'ho notato. Rinominato 'ProductCategory' in' ProductCategories' e Resharper refactored anche '.ToTable (" ProductCategory ")' a '.ToTable (" ProductCategories ")' – guneysus

risposta

3

Solo per divertimento. prova a creare questo database. Ho rimosso un sacco di "clutter" EF è come ASP.NET MVC basato sul concetto di convenzione sulla configurazione.

Hai eseguito la configurazione fino a 2 volte (attributi e/o FluentApi), mentre in realtà avresti potuto farlo 0 volte.

Ecco alcune regole di base sulla convenzione (le convenzioni non sono case sensitive per trigger).

  • Per effettuare una chiave (chiave aka primaria) è necessario nominare neanche Id o [classname] Id
  • Per rendere una chiave esterna è il nome [foreighClassName (PARTA)] Id (non avete bisogno di questo se aggiungi il successivo invece (e ofc puoi averli entrambi contemporaneamente ma il nome deve essere lo stesso (partA)
  • Per accedere all'oggetto "corpo" estraneo, devi solo aggiungere una proprietà del tipo come questo public Brand Brand { get; set; } , il nome non è importante se c'è un solo "link"
  • Sul lato primario potresti voler usare una sorta di collezione per avvolgere tutti i bambini, e sì public ICollection<Event> Events { get; set; } è la strada da percorrere. Si potrebbe chiedere a proposito di IEnumerable o IList (beh, si pensi in questo modo, IEnumerable non può fare .Add() quindi è più o meno di sola lettura. IList bene fa tutto e di più, e sarebbe una bella misura se non dovessi farlo roba che è intraducibile a SQL. così in mezzo abbiamo ICollection.

Quando si utilizza la parola chiave virtuale? Bene in EF7 voi non ne fanno uso, come per l'abilitazione lazy loading, e EF7 non hanno (ancora) e non sappiamo se devono aggiungerlo Github EF7 feature request lacking lazyload

Perché ho rimosso gli attributi [JsonIgnore]? MAI inviare i modelli di entità al cliente. Creare un DTO corretto (popolarmente chiamato modello in ASP.NET MVC)

Ricordarsi di fare Migrazione e (per divertimento) provare prima senza alcun requisito "hardcoded" in FluentAPI e guardare il codice MIG che vedrete PK/FK sono fatti, gli indici e molti altri bob e pin sono aggiunti per te.

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

classe Context

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

Grazie per la spiegazione dettagliata, per i requisiti del DB purtroppo sono bloccato con un database esistente utilizzato da molte altre app, quindi ho aggiunto quelli da quello che mi hanno fornito gli scaffold - grazie per i suggerimenti su DTO, in questo caso tuttavia non credo che ne avrò bisogno, poiché i modelli qui sono scritti manualmente e sono già rappresentazioni di tabelle ridotte, non i tavoli completi - grazie per il tuo tempo! – Danish

+1

I modelli di database/entità vengono solitamente scritti, vengono chiamati Entity Framework Code First (ovvero si creano oggetti modello entità (DTO ish)) e vengono migrati nel database. Se apporti una modifica, devi aggiungere una nuova migrazione per assicurarti che il db sia sincronizzato con i modelli. Non raccomanderei ancora l'uso di EF 7 (Core), per altri scopi rispetto al testing non ancora rilasciato e presto otterrò un aggiornamento abbastanza importante in RC 2 (ef 7 rinomina in core, cambiando tutti i namespace, maggiore speedup e dnu/dnx modifiche a dotnet) e alcune modifiche extra nella versione finale. –

+0

Roadmap: https://github.com/aspnet/Home/wiki/Roadmap Velocità: https://blogs.msdn.microsoft.com/dotnet/2015/11/18/entity-framework-7-rc1-available/ Nuova CLI: http://dotnet.github.io/getting-started/ –

1

Rispondendo alla mia domanda proprio - sembra che potrebbe essere un bug in EF 7 1.0.0-RC1

nelle proprietà di entità per eventi in 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); 
}); 

Nota che ha 2 chiavi - che è stato generato da scaffolding, il tavolo ha una chiave primaria composita

Tuttavia per i miei requisiti con l'API ho solo bisogno di un identificatore primario - la rimozione della chiave composta ha risolto l'errore di generazione colonna invalido

codice aggiornato:

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

Questo è fino a che non viene risolto suppongo che sto ancora ricevendo lo stesso errore. Molto triste. –

+0

nel momento in cui stavo incontrando troppi problemi e documentazione incompleta (circa 3 mesi prima del rilascio) - indovina per un po 'resterò su EF6 – Danish

+0

Dopo 3 mesi anche le cose non sono cambiate. Passata una decisione ieri rollback a EF 6. Triste per vedere che stanno dicendo RC 2 finale anche quando le cose basilari come 1 a molti non funzionano correttamente. –

Problemi correlati