【问题标题】:Entity Framework Fluent API generating extra columns in sql on related objectEntity Framework Fluent API 在相关对象的 sql 中生成额外的列
【发布时间】:2017-02-16 14:42:55
【问题描述】:

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

在这种特定情况下,我的外键关系在 SQL 中生成两列,一列是我定义的,一列是从无到有的......

我有以下两个对象 - Company 和 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])

我的配置有什么问题?

【问题讨论】:

    标签: configuration entity-framework-6 ef-fluent-api


    【解决方案1】:

    使用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"));
    

    【讨论】:

    • 感谢您的详细解释 - 如您所见,我解决了问题,但不清楚原因。您的解释澄清了 - 我只需要在一个地方定义关系。按照这种想法,我从 AppUserConfiguration 中完全删除了对 Company 的引用,并将定义保留在 Company 中,并获得了相同的 SQL。也就是说 - 关于定义关系的正确位置在哪里的任何想法?我认为在“孩子”(用户)依赖于“父母”(公司)的情况下,这种关系会在“孩子”中更好地定义。
    • WRT 导航 - 在这种情况下,如果我们在实体之间导航,它将从用户到公司,因此进一步强调至少在这种情况下,关系定义属于用户 (最后是 AppSystems - 一旦我在迁移对象时将它重新合并到我的项目中......)
    • 正如我所提到的,当您有双向导航时,这只是一个品味问题。但是当你有单向导航时(有些人只放集合,有些人只放参考),那么你必须从具有导航属性的实体开始 - Has 方法需要属性访问器,With 有无参数重载。
    【解决方案2】:

    看来我自己解决了。我将 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])   
    

    【讨论】:

      猜你喜欢
      • 2013-01-26
      • 2023-01-25
      • 1970-01-01
      • 2018-02-21
      • 2018-07-04
      • 2017-04-04
      • 2013-08-16
      • 1970-01-01
      相关资源
      最近更新 更多