2

我有一对简单的类在 EF 4.1 的 Code First 中生成数据库:

public class User
{
    public int UserId { get; set; }
    public string UserName { get; set; }
}

public class Purchase
{
    public int PurchaseId { get; set; }
    public int UserId { get; set; }
    public int? SalespersonUserId { get; set; }

    public User User { get; set; }
    public User SalespersonUser { get; set; }
}

public class NewItemsDataContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Purchase> Purchases { get; set; }
}

在我的程序中,我创建并向其写入一些数据。

class Program
{
    static void Main(string[] args)
    {
        Database.SetInitializer<NewItemsDataContext>(new DropCreateDatabaseIfModelChanges<NewItemsDataContext>());

        using (NewItemsDataContext sadc = new NewItemsDataContext())
        {
            sadc.Users.Add(new User());

            sadc.SaveChanges();
        }
        using (NewItemsDataContext sadc = new NewItemsDataContext())
        {
            sadc.Purchases.Add(new Purchase() { UserId = 1 });
            sadc.SaveChanges();
        }
        using (NewItemsDataContext sadc = new NewItemsDataContext())
        {
            var sql = sadc.Purchases.Include(p => p.User);
            foreach (Purchase purchase in sql)
                Console.WriteLine(purchase.User.UserId.ToString());
        }
    }
}

请注意,当我读回购买记录时,我得到了 purchase.User 为空的异常——也就是说,.Include 没有为用户提取任何内容。现在,如果我忽略我的 OnModelCreating 中的 salespersonUser 导航属性(或者只是将其注释掉):

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Purchase>().Ignore(p => p.SalespersonUser);

    base.OnModelCreating(modelBuilder);
}

该代码有效,并且用户加载到 .Include 中。

当 SalespersonUser nav 属性被忽略时,创建的数据库看起来与您期望的一样。Purchase 表有PurchaseId、UserId 和SalespersonId。但是,一旦您重新添加 SalespersonUser nav 属性(停止忽略它),您最终会在表中得到另外两个键:User_UserId 和 SalespersonUser_UserId(以及原始的 UserId 和 SalespersonUserId)。

此外,生成的 SQL 明确显示了问题所在。

没有导航属性:

{SELECT 
[Extent1].[PurchaseId] AS [PurchaseId], 
[Extent1].[UserId] AS [UserId], 
[Extent1].[SalespersonUserId] AS [SalespersonUserId], 
[Extent2].[UserId] AS [UserId1], 
[Extent2].[UserName] AS [UserName]
FROM  [Purchases] AS [Extent1]
INNER JOIN [Users] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[UserId]}

并使用 nav 属性:

{SELECT 
[Extent1].[PurchaseId] AS [PurchaseId], 
[Extent1].[UserId] AS [UserId], 
[Extent1].[SalespersonUserId] AS [SalespersonUserId], 
[Extent2].[UserId] AS [UserId1], 
[Extent2].[UserName] AS [UserName], 
[Extent1].[SalespersonUser_UserId] AS [SalespersonUser_UserId]
FROM  [Purchases] AS [Extent1]
LEFT OUTER JOIN [Users] AS [Extent2] ON [Extent1].[User_UserId] = [Extent2].[UserId]}

请注意它是如何提取 UserId,但与 User_UserId 连接的,以及左连接。SalespersonUserId 甚至没有在联接中引用。写入记录后在数据库内部设置了UserId,但User_UserID为空。因此,没有什么可加入的,我们为包含的用户获得 null。

在我看来,这是 EF 中的一个错误,但它可能有设计原因。如果是这样,有人可以为我清理一下,也许可以描述一些可以解决它的流畅的 API 吗?我有点偏爱我的导航属性。

4

1 回答 1

3

EF 在您的导航属性方面遇到问题 - 这就是它生成额外 FK 的原因。

尝试添加这个流畅的映射,这样你就可以明确地将 FK 映射到 nav 属性关系:

        modelBuilder.Entity<Purchase>()
            .HasOptional(p => p.SalespersonUser)
            .WithMany()
            .HasForeignKey(p => p.SalespersonUserId);

        modelBuilder.Entity<Purchase>()
            .HasRequired(p => p.User)
            .WithMany()
            .HasForeignKey(p => p.UserId);
于 2012-09-21T19:20:21.040 回答