2015-05-18 12 views
15

disegno di sfondo:EF 6 - codice non valido prima uno-a-uno chiave esterna

Sto cercando di creare mappature EF6 code-prima per la seguente struttura del database:

La progettazione di database è come segue: invece di avere "CustomerID" come chiave esterna su tutte le entità correlate (occupazione, spese, entrate, ecc.), abbiamo una tabella CustomerRelationship, che conterrà il CustomerID, e quindi una colonna "RelatedID" che conterrà la chiave dell'entità correlata. Ad esempio, consente di dire aggiungo un record di occupazione per CustomerID = 1, allora accadrà quanto segue:

  1. Creare record nel CustomerRelationship, impostando CustomerID = 1 RelatedID = {nuovo EmploymentID generato automaticamente, diciamo 5} CustomerRelationshipTypeID = 55 (Id nella tabella di ricerca in cui si afferma che questo disco è di tipo occupazione)

  2. Creare record della tabella collocamento (EmploymentID = 5)

La struttura sopra descritta funzionerà per tutte le entità legate a un cliente.

DB Diagram ho mappature rapporto di lavoro per l'occupazione, qui sono i miei corsi:

public abstract class EntityBase : IEntity 
{ 
    #region IEntity Members 
    public int Id { get; set; } 

    public DateTime CreatedDate { get; set; } 

    public int CreatedUserId { get; set; } 

    public int CreatedSource { get; set; } 

    public DateTime ModifiedDate { get; set; } 

    public int ModifiedUserId { get; set; } 

    public int? DataMigrationId { get; set; } 

    public bool IsActive { get; set; }          
    #endregion 
} 


public class Employment : EntityBase 
{ 
    // ... all properties here.. removed most so easier to read 
    public int EmploymentTypeId { get; set; }  

    **public virtual ICollection<EmploymentRelationship> EmploymentRelationships { get; set; }** 
} 

    public EmploymentMap() 
    { 
     this.HasKey(t => t.Id); 
     ToTable("tblEmployment"); 
     Property(t => t.Id).HasColumnName("EmploymentID");  
     // Mapping for all properties follow  
    } 

public abstract partial class CustomerRelationship : EntityBase 
{ 
    public int CustomerId { get; set; } 

    public decimal? PercentageShare { get; set; } 

    public int CustomerRelationshipTypeId { get; set; } 

    public int RelatedId { get; set; } 
} 

public class EmploymentRelationship : CustomerRelationship 
{  
    public virtual Employment Employment { get; set; } 
} 

    public EmploymentRelationshipMap() 
    { 
     this.HasKey(t => t.Id); 

     Map<EmploymentRelationship>(m => 
     { 
      m.Requires("CustomerRelationshipTypeID").HasValue(55).IsRequired(); // Define lookup value for type of employment 
      m.ToTable("tblCustomerRelationship"); 
     }); 

     Property(t => t.Id).HasColumnName("CustomerRelationshipID"); 
     Property(t => t.CustomerId).HasColumnName("CustomerID"); 
     Property(t => t.RelatedId).HasColumnName("RelatedID"); 

     HasRequired(t => t.Employment) 
      .WithMany(t => t.EmploymentRelationships) 
      .HasForeignKey(t => t.RelatedId); 
    } 

public class Customer : EntityBase 
{ 
    // Customer Properties... 
    public Customer() 
    { 
     EmploymentRelationships = new List<EmploymentRelationship>(); 
    } 

    public virtual ICollection<EmploymentRelationship> EmploymentRelationships { get; set; } 
} 

    public CustomerMap() 
    { 
     this.HasKey(t => t.Id); 

     ToTable("tblCustomer"); 

     Property(t => t.Id).HasColumnName("CustomerID"); 
    } 


public class CustomerContext 
{ 
    public CustomerContext() 
     : base(SymmetryCopy.context_connectionstring_main) 
    { 
    } 

    public virtual DbSet<Customer> Customers { get; set; } 
    public virtual DbSet<Employment> Employments { get; set; } 

    #region Customer Relationship entity mappings 
    public virtual DbSet<EmploymentRelationship> EmploymentRelationships { get; set; } 
    #endregion 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Configurations.Add(new CustomerMap()); 
     modelBuilder.Configurations.Add(new EmploymentMap()); 

     #region Customer Relationship entity mappings 
     modelBuilder.Configurations.Add(new EmploymentRelationshipMap()); 
     #endregion 
    } 
} 

CustomerRepo per interrogare contesto e tornare i risultati:

