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

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

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

例如,SQL表可以是

SQL数据表

这将是我的实体……

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

这是我的映射。

 public class MyEntityMapping : EntityTypeConfiguration { 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”列,该列不存在。

编辑为了澄清,如果“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 */ 

编辑我在NSGaga的建议中再次尝试了一对一的方法。 它不起作用,但结果如下。 实体

 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 { 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 { 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'" 

将最终地图线更改为

 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属性会引发错误,因为实体没有定义的键。

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

我已经在这个问题上工作了几天,我最终做的是在映射片段的上下文中设置Id字段的列名。 这样,您可以为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] 

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

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

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

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

如何使用Entity Framework 4 Code First(POCO)声明一对一关系
entity framework4 CTP 4代码优先:如何使用非常规的主要和外键名称

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

 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 MainEntries { get; set; } public DbSet ExtendedEntries { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity() .HasKey(x => new { x.ThePrimaryKeyId }); modelBuilder.Entity() .HasKey(x => new { x.NotTheSameNameID }); // Extended To Main 1 on 1 modelBuilder.Entity() .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]) 

根据我们上面的讨论,请注意……
这不是“分裂” – 但是
(a)代码首先IMO不允许任何类似的东西(我先尝试过,然后手动修改迁移,但是’内部’都基于预期的列名相同而且似乎无法解决它,因为至少这个版本的EF。
(b)明智的表结构 – 可以使表格看起来完全符合您的需求(正如我之前所说的那样,我将它用于将现有的aspnet成员资格表(我无法更改)与我的用户表相关联,该用户表具有自己的用户-id指向外部/ aspnet表和id。
没错,你不能使用一个C#模型类 – 但C#方面更灵活,如果你能控制应该产生相同效果的C#,至少在我看来(比如在测试中,你可以随时访问它)通过扩展实体,扩展实体和主列,它们总是匹配1到1并保持“同步”。
希望这会有所帮助
注意:您不必担心fk id等 – 只需始终通过MainEntry访问并添加Main条目,id-s就可以了。

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

 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的方向。
(如果你需要一对一的话,所有引用都必须是’虚拟的’)
(并且MainTable可以在这里制作’内部’,因此它从外部看不到 – 它不能嵌套,因为EF不允许 – 被视为NotMapped)
……好吧,那是我能做的最好的:)

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

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

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

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