无法查询集合的集合

时间:2017-06-18 18:43:28

标签: c# entity-framework linq

我在EF中使用linq查询特定属性时遇到了问题。

要概述,用户具有关联角色。每个角色都有关联组。我只是试图获取与用户关联的所有组的MAMUserGroup属性。我可以轻松地将关联角色与.Include()联系起来,但是无法将一个额外级别转到关联的MAMUserGroups。

用户模型:

<?php
    if (!empty($_REQUEST["action"]) && $_REQUEST["action"] == "save" && !empty($_REQUEST["elem"])){
        $data = json_decode(file_get_contents("data.json"), true);
        $data[0]["items"][] = $_REQUEST["elem"];
        file_put_contents("data.json", json_encode($data));
    }
?>

MAMRole模型:

public class User
{
    [Display(Name = "SSO")]
    [Required]
    [StringLength(9, ErrorMessage = "SSO must be 9 numbers", MinimumLength = 9)]
    public virtual string ID { get; set; }

    [Display(Name = "First Name")]
    [Required]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    [Required]
    public string LastName { get; set; }

    [Display(Name = "MAM Roles")]
    public ICollection<MAMRoleModel> MAMRoles { get; set; }



}

MAM Group模型:

public class MAMRoleModel
{
    public int ID { get; set; }

    public string Name { get; set; }

    public ICollection<MAMUserGroupModels> MAMUserGroups { get; set; }
}

我已经尝试了

public class MAMUserGroupModels
{
    public int ID { get; set; }
    public string MAMUserGroup { get; set; }
}

但得到了空引用错误。我也刚从haim770尝试

foreach(var bar in user.MAMRoles)
                {
                    foreach(var foo in bar.MAMUserGroups)
                    {
                        //do something
                    }
                }

但是MAMUserGroup的计数为0,所以它没有看到引用。

1 个答案:

答案 0 :(得分:0)

我认为问题出现是因为你没有在实体框架中将一对多关系中的ICollection声明为虚拟。声明ICollection虚拟将解决它

为了防止将来出现问题,请考虑使您的类更符合实体框架。这减少了各种属性的使用。正确使用复数和单数可以提高查询的可读性,从而帮助那些必须在将来更改代码的人。

  • 主键'ID'不应为虚拟
  • 在你的一对多中使用单侧的虚拟ICollection,并在你的多方面添加对一个和外键的引用
  • 考虑使用标准命名约定。这有助于实体框架定义模型,而无需使用各种属性来帮助它。
  • 如果您真的需要,只会偏离标准命名约定。在这种情况下,为主键,外键,一对多关系等添加属性,或考虑使用流畅的API。

public class User
{
    // Standard naming convention: automatic primary key
    public string ID { get; set; }

    // a user has many MAMRoles.
    // standard naming convention: automatic one-to-many with proper foreign key
    // declare the collection virtual!
    public virtual ICollection<MAMRole> MAMRoles { get; set; }
}

public class MAMRole
{
    // standard naming cause automatic primary key
    public int ID { get; set; }

    // a MAMRole belongs to one user (will automatically create foreign key)
    // standard naming cause proper one-to-many with correct foreign key
    public string UserId {get; set;}
    public virtual User User {get; set;}

    // A MamRole has many MAMUserGroupModels
    // same one-to-many:
    // again: don't forget to declare the collection virtual
    public virtual ICollection<MAMUserGroupModel> MamUserGroupModels{ get; set; }
}

public class MAMUserGroupModel
{
    // automatic primary  key
    public int ID {get; set;}

    // a MAMUserGroupModel belongs to one MAMUser
    // automatic foreign key
    public int MAMUserId {get; set;}
    public virtual MAMUser MAMUser {get; set;}
}
顺便说一下。实体框架知道,只要在IQueryable中使用它,就需要获取属性的值。只有当您想要选择属性值并将它们转换为本地内存时,您才需要使用Include。这使得查询更有效,因为只选择了使用的值。

因此,如果您只想对MamUserGroup内的MamUserGroupModel执行某些操作,请不要包含任何内容:

在婴儿步骤中:

IQueryable<NamRole> mamRolesOfAllUsers = myDbContext.Users
    .SelectMany(user => user.MamRoles);
IQueryable<MamRoleModel> mamUserGroupModelsOfAllUsers = mamRolesOfAllUsers
    .SelectMany(mamRole => mamRole.MamUserGroupModels);
IQueryable<string> mamUserGroups = mamUserGroupModelsOfAllUsers
    .Select(mamUserGroupModel => mamUserGroupModeModel.MamUserGroup;

或在一个声明中

IQueryable<string> mamUserGroups = myDbContext.Users
    .SelectMany(user => user.MamRoles)
    .SelectMany(mamRole => mamRole.MamUserGroupModels)
    .Select(mamUserGroupModel => mamUserGroupModel.MamUserGroup);

请注意,直到知道我还没有与数据库通信。我只创建了查询的表达式。查询将在枚举开始后完成:

foreach(var userGroup in mamUserGroups)
{
    ...
}

请注意使用SelectMany代替Select。每当有集合集合时,使用SelectMany将其设为一个集合。如果你发现自己在foreach中做了一个foreach,这很好地表明SelectMany可能是更好的选择。

最后:你是否看到,由于正确使用复数和单数,查询更具可读性。如果将来有人在实施变更时犯错误,可以减少更改。