Frage

Ich verwende Entity Framework 4.3.1 Code-First und muss eine Entität auf zwei Tabellen aufteilen. Die Tabellen haben einen gemeinsam genutzten Primärschlüssel und er ist 1 zu 1, aber die Spalten sind nicht in jeder Tabelle gleich benannt.

Ich kontrolliere weder das Datenlayout noch kann ich Änderungen anfordern.

So könnten beispielsweise die SQL-Tabellen sein

SQL-Datentabellen

Und das wäre meine Entität ...

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

Und hier ist die Zuordnung, die ich habe.

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

Da der zwischen ihnen gemeinsam genutzte Schlüssel einen anderen Spaltennamen hat, bin ich mir nicht sicher, wie ich ihn zuordnen soll. Diese Zuordnung wird kompiliert, schlägt jedoch zur Laufzeit fehl, da EF SQL nach der Spalte "ThePrimaryKeyId" in der Tabelle "ExtendedTable" ausgibt, die nicht vorhanden ist.

BEARBEITEN Zur Verdeutlichung kann (und funktioniert) das, was ich oben definiert habe, funktionieren, wenn die PK in der "ExtendedTable" Namenskonventionen befolgt. Aber das tut es nicht und ich kann das Schema nicht ändern.

Grundsätzlich muss EF eine SQL-Anweisung wie ausgeben

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]

Aber das einzige, was es zu emittieren scheint, ist

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

Bearbeiten Auf Vorschlag von NSGaga habe ich den 1-zu-1-Ansatz erneut versucht. Es hat nicht funktioniert, aber hier sind die Ergebnisse. Entitäten

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

Hier sind die Zuordnungsklassen

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

Dieses Setup erhält die Meldung

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

Ändern der endgültigen Kartenlinie in

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

Gibt diese Nachricht zurück

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

Ändern des zugeordneten Schlüssels, um die Spalte aus der übergeordneten Tabelle MapKey("ThePrimaryKeyId") zu verwenden. gibt diese Nachricht zurück

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

Das Entfernen der Id-Eigenschaft aus der ExtEntity-Klasse löst einen Fehler aus, da die Entität dann keinen definierten Schlüssel hat.

War es hilfreich?

Lösung

Ich kann nichts finden, das ausdrücklich besagt, dass der Name der Spalte in beiden Tabellen gleich sein muss.Aber ich kann auch nichts finden, was dies nicht sagt oder erklärt, wie Sie dieses Szenario abbilden würden.Jedes Beispiel, das ich finden kann, hat den Schlüssel mit dem gleichen Namen in beiden Tabellen.Mir scheint, dies ist ein Loch im DbContext-Design.

Andere Tipps

Ich habe einige Tage an diesem Problem gearbeitet. Schließlich habe ich den Spaltennamen des ID-Felds im Kontext des Zuordnungsfragments festgelegt.Auf diese Weise können Sie der ID (oder dem von der ID abhängigen Fremdschlüssel) einen anderen Namen als der ID der Haupttabelle geben.

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

Wenn Sie dies ausführen und debuggen, werden Sie feststellen, dass es Ihnen so etwas wie das gibt, was Sie wollen:

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

Verschieben Sie den HasColumnName in das Mapping:

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

Hier kein Visual Studio, aber versuchen Sie dies mit dem 1-zu-1-Ansatz:

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

Update:
Hier sind einige Links, die helfen könnten (es konnte kein echter Referenzlink gefunden werden)

WieDeklarieren einer Eins-zu-Eins-Beziehung mit Entity Framework 4 Code First (POCO)
Entity Framework 4 CTP4 Code First: Umgang mit unkonventionellen Primär- und Fremdschlüsselnamen

Und nur um (wie versprochen) ein 1-zu-1-Mapping (zwei Entitäten, zwei Tabellen) für das bereitzustellen, was es wert ist.
Folgendes funktioniert für mich und sollte in Ihrem Fall ...

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

... und ein Testcode wie ...

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

Dadurch werden das folgende SQL-Skript, Tabellen ... erstellt

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])

Und ein Hinweis gemäß unserer obigen Diskussion ...
Dies ist nicht die "Spaltung" - aber
(a) Code zuerst IMO erlaubt so etwas nicht (ich habe das zuerst versucht und auch die Migrationen manuell geändert, aber es ist "intern", basierend darauf, dass die erwarteten Spaltennamen gleich sind und es keinen Weg daran vorbei gibt, z mindestens diese Version von EF.
(b) Tabellenstruktur weise - die Tabellen könnten so gestaltet werden, dass sie genau so aussehen, wie Sie es benötigen (wie ich bereits sagte, bevor ich sie verwendet habe, um die vorhandenen Aspnet-Mitgliedschaftstabellen (die ich nicht ändern konnte) in meine Benutzertabelle mit einem eigenen Benutzer zu beziehen -id zeigt auf die Tabelle / ID von extern / aspnet.
Sie können es zwar nicht mit einer C # -Modellklasse schaffen - aber die C # -Seite ist viel flexibler, und wenn Sie das C # steuern können, das meiner Meinung nach den gleichen Effekt haben sollte (wie im Test, können Sie immer darauf zugreifen) Durch die erweiterte Entität werden sowohl die erweiterte als auch die Hauptspalte immer 1 zu 1 abgeglichen und bleiben 'synchron'.
Hoffe das hilft einigen
HINWEIS: Sie müssen sich keine Gedanken über die fk-ID usw. machen - greifen Sie einfach immer über MainEntry auf den Haupteintrag zu und fügen Sie ihn hinzu, und die IDs sind in Ordnung.

BEARBEITEN:
Sie können auch Folgendes tun, um den Eindruck zu erwecken, dass Sie sich nur mit einer Klasse befassen müssen (d. H. Eine Art Split)

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

... und benutze es so ...

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

... Sie können auch die Richtung des fk zurücksetzen, indem Sie den WithRequiredPrincipal anstelle von abhängig ausführen.
(Auch alle Referenzen müssen ohne 'virtuell' sein, wenn Sie eins zu eins benötigt haben)
(und MainTable kann wie hier 'intern' gemacht werden, so dass es von außen nicht sichtbar ist - es kann nicht verschachtelt werden, da EF dies nicht zulässt - wird wie NotMapped behandelt)
... nun, das ist das Beste, was ich tun kann :)

Sieht so aus, als ob es in Entity Framework 6 behoben wurde. Siehe dieses Problem http://entityframework.codeplex.com/ workitem / 388

Ich möchte vorschlagen, einige Datenanmerkungen wie diese zu verwenden:

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

Ich war mit diesem Problem konfrontiert und wurde durch Hinzufügen eines Spaltenattributs gelöst, das mit den beiden Spaltennamen übereinstimmt. [Key] [Column("Id")] public int GroupId { get; set; }

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top