Question

J'utilise Entity Framework 4.3.1 Code-First et j'ai besoin de diviser une entité entre deux tables. Les tableaux ont une clé primaire partagée, et il est de 1 à 1, mais les colonnes ne sont pas nommées la même sur chaque table.

Je ne contrôle pas la disposition des données, et je ne peux pas demander de modifications.

Ainsi, par exemple, les tables SQL pourraient être

SQL data tables

Et ce serait mon entité ...

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

Et voici la cartographie que j'ai.

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

Étant donné que la clé partagée entre eux a un nom de colonne différent, je ne sais pas comment le cartographier. Ce mappage sera compilé, mais échoue au moment de l'exécution car EF émet SQL à la recherche de la colonne "ThePrimaryKeyId" sur la table "ExtendTable", qui n'existe pas.

ÉDITERPour clarifier, ce que j'ai défini ci-dessus peut (et fait) fonctionner si le PK sur les conventions de dénomination "ExtendedTable" a suivi. Mais ce n'est pas le cas et je ne peux pas changer le schéma.

Fondamentalement, ce dont j'ai besoin pour émettre, c'est une déclaration SQL comme

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]

Mais la seule chose qu'il semble vouloir émettre est

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

ÉditerJ'ai à nouveau essayé l'approche 1 à 1 à la suggestion de NSGAGA. Cela n'a pas fonctionné, mais voici les résultats. Entités

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

Voici les cours de cartographie

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

Cette configuration reçoit le message

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

Modification de la ligne de carte finale en

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

Renvoie ce message

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

Modification de la touche mappée pour utiliser la colonne à partir de la table parent, MapKey("ThePrimaryKeyId"). Renvoie ce message

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

Supprimer le Id propriété de la ExtEntity La classe lance une erreur car alors l'entité n'a pas de clé définie.

Était-ce utile?

La solution

Je ne trouve rien qui indique spécifiquement que le nom de la colonne doit être le même dans les deux tables; Mais je ne peux pas non plus trouver quoi que ce soit qui ne le dit pas, ou explique comment vous carroleriez ce scénario. Chaque exemple que je peux trouver a la clé avec le même nom dans les deux tables. Il me semble que c'est un trou dans la conception DBContext.

Autres conseils

Je travaille sur ce problème depuis quelques jours, ce que j'ai finalement fait, c'est de définir le nom de la colonne du champ ID dans Le contexte du fragment de cartographie. De cette façon, vous pouvez donner l'ID (ou la clé étrangère dépendante de l'ID) un nom différent de l'ID de la table principale.

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

Si vous courez et déboguez, vous constaterez que cela vous donnerait quelque chose comme ce que vous voulez:

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

Déplacez le nom de HasColumnnn dans le mappage:

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

Pas de studio visuel ici, mais essayez ceci avec l'approche 1 à 1:

this.hasrequure (e => e.extendProperties) .hasconstraine ((e, m) => e.id == M.ID);

Mise à jour:
Voici quelques liens qui pourraient aider (ne pourraient pas trouver de lien de référence réel)

Comment déclarer une relation à un à une seule relation à l'aide de l'entité Framework 4 Code d'abord (POCO)
Code d'entité 4 Code CTP 4 First: Comment travailler avec des noms de clés primaires et étrangers non conventionnels

Et juste pour fournir (comme je l'ai promis) une cartographie de 1 à 1 (deux entités, deux tables), pour ce qu'elle vaut.
Voici ce qui fonctionne pour moi et devrait dans votre cas ...

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

... et un code de test quelque chose comme ...

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

Cela crée le script SQL suivant, les tables ...

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

Et une note, selon notre discussion ci-dessus ...
Ce n'est pas le «divisé» - mais
(a) Code d'abord IMO ne permet rien de tel (j'ai essayé d'abord cela et aussi de modifier les migrations manuellement, mais il est `` en interne '' tout basé sur les noms de colonne attend Cette version d'EF au moins.
(b) Structure de la table en termes de structure - Les tables pourraient être faites pour regarder exactement ce dont vous avez besoin (comme je l'ai dit avant de l'utiliser pour relier les tables de membre ASPNET existantes (que je ne pouvais pas changer) en ma table utilisateur qui a un propre utilisateur -Id pointant vers l'extérieur / ASPNET Table et ID.
Certes, vous ne pouvez pas le faire en utilisant une classe de modèle C # - mais le côté C # est beaucoup plus flexible et si vous pouvez contrôler le C # qui devrait donner le même effet, à mon avis au moins (comme dans le test, vous pouvez y accéder toujours Grâce à l'entité étendue, les colonnes étendues et principales et elles sont toujours appariées 1 à 1 et restent «en synchronisation».
J'espère que cela aide certains
Remarque: vous n'avez pas à vous soucier de l'ID FK, etc. - Il suffit d'accès et d'ajouter l'entrée principale via MainEntry, et ID-S ira bien.

ÉDITER:
Vous pouvez également faire ce qui suit, pour gagner l'apparence de devoir faire face à une seule classe (c'est-à-dire une sorte de scission)

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

... et utilisez-le comme ça ...

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

... vous pouvez également retourner la direction du FK en faisant le WithRequiredPrincipal au lieu de dépendant.
(Toutes les références doivent également être sans «virtuelle» si vous avez besoin d'un à un)
(Et mainable peut être rendu «interne» comme il est ici, donc il n'est pas visible de l'extérieur - il ne peut pas être imbriqué comme ce que EF ne le permet pas - est traité comme Not Mapet)
... eh bien, c'est le mieux que je puisse faire :)

On dirait qu'il a été corrigé dans l'entité Framework 6. Voir ce problème http://entityframework.codeplex.com/Workitem/388

Je voudrais suggérer d'utiliser des annotations de données comme celle-ci:

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

J'ai fait face à ce problème et résolu par Add Column Attribut pour correspondre aux deux noms de colonne. [Key] [Column("Id")] public int GroupId { get; set; }

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top