Domanda

Sto usando Entity Framework 4.3.1 Code-First e ho bisogno di dividere un'entità tra due tabelle. Le tabelle hanno una chiave primaria condivisa ed è 1 a 1, ma le colonne non hanno lo stesso nome in ogni tabella.

Non controllo il layout dei dati, né posso richiedere modifiche.

Quindi, ad esempio, le tabelle SQL potrebbero essere

tabelle dati SQL

E questa sarebbe la mia entità ...

public class MyEntity
{
    public int Id {get; set;}
    public string Name {get;set}
    public string FromAnotherTable {get;set;}
}

Ed ecco la mappatura che ho.

public class MyEntityMapping : EntityTypeConfiguration<MyEntity>
{
    public MyEntityMapping()
    {
        this.Property(e => e.Id).HasColumnName("ThePrimaryKeyId");
        this.Property(e => e.Name).HasColumnName("MyDatabaseName");
        this.Property(e => e.FromAnothertable).HasColumnName("AnotherTableColumn");
        this.Map(m =>
            {
                m.Properties(e =>
                     {
                         e.Id,
                         e.Name
                     });
                m.ToTable("MainTable");
            });
        this.Map(m =>
            {
                m.Properties(e =>
                     {
                         e.Id,
                         e.FromAnotherTable
                     });
                m.ToTable("ExtendedTable");
            });
}

Poiché la chiave condivisa tra loro ha un nome di colonna diverso, non sono sicuro di come mapparla. Questa mappatura verrà compilata, ma non riesce a runtime perché EF emette SQL cercando la colonna "ThePrimaryKeyId" nella tabella "ExtendedTable", che non esiste.

MODIFICA Per chiarire, ciò che ho definito sopra può (e funziona) se il PK su "ExtendedTable" ha seguito le convenzioni di denominazione. Ma non è così e non posso cambiare lo schema.

Fondamentalmente, quello che ho bisogno che EF emetta è un'istruzione SQL come

SELECT
    [e1].*,   /*yes, wildcards are bad. doing it here for brevity*/
    [e2].*
FROM [MainTable] AS [e1]
INNER JOIN [ExtendedTable] AS [e2]  /*Could be left join, don't care. */
    ON  [e1].[ThePrimaryKeyId] = [e2].[NotTheSameName]

Ma l'unica cosa che sembra voler emettere è

