实体框架从流畅的映射双重外键列

时间:2017-06-30 13:23:51

标签: sql-server entity-framework

我有一个带有遗留实体结构的遗留SQL Server数据库(在这种情况下我无法更改数据库或实体)

表:

公司

   Id (PK, uniqueidentifier, not null)
   Name (varchar(200), not null)

地点

   Id (PK, uniqueidentifier, not null)
   CompanyId (FK, uniqueidentifier, not null)
   Name (varchar(200), not null)

实体:

public class Company
{
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
    public virtual ICollection<Location> Locations { get; set; }
}

public class Location
{
    public virtual Guid Id { get; set; }
    public virtual string Code { get; set; }
    public virtual string Name { get; set; }
    public virtual Company Company { get; set; }
}

带映射的模型:

public partial class CompanyModel : DbContext
{
    public virtual DbSet<Company> Companies { get; set; }
    public virtual DbSet<Location> Locations { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Location>()
            .HasRequired(e => e.Company)
            .WithMany()
            .Map(m => m.MapKey("CompanyId"));
    }
}

示例程序:

var guid = Guid.Parse("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX");

using (var mod = new CompanyModel())
{
   var company = mod.Companies.FirstOrDefault(e => e.Id == guid);
   var locations = company.Locations;
}

如果我现在运行此程序,则SQL跟踪会生成以下内容:

exec sp_executesql N'SELECT 
1 AS [C1], 
[Extent1].[Id] AS [Id], 
[Extent1].[Code] AS [Code], 
[Extent1].[Name] AS [Name], 
[Extent1].[CompanyId] AS [CompanyId], 
[Extent1].[Company_Id1] AS [Company_Id1]
FROM [dbo].[Locations] AS [Extent1]
WHERE ([Extent1].[Company_Id1] IS NOT NULL) AND ([Extent1].[Company_Id1] = @EntityKeyValue1)',N'@EntityKeyValue1 uniqueidentifier',@EntityKeyValue1='XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXXX'

如您所见,包含了Company_Id1。如果我没有在OnModelCreating中映射列,我会得到Company_Id和Compan_Id1

我确定问题在于我的映射,任何帮助都会受到赞赏。

3 个答案:

答案 0 :(得分:1)

最佳做法是在实体模型中包含FK属性。这也为外键提供了基于约定的配置。

public class Company
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Location> Locations { get; } = new HashSet<Location>();
}

public class Location
{
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
    public Guid CompanyId { get; set; }
    public virtual Company Company { get; set; }
}

答案 1 :(得分:1)

问题是由以下行引起的:

.WithMany()

基本上你告诉EF类似“我在CompanyLocation之间有关系,FK列名为LocationId(但没有明确的LocationId属性),{ {1}} Company实体中的引用导航属性和Location实体中的无集合导航属性

到目前为止一切顺利。但是,当EF在Company实体中遇到Locations集合导航属性时,按照惯例,它会考虑另一个关系而Company中没有引用导航属性,没有FK属性和自动生成FK列名称。这绝对不是你的意图。

根据经验,确保流畅的配置始终反映导航和FK属性的存在/不存在。在您的情况下,只需将上面的行更改为

Location

问题将得到解决。

答案 2 :(得分:0)

我会将公司模式改为:

public class Company
{
     [Key]  // This tells the engine that the following property id the primary key
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] // this tells the engine that the following property's value is assigned by DB
    public Guid Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Location> Locations { get; set; }
}

现在,为什么在查询中看到Company_Id1?因为在实体框架中,这是主键的默认命名约定。您为PK使用了不同的名称,因此您需要使用[Key]数据注释在模型中定义主键。

您的插入和更新不应指定此列的值(数据库应指定值),这就是您需要将[DatabaseGenerated(DatabaseGeneratedOption.Identity)]添加到模型的原因。

此外,虚拟用于导航属性(例如:外键),这就是我从公司模型中的前两个属性中删除virtual标记的原因。

您也需要对Locations模型执行相同的操作。