public class CustomerRepository : BaseRepository<Customer, CustomerContext>, ICustomerRepository 
{ 
    public CustomerRepository() : 
     base(new CustomerContext()) 
    { 

    } 

    public async Task<List<Employment>> GetEmployments(int customerId) 
    { 
     List<Employment> employments = new List<Employment>(); 
     using (var context = new CustomerContext()) 
     { 
      var employmentRelationships = context.EmploymentRelationships.Where(l => l.CustomerId == customerId).ToList(); 
      employments = employmentRelationships.Select(x => x.Employment).ToList(); 
     } 
     return employments; 
    } 
} 

Il metodo di cui sopra GetEmployments quindi restituisce tutti i record corrispondenti a CustomerID con CustomerRelationshipTypeID = 5 5 (valore chiave per gli impieghi). Vedi ritorna sotto.

enter image description here

ora per avere alle mie domande attuali:

Quando provo e collegare un altro tipo di entità, vale a dire: Caro-vita, seguendo lo stesso approccio come quello di occupazione, creazione di Expense.cs, ExpenseMap cs, ExpenseRelationship.cs, ExpenseRelationshipMap.cs, avente la seguente in ExpenseRElationshipMap.cs:

public class ExpenseRelationshipMap 
{ 
    public ExpenseRelationshipMap() 
    { 
     HasKey(t => t.Id); 

     Map<ExpenseRelationship>(m => 
     { 
      m.Requires("CustomerRelationshipTypeID").HasValue(60).IsRequired(); 
      m.ToTable("tblCustomerRelationship"); // Define lookup value for type of Expense 
     }); 

     Property(t => t.Id).HasColumnName("CustomerRelationshipID"); 
     Property(t => t.CustomerId).HasColumnName("CustomerID"); 
     Property(t => t.RelatedId).HasColumnName("RelatedID"); 
     Property(t => t.PercentageShare).HasColumnName("PercentageShare"); 

     HasRequired(t => t.Expense) 
      .WithMany(t => t.ExpenseRelationships) 
      .HasForeignKey(t => t.RelatedId); 
    } 
} 

Una volta ho creato la voce della mappa, come sopra indicato, quando i quering GetEmployments() metodo, ora ottengo il seguente exc eption:

"I tipi di entità 'ExpenseRelationship' e 'EmploymentRelationship' non può condividere la tabella 'tblCustomerRelationship' perché non sono in stessa gerarchia tipo o non hanno uno valido per uno straniero chiave rapporto con corrispondenti chiavi primarie tra loro. ",

Che mi manca?

UPDATE

Come per i commenti jjj, Ho aggiornato il mio mappature e ha creato una classe di base CustomerRelationship.cs.

public class Employment : EntityBase 
{  
    public string EmployerName { get; set; } 

    public string EmployerContactFirstName { get; set; } 

    public string EmployerContactSurname { get; set; } 

    public virtual ICollection<EmploymentRelationship> EmploymentRelationships { get; set; } 
} 

public class Expense : EntityBase 
{ 
    public string Description { get; set; } 

    public virtual ICollection<ExpenseRelationship> ExpenseRelationships { get; set; } 
} 

public abstract class CustomerRelationship : EntityBase 
{ 
    public int CustomerId { get; set; } 

    public int? CustomerRelationshipTypeId { get; set; } 

    public int RelatedId { get; set; } 
} 

public class EmploymentRelationship : CustomerRelationship 
{ 
    public virtual Employment Employment { get; set; } 
} 

public class ExpenseRelationship: CustomerRelationship 
{ 
    public virtual Expense Expense{ get; set; } 
} 

public class CustomerRelationshipMap : BaseMap<CustomerRelationship> 
{ 
    public CustomerRelationshipMap() 
    { 
     ToTable("CustomerRelationship"); 

     Map<EmploymentRelationship>(m => m.Requires("CustomerRelationshipTypeID").HasValue(55)); 
     Map<ExpenseRelationship>(m => m.Requires("CustomerRelationshipTypeID").HasValue(60)); 

     Property(t => t.Id).HasColumnName("CustomerRelationshipID");    
     Property(t => t.CustomerId).HasColumnName("CustomerID"); 
     Property(t => t.RelatedId).HasColumnName("RelatedID");    
    } 

public class EmploymentRelationshipMap : BaseMap<EmploymentRelationship> 
{ 
    public EmploymentRelationshipMap() 
    { 
     HasRequired(t => t.Employment) 
      .WithMany(t => t.EmploymentRelationships) 
      .HasForeignKey(t => t.RelatedId); 
    } 
} 

public class ExpenseRelationshipMap : BaseMap<ExpenseRelationship> 
{ 
    public ExpenseRelationshipMap() 
    { 
     HasRequired(t => t.Expense) 
      .WithMany(t => t.ExpenseRelationships) 
      .HasForeignKey(t => t.RelatedId); 
    } 
} 

public class CustomerContext : BaseContext 
{ 
    public CustomerContext() 
     : base(context_connectionstring_main) 
    { 
    } 

