12

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

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

例如,SQL 表可以是

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”列,该表不存在。

编辑 为了澄清,如果“扩展表”上的 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会引发错误,因为实体没有定义的键。

4

8 回答 8

3

这几天我一直在研究这个问题,我最终所做的是在映射片段的上下文中设置 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]
于 2015-04-22T09:55:07.877 回答
2

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

于 2012-03-30T22:56:43.467 回答
2

将 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");
    });
}
于 2014-06-25T12:56:57.740 回答
1

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

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

更新:
这里有一些可能有帮助的链接(找不到真正的参考链接)

如何使用实体框架 4 代码优先 (POCO) 实体框架 4 CTP 4 代码优先声明一对一关系
:如何使用非常规的主键和外键名称

于 2012-03-27T21:22:10.937 回答
1

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

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 成员表(我无法更改)关联到我的用户表之前说过,该表有一个指向外部/aspnet 表和 id 的自己的用户 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 的方向。
(如果您需要一对一,则所有引用都必须是 w/o 'virtual')
(并且 MainTable 可以在此处设置为“内部”,因此从外部看不到 - 它不能像 EF 一样嵌套不允许 - 被视为 NotMapped)
...嗯,这是我能做的最好的:)

于 2012-04-01T21:19:55.587 回答
1

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

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; }
}
于 2012-04-02T20:51:24.080 回答
1

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

于 2013-05-24T10:17:55.227 回答
0

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

于 2016-11-02T21:32:46.647 回答