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

问题描述 投票:12回答:8

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

c# entity-framework ef-code-first entity-framework-4.3
8个回答
2
投票

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


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]

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

1
投票

这里没有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


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成员资格表(我无法更改)与我的用户表相关联,后者拥有自己的用户-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) ......好吧,那是我能做的最好:)


1
投票

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


0
投票

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

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

0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.