为“多对多”映射表Code First创建一个实体

时间:2011-10-10 21:39:24

标签: entity-framework-4.1

我首先要说这可能在概念上不正确,所以我也会发布我的问题,所以如果有人可以帮助解决潜在的问题,我就不需要这样做了。

以下是我的模型的简化版本。

public class MenuItem
{
    public int MenuItemId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Department> Departments { get; set; }

    private ICollection<MenuSecurityItem> _menuSecurityItems;
    public virtual ICollection<MenuSecurityItem> MenuSecurityItems
    {
        get { return _menuSecurityItems ?? (_menuSecurityItems = new HashSet<MenuSecurityItem>()); }
        set { _menuSecurityItems = value; }
    }
}

public class Department
{
    public int DepartmentId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<MenuItem> MenuItems { get; set; }
}

我的根本问题是我想要选择属于某个部门的所有MenuItem(为了参数而使用DepartmentId = 1的部门),还要包括所有MenuSecurityItems。

我无法包含()MenuSecurityItems,因为MenuItems导航集合的类型为ICollection,并且不支持Include()。这似乎也不起作用Department.MenuItems.AsQueryable().Include(m => m.MenuSecurityItems)

我“解决”这个问题的方法是为Code First创建的多对多映射表创建一个实体。

public class DepartmentMenuItems
{
    [Key, Column(Order = 0)]
    public int Department_DepartmentId { get; set; }
    [Key, Column(Order = 1)]
    public int MenuItem_MenuItemId { get; set; }
}

然后我能够像这样通过映射表加入。 (MenuDB是我的DBContext)

var query = from mItems in MenuDb.MenuItems
            join depmItems in MenuDb.DepartmentMenuItems on mItems.MenuItemId equals depmItems.MenuItem_MenuItemId
            join dep in MenuDb.Departments on depmItems.Department_DepartmentId equals dep.DepartmentId
            where dep.DepartmentId == 1
            select mItems;

这实际上适用于该特定查询...但它打破了我的导航集合。现在,EF4.1在尝试使用导航集时尝试查找名为DepartmentMenuItems1的对象时抛出异常。

如果有人可以帮我解决原始问题或我现在使用映射表实体创建的问题,我们将不胜感激。

1 个答案:

答案 0 :(得分:1)

通过在要包含的外部集合上使用Select来预先加载嵌套集合:

var department = context.Departments
    .Include(d => d.MenuItems.Select(m => m.MenuSecurityItems))
    .Single(d => d.DepartmentId == 1);

您还可以使用字符串版本为Include的虚线路径:Include("MenuItems.MenuSecurityItems")


修改:在评论中提问您如何将过滤器应用于要加载的MenuItems集合:

很遗憾,您无法使用Include内的预先加载进行过滤。在您的特定情况下(您只加载一个部门)的最佳解决方案是放弃急切加载并改为利用明确的加载:

// 1st roundtrip to DB: load department without navigation properties
var department = context.Departments
    .Single(d => d.DepartmentId == 1);

// 2nd roundtrip to DB: load filtered MenuItems including MenuSecurityItems
context.Entry(department).Collection(d => d.MenuItems).Query()
    .Include(m => m.MenuSecurityItems)
    .Where(m => m.Active)
    .Load();

这需要两次往返数据库和两个查询,但在我看来这是最干净的方法,它实际上只加载你需要的数据。

其他解决方法是:1)要么稍后在内存中应用过滤器(但是必须先从数据库加载整个集合才能过滤)或2)使用投影。这在此解释(第二点和第三点):

EF 4.1 code-first: How to order navigation properties when using Include and/or Select methods?

此答案中的示例适用于排序,但同样适用于过滤(只需在OrderBy代码段中替换Where

相关问题