    public virtual DbSet<Customer> Customers { get; set; } 
    public virtual DbSet<Employment> Employments { get; set; } 

    public virtual DbSet<CustomerRelationship> CustomerRelationships { get; set; } 
    public virtual DbSet<EmploymentRelationship> EmploymentRelationships { get; set; } 
    public virtual DbSet<ExpenseRelationship> ExpenseRelationships { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Configurations.Add(new CustomerMap()); 
     modelBuilder.Configurations.Add(new EmploymentMap()); 

     modelBuilder.Configurations.Add(new CustomerRelationshipMap()); 
     modelBuilder.Configurations.Add(new EmploymentRelationshipMap()); 
     modelBuilder.Configurations.Add(new ExpenseRelationshipMap()); 
    } 
} 

Quando interrogo contesto cliente in questo modo:

var relationships = context.CustomerRelationships.Where(l => l.CustomerId == customerId).ToList(); 

ottengo la seguente eccezione:

"La componente chiave esterna 'RelatedId' non è una proprietà dichiarata digitare 'EmploymentRelationship'. Verificare che non sia stato esplicitamente escluso dal modello e che sia una proprietà primitiva valida. ",

+1

Ok, tale errore con il componente chiave esterna ha un senso. Immagino che non sia possibile utilizzare una proprietà di classe base per un'associazione di chiavi esterne di classe derivata. Vedi http://stackoverflow.com/questions/11900155/how-to-map-foreign-keys-between-tph-tpt-objects-entity-framework-code-first e http://stackoverflow.com/questions/25619452/entity-framework-fluent-api-does-not-consider-base-class-properties – jjj

+0

Si sta utilizzando un database relazionale. Perché non vorresti modellare le relazioni usando le relazioni? – Colin

risposta

7

È necessaria una configurazione di classe base per tutte le proprietà condivise (inclusa la chiave primaria).

public class CustomerRelationshipMap : EntityTypeConfiguration<CustomerRelationship> 
{ 
    public CustomerRelationshipMap() 
    { 
     ToTable("tblCustomerRelationship"); 

     Map<EmploymentRelationship>(m => m.Requires("CustomerRelationshipTypeID").HasValue(55)); 
     Map<ExpenseRelationship>(m => m.Requires("CustomerRelationshipTypeID").HasValue(60)); 

     HasKey(t => t.Id); 
     Property(t => t.Id).HasColumnName("CustomerRelationshipID"); 
     Property(t => t.CustomerId).HasColumnName("CustomerID"); 
     Property(t => t.RelatedId).HasColumnName("RelatedID"); 
    } 
} 

Quindi, si dovrebbe essere in grado di avere la configurazione specifici della classe derivata nelle altre classi di configurazione (anche se, questo non è qualcosa che ho provato prima).

Modifica

Inoltre, non si può avere diverse associazioni di chiave esterna per classi derivate che utilizzano la stessa proprietà di classe di base. Ci sono alcune opzioni che posso pensare, ma sarebbe dipenderà dalla vostra situazione:

  1. separati chiavi esterne per l'associazione tra EmploymentRelationship-Employment e ExpenseRelationship-Expense.
  2. Dare Employment e Expense una classe base comune, come pure - anche se questo potrebbe sconfiggere lo scopo di ciò che si sta cercando di fare ....
  3. separato 1: 0..1 rapporti tra CustomerRelationship e Employment/Expense (e sbarazzarsi di EmploymentRelationship e ExpenseRelationship)
  4. TPT eredità dove Employment e Expense ereditare da CustomerRelationship (e sbarazzarsi di EmploymentRelationship e ExpenseRelationship)

Fonti

+0

@FaNIX: Scusate, avevo sbagliato quell'ultima parte. : -/Ho fatto una modifica. – jjj

+0

Cosa intendi per "chiavi esterne separate per l'associazione con occupazione e spese" – FaNIX

+0

@FaNIX: stavo pensando a colonne separate per EmploymentId e ExpenseId in CustomerRelationship. Probabilmente non è una buona soluzione – jjj

Problemi correlati