密钥列具有不同名称时实体拆分?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了密钥列具有不同名称时实体拆分?相关的知识,希望对你有一定的参考价值。
我正在使用Entity Framework 4.3.1 Code-First,我需要在两个表之间拆分实体。这些表共享一个主键,它是1对1,但每个表上的列名称不相同。
我不控制数据布局,也不能请求任何更改。
例如,SQL表可以是
这将是我的实体......
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”列,该列不存在。
编辑为了澄清,如果“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<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'"
将最终地图线更改为
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'"
从Id
类中删除ExtEntity
属性会引发错误,因为实体没有定义的键。
我找不到任何明确说明两个表中列的名称必须相同的内容;但是我也找不到任何说它没有的东西,或者解释你将如何映射那个场景。我能找到的每个例子都有两个表中同名的密钥。在我看来,这是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);
更新: 以下是一些可能有用的链接(无法找到真正的参考链接)
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
并且只是提供(正如我所承诺的)一对一(两个实体,两个表)映射,以获得它的价值。 这对我有用,在你的情况下应该......
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])
还有一张说明,根据我们上面的讨论...... 这不是“分裂” - 但是 (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; }
以上是关于密钥列具有不同名称时实体拆分?的主要内容,如果未能解决你的问题,请参考以下文章