Entity Framework Fluent API 在相关对象的 sql 中生成额外的列

Entity Framework Fluent API generating extra columns in sql on related object

我有一个预先存在的数据模型,最初使用 EF 4 访问。更新到 EF 6,遇到配置语法更改的问题,并定义要正确查询的关系。

在这种特定情况下,我的外键关系在 SQL 中生成两列,一个是我定义的,一个是从哪里来的...

我有以下两个对象 - 公司和 AppUser:

public class Company : EntityBase, IComparable<Company>
{
    public string Name { get; set; }
    public virtual IList<AppSystem> AppSystems { get; set; }
    public virtual IList<AppUser> AppUsers { get; set; }
    public string PortalCustomerName { get; set; }
}

public class AppUser : EntityBase
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public virtual Company Company { get; set; }
    public FlowStatus FlowStatus { get; set; }
    public virtual IList<AppUserRole> AppUserRoles { get; set; }
}

public abstract class EntityBase
{
    /// <summary>
    /// The id assigned by the system.
    /// </summary>
    public virtual int Id { get; set; }
}

两个类的配置:

public class CompanyConfiguration : EntityTypeConfiguration<Company>
    public CompanyConfiguration()
    {
        ToTable("Company");
        Property(c => c.Id).HasColumnName("CompanyID");
        Property(c => c.Name).HasMaxLength(50).IsRequired();
        Property(c => c.PortalCustomerName).HasMaxLength(50).IsRequired();
        HasMany(c => c.AppSystems);
        HasMany(c => c.AppUsers);
    }
}

public class AppUserConfiguration : EntityTypeConfiguration<AppUser>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="AppUserConfiguration"/> class with default values.
    /// </summary>
    public AppUserConfiguration()
    {
        ToTable("AppUser");
        Property(u => u.Id).HasColumnName("AppUserId");
        Property(u => u.Email).HasMaxLength(256).IsRequired();
        Property(u => u.FirstName).HasMaxLength(50);
        Property(u => u.LastName).HasMaxLength(50);
        Property(u => u.FlowStatus.Value).HasColumnName("FlowStatus");
        HasRequired(u => u.Company).WithMany().Map(m => m.MapKey("CompanyID"));
    }
}

数据模型为:

当我查询公司时,没问题,我得到 SQL 匹配

SELECT 
[Extent1].[CompanyID] AS [CompanyID], 
[Extent1].[Name] AS [Name], 
[Extent1].[PortalCustomerName] AS [PortalCustomerName]
FROM [dbo].[Company] AS [Extent1]
WHERE N'Joe''s Diner' = [Extent1].[Name]
ORDER BY [Extent1].[Name] ASC

但是,当我查询 AppUsers 时遇到了查询问题。通过上面的配置,我得到了正确的 CompanyId 请求,但我也得到了一个额外的列,Company_Id1:

SELECT 
[Extent1].[AppUserId] AS [AppUserId], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName], 
[Extent1].[Email] AS [Email], 
[Extent1].[FlowStatus] AS [FlowStatus], 
[Extent1].[CompanyID] AS [CompanyID], 
**[Extent1].[Company_Id1] AS [Company_Id1]**
FROM [dbo].[AppUser] AS [Extent1]
WHERE ([Extent1].[AppUserId] > 0) AND (N'Joe' = [Extent1].[LastName])

但是如果我删除映射并以 "HasMany()" 结束配置,正确的映射就会消失,并且 Company_Id1 变为 "Company_Id":

SELECT 
[Extent1].[AppUserId] AS [AppUserId], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName], 
[Extent1].[Email] AS [Email], 
[Extent1].[FlowStatus] AS [FlowStatus], 
[Extent1].[Company_Id] AS [Company_Id]
FROM [dbo].[AppUser] AS [Extent1]
WHERE ([Extent1].[AppUserId] > 0) AND (N'Joe' = [Extent1].[LastName])

我的配置有什么问题?

使用 EntityTypeConfiguration 的主要缺陷是配置关系。问题是关系通常涉及两个实体,但只需要(正确)配置一次。配置必须准确反映当前的导航和 FK 属性。

在你的情况下,你有两个相互冲突的配置,它们是一个相同的关系:

公司:

HasMany(c => c.AppUsers);

应用用户:

HasRequired(u => u.Company).WithMany().Map(m => m.MapKey("CompanyID"));

注意第二个配置中的无参数WithMany

根据经验,始终在一个地方配置关系。由于 Has 方法需要导航 属性 而 With 不需要,因此请在具有导航 属性 的实体的配置中执行此操作。如果两个实体都具有关系的导航属性,则使用其中一个(但仍然只使用一次)。

适用于您的方案,因为您的两个实体都有导航 属性,请从 Company 配置中删除现有行并在 AppUser 配置中使用以下内容:

HasRequired(u => u.Company).WithMany(c => c.AppUsers).Map(m => m.MapKey("CompanyID"));

或从 AppUser 配置中删除现有行并在 Company 配置中使用以下内容:

HasMany(c => c.AppUsers).WithRequired(u => u.Company).Map(m => m.MapKey("CompanyID"));

看来我自己解决了。我将 CompanyConfiguration 修改为:

 HasMany(c => c.AppUsers).WithRequired(u=>u.Company).Map(u=>u.MapKey("CompanyId"));

并更改了 AppUserConfiguration

 HasRequired(u => u.Company);

显然通过将映射定义移动到主要对象(公司)我告诉 EF - 公司可以被许多用户使用,用户必须有一个公司,并将其标识为 "CompanyId"

SELECT 
[Extent1].[AppUserId] AS [AppUserId], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName], 
[Extent1].[Email] AS [Email], 
[Extent1].[FlowStatus] AS [FlowStatus], 
[Extent1].[CompanyId] AS [CompanyId]
FROM [dbo].[AppUser] AS [Extent1]
WHERE ([Extent1].[AppUserId] > 0) AND (N'Joe' = [Extent1].[LastName])