首页 文章

密钥列具有不同名称时实体拆分?

提问于
浏览
12

我正在使用Entity Framework 4.3.1 Code-First,我需要在两个表之间拆分实体 . 这些表共享一个主键,它是1对1,但每个表上的列名称不相同 .

我不控制数据布局,也不能请求任何更改 .

例如,SQL表可以是

SQL data tables

这将是我的实体......

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

这是我的映射 .

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

由于它们之间共享的密钥具有不同的列名,因此我不确定如何映射它 . 此映射将编译,但在运行时失败,因为EF发出SQL查找“ExtendedTable”表上的“ThePrimaryKeyId”列,该列不存在 .

EDIT 为了澄清,如果"ExtendedTable"上的PK遵循命名约定,我上面定义的内容可以(并且确实)起作用 . 但它没有改变架构 .

基本上,我需要EF发出的是一个类似的SQL语句

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]

但它似乎唯一想要发出的是

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

Edit 我在NSGaga 's suggestion. It didn'工作时再次尝试了一对一的方法,但结果如下 . 实体

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

这是映射类

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

此设置获取消息

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

将最终 Map 线更改为

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

返回此消息

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

更改映射键以使用父表中的列 MapKey("ThePrimaryKeyId") . 返回此消息

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

ExtEntity 类中删除 Id 属性会引发错误,因为该实体没有定义的键 .

8 回答

  • 1

    我找不到任何明确说明两个表中列的名称必须相同的内容;但是我也找不到任何说它没有的东西,或者解释你将如何映射那个场景 . 我能找到的每个例子都有两个表中同名的密钥 . 在我看来,这是DbContext设计中的一个漏洞 .

  • 0

    我已经在这个问题上工作了几天,我最终做的是设置Id字段的列名称 within 映射片段的上下文 . 这样,您可以为Id(或依赖于Id的外键)提供与主表的Id不同的名称 .

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

    如果你运行并调试它,你会发现它会给你一些你想要的东西:

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

    将HasColumnName移动到映射中:

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

    这里没有Visual Studio,但尝试使用1对1方法:

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

    Update:
    以下是一些可能有用的链接(无法找到真正的参考链接)

    How to declare one to one relationship using Entity Framework 4 Code First (POCO)
    Entity Framework 4 CTP 4 Code First: how to work with unconventional primary and foreign key names

  • 0

    并且只是提供(如我所承诺的)1对1(两个实体,两个表)映射,以获得它的 Value .
    这对我有用,在你的情况下应该......

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

    ...和测试代码类似......

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

    这会创建以下SQL脚本,表格......

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

    还有一张说明,根据我们上面的讨论......
    这个''t the '分裂' - 但是
    (a)代码首先IMO没有't allow anything like that (I tried that first and also modifying the migrations manually but it' s 'internally'全部基于预期的列名相同而且似乎没有办法绕过它,至少对于这个版本的EF .
    (b)明智的表结构 - 可以使表格看起来完全符合您的需求(正如我之前所说的那样,我将它用于将现有的aspnet成员资格表(我无法更改)与我的用户表相关联,后者拥有自己的用户-id指向外部/ aspnet表和id .
    没错,你不能使用一个C#模型类 - 但是C#方面更灵活,如果你可以控制应该产生相同效果的C#,至少在我看来(比如在测试中,你可以随时访问它)通过扩展实体,扩展实体和主列,他们're always matched 1 to 1 and stay '同步' .
    希望这会有所帮助
    注意:您不必担心fk id等 - 只需始终通过MainEntry访问并添加Main条目,id-s就可以了 .

    EDIT:
    您还可以执行以下操作,以获得只需处理一个类(即分类)的外观

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

    ......并像这样使用它......

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

    ...你也可以通过 WithRequiredPrincipal 而不是依赖来恢复fk的方向 .
    (如果你需要一对一的话,所有引用都必须是没有'virtual')
    (并且可以将MainTable设为'internal',因为它不能从外部看到 - 它不能嵌套,因为EF不允许 - 被视为NotMapped)
    ......好吧,那是我能做到的最好的做:)

  • 1

    看起来它已在Entity Framework 6中修复 . 请参阅此问题http://entityframework.codeplex.com/workitem/388

  • 2

    我想建议使用这样的一些数据注释:

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

    我遇到了这个问题,并通过添加Column属性以匹配两个列名来解决 . [Key] [Column("Id")] public int GroupId { get; set; }

相关问题