【问题标题】:LINQ only returns one item in collectionLINQ 只返回集合中的一项
【发布时间】:2019-03-22 01:49:31
【问题描述】:

我有一个Project 模型,它有一个AppUsers 的集合被指定为ProjectManagers 或更一般的Users

public class Project
    {
        public int ProjectID { get; set; }

        [Required(ErrorMessage = " Please enter a project name")]
        public string Name { get; set; }

        public virtual ICollection<AppUser> ProjectManagers { get; set; }

        public virtual ICollection<AppUser> Users { get; set; }
    }

在我的控制器中,我尝试生成分配给特定AppUser 的所有项目的列表,无论它们是在ProjectManagers 还是Users 集合中

projectList = repository.Projects.Where(p => p.ProjectManagers.Contains(user) || p.Users.Contains(user));

但是,如果AppUser 涉及多个Project,则只有最后一个Project 会填充此列表。如何确保返回所有相关项目?

我的AppUser 模特:

public class AppUser : IdentityUser
    {
        //literally does nothing at this point and 
        // is merely a placeholder until I add more
    }

在我的控制器中,我使用List 操作方法来显示与AppUser 相关的所有Projects。这是无法正常工作的部分,它只返回一个Project

public async Task<IActionResult> List(int page = 1)
        {
            var user = await userManager.FindByNameAsync(User.Identity.Name);
            if(user != null)
            {
                IQueryable<Project> projectList = GetUserProjects(page, user);
                var model = GetProjectsListViewModel(projectList, page, user);
                return View(model);
            }
            TempData["message"] = "User not found";
            return RedirectToAction("Index", "Home");
        }

以下方法用于生成列表和模型进行分页

 public ProjectsListViewModel GetProjectsListViewModel (IQueryable<Project> projectList, int page, AppUser user)
        {
            ProjectsListViewModel model = new ProjectsListViewModel();
            model.Projects = projectList
                    .OrderBy(p => p.ProjectID)
                    .Skip((page - 1) * PageSize)
                    .Take(PageSize);
            model.PagingInfo = new PagingInfo
            {
                CurrentPage = page,
                ItemsPerPage = PageSize,
                TotalItems = projectList.Count()
            };
            return model;
        }

        public IQueryable<Project> GetUserProjects(int page, AppUser user)
        {
            IQueryable<Project> projectList;
            //IQueryable<Project> result;
            if (user.UserName == "Admin")
            {
                projectList = repository.Projects;
            }
            else
            {

               projectList = repository.Projects
                .Where(p => p.ProjectManagers.Any(pm => pm.Id == user.Id) || p.Users.Any(u => u.Id == user.Id))
                .Select(x => new { UserId = user.Id, Projects = x })
                .GroupBy(p => p.UserId)
                .Select(x => x.OrderByDescending(p => p.Projects.ProjectID).First().Projects);

                //None of the commented out code works either
                //projectList = repository.Projects.Where(p => p.ProjectManagers.Contains(user) || p.Users.Contains(user) || p.CommissioningAuthorities.Contains(user));

                //foreach (Project p in repository.Projects)
                //{
                //    if(p.ProjectManagers.Contains(user) || p.Users.Contains(user) || p.CommissioningAuthorities.Contains(user))
                //    {
                //        projectList.ToList().Add(p);
                //    }

                //}
            }
            return projectList;
        }

编辑 我注意到当我运行proj.ProjectManagers.Add(appUser); 时,它会将该用户添加为ProjectManager,但看起来它也会从之前分配的任何其他项目的ProjectManager 集合中删除它们

【问题讨论】:

  • AppUser 是否覆盖 GetHashCodeEquals?如果不是,您在两个项目中是否有完全相同的参考?因为如果您不覆盖EqualsGetHashCode,那么它将执行引用相等,并且仅匹配完全相同的对象,而不匹配具有等效值的对象。
  • @juharr 他们需要被覆盖做什么?这是我还没有实现的东西,也不太清楚如何开始。现在 AppUser 扩展 IdentityUser 但我没有添加任何其他属性
  • 这取决于您在AppUser 中的内容。假设您想在 Name 属性上进行匹配,那么您可以让 Equals 比较名称,GetHashCode 只会返回 Name.GetHashCode
  • 实际上,如果这是 EF,那么您只需要更改代码以匹配 Ashkan 在他的答案中的任何 PK。

标签: c# linq asp.net-core


【解决方案1】:

在这里,由于您的项目类中没有日期时间,我假设 Id 较大的项目是较新的项目:

var result = Projects
    .Where(p => p.ProjectManagers.Any(pm => pm.Id == user.Id)
           || p.Users.Any(u => u.Id == user.Id))
    .Select(x => new { UserId = user.Id, Projects = x })
    .GroupBy(p => p.UserId)
    .Select(x => x.OrderByDescending(p => p.Projects.ProjectID).First().Project);

获取用户的所有项目:

var result = Projects
    .Where(p => p.ProjectManagers.Any(pm => pm.Id == user.Id)
           || p.Users.Any(u => u.Id == user.Id));

【讨论】:

  • 我认为这很可能是答案,但我在x.OrderByDescending(p =&gt; p.ProjectID).First())ProjectID 上遇到错误,指出“ 不包含'的定义项目 ID'"
  • @coolhand 是的,我的代码中有错误。请查看我编辑的答案。
  • 我想我是误会了。我正在尝试返回 IQueryable&lt;Project&gt; projectList;,但您的 result{string UserId, Project Projects}
  • 哦,是的,只需在第一个之后添加 .Project 即可。我只是将它添加到答案中。 ;) 这发生在你早上 7 点回答,而你还没有睡觉时
  • 我正在跟踪你现在在做什么(除了答案需要respository.ProjectsFirst().Projects 才能通过),但我仍然得到与返回单个项目相同的结果
【解决方案2】:

问题是我没有使用多对多联合表。因此,当我将AppUser 添加到Project 时,它删除了任何先前对象之间的关系。因此,只有一个Project 会与用户相关联。为了解决这个问题,我创建了这个类:

public class ProjectManager
{
    public int ProjectID { get; set; }
    public Project Project { get; set; }

    public string AppUserID { get; set; }
    public AppUser AppUser { get; set; }
}

并将这个集合添加到我的AppUser 类中:

    public class AppUser : IdentityUser
    {
        public ICollection<ProjectManager> ProjectsAsManager { get; set; }
    }

并将以下内容添加到我的 dbContext:

            modelBuilder.Entity<ProjectManager>()
                .HasKey(ppm => new { ppm.ProjectID, ppm.AppUserID });

            modelBuilder.Entity<ProjectManager>()
                .HasOne(pm => pm.AppUser)
                .WithMany(p => p.ProjectsAsManager)
                .HasForeignKey(pm => pm.AppUserID);

            modelBuilder.Entity<ProjectManager>()
                .HasOne(pm => pm.Project)
                .WithMany(p => p.ProjectManagers)
                .HasForeignKey(pm => pm.ProjectID);

            base.OnModelCreating(modelBuilder);

运行迁移和更新后,单个用户现在可以与多个Project 关联为ProjectManager

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-08
    • 1970-01-01
    • 2013-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    相关资源
    最近更新 更多