【问题标题】:Entity Framework 4.2 code first fluent api mapping intermediate tableEntity Framework 4.2 代码先fluent api 映射中间表
【发布时间】:2012-11-01 12:17:39
【问题描述】:

如何使用 code first 和 fluent api 设置这些表关系的映射?

我为创建的所有三个实体都有一个 poco,但大多数人没有一个用于“中间”表 (PlanUser) 的 poco,所以我找不到一个很好的例子.我需要告诉 EF 将 PlanUser.PlanCustomerId 列映射到 Plan.CustomerId 但它要么不返回正确的结果,要么在当前设置中映射为 计划抛出错误:

"指定的关联外键列'CustomerId'是 无效的。指定的列数必须与 主键列。”

public class PlanUserMap : EntityTypeConfiguration<PlanUser>
{
    public PlanUserMap()
    {
        this.ToTable("PlanUser");
        this.HasKey(c => new { c.CustomerId, c.PlanCustomerId });

        this.HasRequired(c => c.Plan).WithMany().HasForeignKey(x => x.CustomerId).WillCascadeOnDelete(false);
        this.HasRequired(c => c.Customer).WithMany().HasForeignKey(x => x.CustomerId).WillCascadeOnDelete(false);
    }
}

public class PlanMap : EntityTypeConfiguration<Plan>
{
    public PlanMap()
    {
        this.ToTable("Plan");
        this.HasKey(c => c.CustomerId);
        // the below line returns only 1 row for any given customer even if there are multiple PlanUser rows for that customer
        //this.HasMany(c => c.PlanUsers).WithRequired().HasForeignKey(c => c.PlanCustomerId); 
        // this throws an error
        this.HasMany(c => c.PlanUsers).WithMany().Map(m => m.MapLeftKey("PlanCustomerId").MapRightKey("CustomerId"));
    }
}

public partial class CustomerMap : EntityTypeConfiguration<Customer>
{
    public CustomerMap()
    {
        this.ToTable("Customer");
        this.HasKey(c => c.Id);
        this.HasMany(c => c.PlanUsers).WithRequired().HasForeignKey(c => c.CustomerId);
    }
}

@Slauma,sql profiler 显示正在执行的这些查询。除了客户 ID 1 之外,第二个还应包括客户 ID 43,但它没有。我不知道为什么它不检索第二行。

exec sp_executesql N'SELECT 
[Extent1].[CustomerId] AS [CustomerId], 
[Extent1].[PlanCustomerId] AS [PlanCustomerId], 
[Extent1].[CreatedOnUtc] AS [CreatedOnUtc], 
[Extent1].[IsSelected] AS [IsSelected], 
[Extent1].[IsDeleted] AS [IsDeleted], 
[Extent1].[AccessRights] AS [AccessRights]
FROM [dbo].[PlanUser] AS [Extent1]
WHERE [Extent1].[CustomerId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=43

exec sp_executesql N'SELECT 
[Extent1].[CustomerId] AS [CustomerId], 
[Extent1].[Name] AS [Name], 
[Extent1].[PlanTypeId] AS [PlanTypeId], 
[Extent1].[OrderId] AS [OrderId], 
[Extent1].[CreatedOnUtc] AS [CreatedOnUtc], 
[Extent1].[IsActive] AS [IsActive]
FROM [dbo].[Plan] AS [Extent1]
WHERE [Extent1].[CustomerId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

这是导致查询执行的 C# 代码:

    public List<Plan> GetPlans()
    {
        List<Plan> plans = new List<Plan>();

        // add each plan they have access rights to to the list
        foreach (var accessiblePlan in Customer.PlanUsers)
        {
            plans.Add(accessiblePlan.Plan);
        }

        return plans;
    }

【问题讨论】:

    标签: entity-framework


    【解决方案1】:

    我认为您需要的实际上比您尝试的映射更简单:

    public class PlanUserMap : EntityTypeConfiguration<PlanUser>
    {
        public PlanUserMap()
        {
            this.ToTable("PlanUser");
            this.HasKey(pu => new { pu.CustomerId, pu.PlanCustomerId });
    
            this.HasRequired(pu => pu.Customer)
                .WithMany(c => c.PlanUsers)
                .HasForeignKey(pu => pu.CustomerId)
                .WillCascadeOnDelete(false);
    
            this.HasRequired(pu => pu.Plan)
                .WithMany(p => p.PlanUsers)
                .HasForeignKey(pu => pu.PlanCustomerId)
                .WillCascadeOnDelete(false);
        }
    }
    
    public class PlanMap : EntityTypeConfiguration<Plan>
    {
        public PlanMap()
        {
            this.ToTable("Plan");
            this.HasKey(p => p.CustomerId);
        }
    }
    
    public partial class CustomerMap : EntityTypeConfiguration<Customer>
    {
        public CustomerMap()
        {
            this.ToTable("Customer");
            this.HasKey(c => c.Id);
        }
    }
    

    我不确定您为什么禁用级联删除。我可能不会这样做(即我会删除两个关系的 WillCascadeOnDelete(false)),因为关联实体 PlanUser 有点依赖于其他两个实体。

    这里有更多关于这种模型的详细信息(有时称为“与有效负载的多对多关系”):Create code first, many to many, with additional fields in association table

    【讨论】:

    • 尝试了这些更改,但与原始映射一样,任何给定客户仅返回 1 个 PlanUser。我当前的客户 43 数据集应该返回 2 个 PlanUser 行。我使用了 sql profiler,查询忽略了第二个 PlanUser 行。我想在其中粘贴查询,但此框中的字符数限制。
    • @StrandedPirate:您的所有导航属性是否都标记为virtual,即Customer.PlanUsersPlanUser.Plan 等?
    • 是的。他们不应该吗?
    • @StrandedPirate:他们应该这样做,因为您的示例查询依赖于延迟加载。您是否尝试过预先加载:var customer = context.Customers.Include(c =&gt; c.PlanUsers).Single(c =&gt; c.Id == 43),然后在调试器中检查customer.PlanUsers 有多少元素?
    • 这确实拉回了两条记录: var list = planUserRepository.Table.Include(a => a.Plan).Where(a => a.CustomerId == Customer.Id).ToList( );但我仍然想知道为什么我的映射设置不起作用。当我们调用 Customer.PlanUsers 时,它背后没有用户定义的 linq 查询,它只是获取所有可用的行。此时我已注释掉所有导航属性,将 Plan.CustomerId 列重命名为 Plan.PlanCustomerId,并且所有内容都正确映射,但仍然只返回 1 行用户。
    【解决方案2】:

    好的,我想通了。感谢@Slauma 的帮助!我们将存储库模式与 IOC(autofac) 一起使用。首先,我不应该在我的存储库服务中调用 Customer.PlanUsers。在某个地方,我们开始使用存储库服务中的 IOC 解析 Customer 实体,以快速获取某些客户属性并将其放入只读变量中。这不可避免地困扰着我们,因为我们没有查询实体存储库本身,而是从 Customer 实体开始,期望它填充我们需要的所有内容,而不是预先填充。

        public List<Plan> GetPlans()
        {
            List<Plan> plans = new List<Plan>();
            var list = _planUserRepository.Table.Where(a => a.CustomerId == Customer.Id).ToList();
            foreach (var planUser in list)
            {
                plans.Add(planUser.Plan);
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-19
      相关资源
      最近更新 更多