繁体   English   中英

在Entity Framework 6中仅将现有数据库映射到现有POCO,而无需设计器或CodeFirst

[英]Solely mapping existing db to existing POCOs without designer or CodeFirst in Entity Framework 6

@slauma的一个关键事实发表了评论。 因此,请查看答案和评论!

我正在尝试使用EF6现在与NHibernate实际一起使用的组件。 问题是,我有一些带有不同名称的主键的TPT继承。 给出了数据库和POCO类,我无法更改它们,因此CodeFirst和EF设计器都毫无疑问。

有没有办法像将NHibernate中的这些.hbm.xml映射文件一样,将现有的Db映射到现有的POCO类?

更新:

我遇到的实际问题首先是几个类的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
}

如前所述,更改属性或列名称不是一种选择。

更新二:

这是我的注册码:

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]选择[ndx] (必须为record_ndx ),并且还尝试加入[ndx]

关于在CodePlex上关闭此工作项的评论声称, 由于EF 6在TPT映射中定义父实体和子实体的不同键列名称,因此可以与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属性必须始终位于继承层次结构的基类中。 但是,从EF 6开始,此属性可以映射两次(或者通常在TPT继承链中每个实体映射一次)到每个表的不同列名。

完全摆脱RecordNdx属性将是最干净的解决方案。 但是,因为您说过您无法触摸属性,所以至少将RecordNdx的值RecordNdxNdx属性是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]

看起来映射已得到遵守,即RecordsPatients表由ndxrecord_ndx列连接。

编辑3

重要的是,上下文类不包含派生实体的集合,即没有public DbSet<Patient> Patients { get; set; } public DbSet<Patient> Patients { get; set; } 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]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM