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])
我有一个预先存在的数据模型,最初使用 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])