 SELECT
        [e1].*,
        [e2].*
    FROM [MainTable] AS [e1]
    INNER JOIN [ExtendedTable] AS [e2]
        ON  [e1].[ThePrimaryKeyId] = [e2].[ThePrimaryKeyId] /* this column doesn't exist */

Modifica Ho provato di nuovo l'approccio 1 a 1 su suggerimento di NSGaga. Non ha funzionato, ma ecco i risultati. Entità

public class MyEntity
{
    public int Id { get; set; }
    public int Name { get; set; }
    public virtual ExtEntity ExtendedProperties { get; set; }
}
public class ExtEntity
{
    public int Id { get; set; }
    public string AnotherTableColumn { get; set; }
    public virtual MyEntity MainEntry { get; set; }
}

Ecco le classi di mappatura

public class MyEntityMapping : EntityTypeConfiguration<MyEntity>
{
    public MyEntityMapping()
    {
        this.Property(e => e.Id).HasColumnName("ThePrimaryKeyId");
        this.Property(e => e.Name).HasColumnName("MyDatabaseName");
        this.ToTable("MainTable");
        this.HasKey(e => e.Id);
        this.HasRequired(e => e.ExtendedProperties).WithRequiredPrincipal(f => f.MainEntry);
    }
}

public class ExtEntityMapping : EntityTypeConfiguration<ExtEntity>
{
    public ExtEntityMapping()
    {
        this.Property(e => e.Id).HasColumnName("NotTheSameName");
        this.Property(e => e.AnotherTableColumn).HasColumnName("AnotherTableColumn");
        this.ToTable("ExtendedTable");
        this.HasKey(e => e.Id);
        this.HasRequired(e => e.MainEntry).WithRequiredDependent(f => f.ExtendedProperties);
    }
}

Questa configurazione riceve il messaggio

"Column or attribute 'MyEntity_ThePrimaryKeyId' is not defined in 'ExtendedTable'"

Modifica della linea della mappa finale in

this.HasRequired(e => e.MainEntry).WithRequiredDependent(f => f.ExtendedProperties).Map(m => M.MapKey("NotTheSameName"));

Restituisce questo messaggio

"Each property name in a type must be unique. property name 'NotTheSameName' was already defined."

Modifica della chiave mappata per utilizzare la colonna dalla tabella padre, MapKey("ThePrimaryKeyId"). restituisce questo messaggio

"Column or attribute 'ThePrimaryKeyId' is not defined in 'ExtendedTable'"

La rimozione della proprietà Id dalla classe ExtEntity genera un errore perché l'entità non ha una chiave definita.

È stato utile?

Soluzione

Non riesco a trovare nulla che indichi specificamente che il nome della colonna deve essere lo stesso in entrambe le tabelle;ma non riesco nemmeno a trovare qualcosa che dica di no, o che spieghi come mapperesti quello scenario.Ogni esempio che riesco a trovare ha la chiave con lo stesso nome in entrambe le tabelle.Mi sembra che questo sia un buco nel design di DbContext.

Altri suggerimenti

Sto lavorando proprio a questo problema da alcuni giorni, quello che alla fine ho fatto è stato impostare il nome della colonna del campo Id all'interno del contesto del frammento di mappatura.In questo modo puoi dare all'ID (o alla chiave esterna dipendente dall'ID) un nome diverso dall'ID della tabella principale.

this.Map(m =>
    {
        m.Property(p => p.Id).HasColumnName("NotTheSameName");
        m.Properties(e =>
             {
                 e.Id,
                 e.FromAnotherTable
             });
        m.ToTable("ExtendedTable");
    });

Se lo esegui ed esegui il debug, scoprirai che ti darà qualcosa come quello che desideri:

[e1].[ThePrimaryKeyId] = [e2].[NotTheSameName]

Sposta HasColumnName all'interno della mappatura:

this.Property(e => e.FromAnothertable).HasColumnName("AnotherTableColumn");
this.Map(m =>
    {
        m.Properties(e => new
             {
                 e.Id,
                 e.Name
             });
             m.Property(e => e.Id).HasColumnName("ThePrimaryKeyId");
             m.Property(e => e.Name).HasColumnName("MyDatabaseName");

           m.Property(e => e.Id).HasColumnName("ThePrimaryKeyId");
        m.ToTable("MainTable");
    });
this.Map(m =>
    {
        m.Properties(e => new
             {
                 e.Id,
                 e.FromAnotherTable
             });
        m.ToTable("ExtendedTable");
    });
}

Nessun Visual Studio qui, ma prova questo con l'approccio 1 a 1:

this.HasRequired (e=> e.ExtendedProperties) .HasConstraint ((e, m)=> e.Id== m.Id);

Aggiorna:
Di seguito sono riportati alcuni collegamenti che potrebbero aiutare (impossibile trovare un collegamento di riferimento reale)

Comeper dichiarare una relazione uno a uno utilizzando Entity Framework 4 Code First (POCO)
Entity Framework 4 CTP4 Code First: come lavorare con nomi di chiavi primarie ed esterne non convenzionali

E solo per fornire (come avevo promesso) una mappatura 1 a 1 (due entità, due tabelle), per quello che vale.
Ecco cosa funziona per me e dovrebbe nel tuo caso ...

public class MainTable
{
    public int ThePrimaryKeyId { get; set; }
    public string Name { get; set; }
}
public class ExtendedTable
{
    public int NotTheSameNameID { get; set; }
    public string AnotherTableColumn { get; set; }
    public MainTable MainEntry { get; set; }
}
public class MainDbContext : DbContext
{
    public DbSet<MainTable> MainEntries { get; set; }
    public DbSet<ExtendedTable> ExtendedEntries { get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MainTable>()
            .HasKey(x => new { x.ThePrimaryKeyId });

        modelBuilder.Entity<ExtendedTable>()
            .HasKey(x => new { x.NotTheSameNameID });

        // Extended To Main 1 on 1
        modelBuilder.Entity<ExtendedTable>()
            .HasRequired(i => i.MainEntry)
            .WithRequiredDependent();
    }
}

... e un codice di prova simile a ...

using (var db = new UserDbContext())
{
    foreach (var userid in Enumerable.Range(1, 100))
    {
        var main = new MainTable { Name = "Main" + userid };
        db.MainEntries.Add(main);

        var extended = new ExtendedTable { AnotherTableColumn = "Extended" + userid, MainEntry = main };
        db.ExtendedEntries.Add(extended);
    }
    int recordsAffected = db.SaveChanges();
    foreach (var main in db.MainEntries)
        Console.WriteLine("{0}, {1}", main.Name, main.ThePrimaryKeyId);
    foreach (var extended in db.ExtendedEntries)
        Console.WriteLine("{0}, {1}, {2}, {3}", extended.AnotherTableColumn, extended.NotTheSameNameID, extended.MainEntry.Name, extended.MainEntry.ThePrimaryKeyId);
}

Questo crea il seguente script SQL, tabelle ...

CREATE TABLE [MainTables] (
    [ThePrimaryKeyId] [int] NOT NULL IDENTITY,
    [Name] [nvarchar](4000),
    CONSTRAINT [PK_MainTables] PRIMARY KEY ([ThePrimaryKeyId])
)
CREATE TABLE [ExtendedTables] (
    [NotTheSameNameID] [int] NOT NULL,
    [AnotherTableColumn] [nvarchar](4000),
    CONSTRAINT [PK_ExtendedTables] PRIMARY KEY ([NotTheSameNameID])
)
CREATE INDEX [IX_NotTheSameNameID] ON [ExtendedTables]([NotTheSameNameID])
ALTER TABLE [ExtendedTables] ADD CONSTRAINT [FK_ExtendedTables_MainTables_NotTheSameNameID] FOREIGN KEY ([NotTheSameNameID]) REFERENCES [MainTables] ([ThePrimaryKeyId])

E una nota, come da nostra discussione sopra ...
Questa non è la "scissione", ma
(a) il codice prima IMO non consente nulla del genere (l'ho provato prima e ho anche modificato le migrazioni manualmente, ma è `` internamente '' tutto basato sul fatto che i nomi delle colonne previsti siano gli stessi e sembra che non ci sia modo di aggirarlo, perché almeno questa versione di EF.
(b) struttura della tabella saggia: le tabelle potrebbero essere fatte per sembrare esattamente ciò di cui hai bisogno (come ho detto prima l'ho usato per mettere in relazione le tabelle di appartenenza aspnet esistenti (che non potevo cambiare) nella mia tabella utente che ha un proprio utente -id che punta a una tabella e un ID esterni / aspnet.
È vero, non puoi farlo usando una classe del modello C #, ma il lato C # è molto più flessibile e se puoi controllare il C # che dovrebbe dare lo stesso effetto, almeno a mio parere (come nel test, puoi accedervi sempre attraverso l'entità estesa, sia la colonna estesa che quella principale e sono sempre abbinate 1 a 1 e rimangono "sincronizzate".
Spero che questo aiuti alcuni
NOTA: non devi preoccuparti dell'id fk ecc. - accedi e aggiungi sempre la voce principale tramite MainEntry, e id-s andrà bene.

MODIFICA:
Potresti anche fare quanto segue, per dare l'impressione di avere a che fare con una sola classe (cioè una specie di divisione)

public class ExtendedTable
{
    public int NotTheSameNameID { get; set; }
    public string AnotherTableColumn { get; set; }

    public string Name { get { return MainEntry.Name; } set { MainEntry.Name = value; } }
    // public int MainID { get { return MainEntry.ThePrimaryKeyId; } set { MainEntry.ThePrimaryKeyId = value; } }
    internal MainTable MainEntry { get; set; }

    public ExtendedTable()
    {
        this.MainEntry = new MainTable();
    }
}

... e usalo in questo modo ...

var extended = new ExtendedTable { AnotherTableColumn = "Extended" + userid, Name = "Main" + userid };  

... inoltre puoi invertire la direzione di fk eseguendo WithRequiredPrincipal invece di dipendente.
(anche tutti i riferimenti devono essere senza "virtuale" se hai richiesto uno a uno)
(e MainTable può essere reso "interno" in quanto è qui, quindi non è visibile dall'esterno - non può essere annidato perché EF non lo consente - viene trattato come NotMapped)
... beh, questo è il meglio che potessi fare :)

Sembra che sia stato risolto in Entity Framework 6. Consulta questo problema http://entityframework.codeplex.com/ workitem / 388

Vorrei suggerire di utilizzare alcune annotazioni di dati come questa:

MainTable
---------
MainTableId
DatabaseName

ExtendedTable
----------
NotTheSameName
AnotherColumn

public class MainTable
{
 [Key]
 public int MainTableId { get; set; }
 public string DatabaseName { get; set; }

 [InverseProperty("MainTable")]
 public virtual ExtendedTable ExtendedTable { get; set; }
}

public class ExtendedTable
{
 [Key]
 public int NotTheSameName { get; set; }

 public string AnotherColumn { get; set; }

 [ForeignKey("NotTheSameName")]
 public virtual MainTable MainTable { get; set; }
}

Ho riscontrato questo problema e l'ho risolto aggiungendo l'attributo Column in modo che corrispondesse a entrambi i nomi di colonna. [Key] [Column("Id")] public int GroupId { get; set; }

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top