@slauma在评论中给出了一个关键事实。所以看看答案和评论吧!
我正在尝试使用组件,实际上与NHibernate一起使用,现在使用EF6。问题是,我有一些具有不同名称主键的TPT继承。数据库和POCO类是给定的,我不能改变它们中的任何一个,所以CodeFirst和EF设计器都是没有问题的。
是否有一种方法可以将现有的Db映射到现有的POCO类,就像你在NHibernate中使用这些.hbm.xml映射文件一样?
更新:
我遇到的实际问题首先是几个类的TPT映射,其中这些类具有不同的命名主键,这似乎首先不被代码支持。
:
public class Record
{
public virtual int Ndx { get; set; } // table column 'ndx'
public virtual DateTime CreatedAt { get; set; } // table column 'created'
// ... further properties
}
public class Patient : Record
{
public virtual int RecordNdx {get; set;} // table column 'record_ndx) with FK => records.ndx
// ... further properties
}
如前所述,不能更改属性或列名。
更新2:我的注册码:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Record>()
.ToTable("record_descriptors", "schema");
modelBuilder.Entity<Record>()
.HasKey<int>(e => e.ndx);
modelBuilder.Entity<Record>()
.Property(e => e.read_flag)
.IsFixedLength()
.IsUnicode(false);
modelBuilder.Entity<Record>()
.Property(e => e.row_version)
.IsFixedLength();
modelBuilder.Entity<Record>()
.Property(e => e.update_info)
.IsUnicode(false);
modelBuilder.Entity<Patient>()
.ToTable("patienten", "schema");
modelBuilder.Entity<Patient>()
.Property(e => e.mpi)
.IsUnicode(false);
modelBuilder.Entity<Patient>()
.Property(e => e.ndx)
.HasColumnName("record_ndx");
modelBuilder.Entity<Patient>()
.Ignore(r => r.RecordNdx);
}
更新三世
对于测试我使用:
db.patients.First(p => p.Ndx == 6040);
,这产生了以下SQL(更广泛,因为真实的记录&病人类):
SELECT
[Limit1].[C1] AS [C1],
[Limit1].[ndx] AS [ndx],
[Limit1].[owner_user_object_ndx] AS [owner_user_object_ndx],
[Limit1].[creator_department_user_object_ndx] AS [creator_department_user_object_ndx],
[Limit1].[creator_user_user_object_ndx] AS [creator_user_user_object_ndx],
[Limit1].[created] AS [created],
[Limit1].[read_flag] AS [read_flag],
[Limit1].[last_update] AS [last_update],
[Limit1].[last_update_user] AS [last_update_user],
[Limit1].[last_update_department] AS [last_update_department],
[Limit1].[freitext] AS [freitext],
[Limit1].[row_version] AS [row_version],
[Limit1].[update_info] AS [update_info],
[Limit1].[mpi] AS [mpi]
FROM ( SELECT TOP (1)
[Extent1].[ndx] AS [ndx],
[Extent1].[mpi] AS [mpi],
[Extent2].[owner_user_object_ndx] AS [owner_user_object_ndx],
[Extent2].[creator_department_user_object_ndx] AS [creator_department_user_object_ndx],
[Extent2].[creator_user_user_object_ndx] AS [creator_user_user_object_ndx],
[Extent2].[created] AS [created],
[Extent2].[read_flag] AS [read_flag],
[Extent2].[last_update] AS [last_update],
[Extent2].[last_update_user] AS [last_update_user],
[Extent2].[last_update_department] AS [last_update_department],
[Extent2].[freitext] AS [freitext],
[Extent2].[row_version] AS [row_version],
[Extent2].[update_info] AS [update_info],
'0X0X' AS [C1]
FROM [schema].[patienten] AS [Extent1]
INNER JOIN [schema].[record_descriptors] AS [Extent2] ON [Extent1].[ndx] = [Extent2].[ndx]
WHERE 6040 = [Extent1].[ndx]
) AS [Limit1]
是错误的,因为它从[patienten]
(必须是record_ndx
)中选择了[ndx]
,并且还试图连接[ndx]
关于在CodePlex上关闭这个工作项的注释声称自EF 6以来在TPT映射中定义父实体和子实体的不同键列名称使用Code-First工作。如果这是真的,下面的Code-First映射应该允许映射你的模型和数据库:
modelBuilder.Entity<Record>()
.ToTable("YourRecordTableName");
modelBuilder.Entity<Record>()
.HasKey(r => r.Ndx);
modelBuilder.Entity<Record>()
.Property(r => r.Ndx)
.HasColumnName("ndx"); // probably redundant because case doesn't matter
modelBuilder.Entity<Record>()
.Property(r => r.CreatedAt)
.HasColumnName("created");
modelBuilder.Entity<Patient>()
.ToTable("YourPatientTableName");
modelBuilder.Entity<Patient>()
.Property(r => r.Ndx) // Yes, no typo: It must be Ndx, NOT RecordNdx !
.HasColumnName("record_ndx");
modelBuilder.Entity<Patient>()
.Ignore(r => r.RecordNdx);
最后一个映射(忽略RecordNdx
属性)很重要。这意味着您的键属性将是Patient.Ndx
。我不认为你可以在派生类中设置任何属性为键属性。key属性必须始终位于继承层次结构的基类中。然而,这个属性可以映射两次(或者通常在TPT继承链中每个实体一次)到每个表的不同列名——从EF 6开始。
完全去掉RecordNdx
属性将是最干净的解决方案。但是既然你说你不能触摸你的属性,那么至少将RecordNdx
的值与Ndx
属性耦合是有意义的(如果你可以改变属性的getter和setter):
public virtual int RecordNdx
{
get { return Ndx; }
set { Ndx = value; }
}
编辑
我刚刚用EF 6.1测试了上面的Code-First映射,它确实有效!Record
表中的主键列为ndx
, Patient
表中的主键列为record_ndx
。在这些EF之间创建了TPT映射所需的一对一关系。
编辑2
这是我使用的完整的测试程序(当前的EF 6.1 Nuget包,.NET 4.5, VS 2012, SQL Server 2012 Express):
using System;
using System.Data.Entity;
namespace EFTPT6
{
public class Record
{
public virtual int Ndx { get; set; }
public virtual DateTime CreatedAt { get; set; }
}
public class Patient : Record
{
public virtual int RecordNdx { get; set; }
public virtual string Name { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Record> Records { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Record>()
.ToTable("Records");
modelBuilder.Entity<Record>()
.HasKey(r => r.Ndx);
modelBuilder.Entity<Record>()
.Property(r => r.Ndx)
.HasColumnName("ndx");
modelBuilder.Entity<Record>()
.Property(r => r.CreatedAt)
.HasColumnName("created");
modelBuilder.Entity<Patient>()
.ToTable("Patients");
modelBuilder.Entity<Patient>()
.Property(r => r.Ndx)
.HasColumnName("record_ndx");
modelBuilder.Entity<Patient>()
.Ignore(p => p.RecordNdx);
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (var ctx = new MyContext())
{
ctx.Database.Initialize(true);
string sql = ctx.Records.ToString();
}
}
}
}
程序末尾的字符串sql
是:
SELECT
CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))
THEN '0X'
ELSE '0X0X'
END AS [C1],
[Extent1].[ndx] AS [ndx],
[Extent1].[created] AS [created],
CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))
THEN CAST(NULL AS varchar(1))
ELSE [Project1].[Name]
END AS [C2]
FROM [dbo].[Records] AS [Extent1]
LEFT OUTER JOIN (SELECT
[Extent2].[record_ndx] AS [record_ndx],
[Extent2].[Name] AS [Name],
cast(1 as bit) AS [C1]
FROM [dbo].[Patients] AS [Extent2] ) AS [Project1]
ON [Extent1].[ndx] = [Project1].[record_ndx]
看起来映射是受尊重的,即Records
和Patients
表由ndx
和record_ndx
列连接。
编辑3
重要的是上下文类不包含派生实体的集合,即没有public DbSet<Patient> Patients { get; set; }
。如果是这样,则忽略modelBuilder.Entity<Patient>().Property(r => r.Ndx).HasColumnName("record_ndx");
映射,EF期望Patient
中的主键名是ndx
而不是record_ndx
。例如,上面SQL的最后一行变成了ON [Extent1].[ndx] = [Project1].[ndx]
。