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:
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)
Creare record della tabella collocamento (EmploymentID = 5)
La struttura sopra descritta funzionerà per tutte le entità legate a un cliente.
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.
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. ",
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
Si sta utilizzando un database relazionale. Perché non vorresti modellare le relazioni usando le relazioni? – Colin