【问题标题】:Delete records with multiple ids based on condition根据条件删除多个id的记录
【发布时间】:2017-10-14 02:47:26
【问题描述】:

下面是我的课:

 public partial class Ads
    {
       public int Id { get; set; }
       public int RegionId { get; set; }
       public string Name { get; set; }
       public int Group { get; set; }
    }

记录:

Id      Name   Group
1       abc     1
2       xyz     1
3       lmn     1
4       xxx     2
5       ppp     2
6       ttt     3
7       ggg     3

现在我想删除所有记录/仅删除具有同一组特定 id 的某些 id 的记录。

代码:

public void Delete(int[] ids,bool flag = false)
        {
            using (var context = new MyEntities())
            {
                context.Ads.RemoveRange(
                    context.Ads.Where(t => (flag ?
                   (context.Ads.Any(x => ids.Contains(x.Id) && x.Group == t.Group)) : false)));
                context.SaveChanges();
            }
        }

我想要做的是如下所示:

If flag is false with ids=3,5 then
    I want to delete only records with Id=3,5
Else if flag is true with ids=3,5 then
    I want to delete records with Id=3,5 but all other records too of the group to which ids=3,5 belong to.
    Here id=3 belongs to group 1 so I want to delete all records of group1 i.e id=1,2 like wise ids=5 belongs to
    group 2 so I want to delete all records of group 2 i.e id=4.

最后一种情况的预期输出(flag=true):

Id      Name   Group
6       ttt     3
7       ggg     3

但我认为我没有这样做是正确的方法,并且查询中有一些改进的来源。

注意: ids[] 将始终包含来自不同组的 id,并且来自不同组的最高 id。

如何改进我对这两种情况(flag=true 和 false)的查询?

【问题讨论】:

  • 嗨学习。请回想I provided before 的建议,它是:在提及您自己时,请始终使用大写的“I”。以英语为母语的读者通常会觉得读“i”很烦人,这种轻微的挫败感可能会妨碍您提供帮助。请您记得在下一篇文章中解决这个问题吗?

标签: c# entity-framework linq


【解决方案1】:

怎么样

var removeRecs=context.Ads.where(t => ids.contains(t.id))
if(flag)
removeRecs.AddRange(context.Ads.where(t=> removeRecs.Any(r =>t.groupId==r.Id)))
Ads.RemoveRange(removeRecs);

【讨论】:

  • 我没有得到这个 addRange 方法
  • 应该是AddRange
  • 这不是语法问题,但我没有得到那个 AddRange 方法。
  • 尝试 removeRecs.ToList().AddRange bcs AddRange 将仅在 IEnumerable 而不是 Iquariable 上可用
【解决方案2】:

不要让自己太难,不是所有事情都必须/可以在查询的 where 语句中完成。此外,循环中的一般经验法则尝试排除所有常量值并进行检查。所以试试这个:

    public static void Delete(int[] ids, bool flag = false)
    {
        using (var context = new MyEntities())
        {
            var query = context.Ads.AsQueryable();
            query = flag
              ? query.Where(x => context.Ads
                                   .Where(i => ids.Contains(i.Id))
                                   .Select(i => i.Group)
                                   .Contains(x.Group))
              : query.Where(x => ids.Contains(x.Id));

            context.Ads.RemoveRange(query);
            context.SaveChanges();
        }
    }

【讨论】:

  • 你能告诉我一些关于这一行的事情吗:var query = context.Ads.AsQueryable();
  • 也许 AsQueryable 可以替换为像“as IQueryable”这样的转换。或者,您可以使用 'IQueryable query = ...',而不是使用 'var query = ...'。目标是确保下一条语句可以重用相同的变量。由于此语句返回 IQueryable,我们不能以 DbSet 作为类型开始。您如何编写它取决于您,并且取决于您希望代码的可读性。最后编译器会优化这条语句。
【解决方案3】:
 public void Delete(int[] ids, bool flag = false)
        {
            using (var context = new MyEntities())
            {
                var items = context.Ads.Where(x => ids.Any(a => x.Id == a));


                if (!flag)
                {
                    //flag=false --> delete items with Id in ids[]
                    context.Ads.RemoveRange(items);
                }
                else
                {
                    var groups = items.GroupBy(a => a.Group).Select(a => a.Key);

                    //flag=true --> delete all items in selected groups
                    context.Ads.RemoveRange(context.Ads.Where(x => groups.Any(a => x.Group == a)));
                }
                context.SaveChanges();
            }

【讨论】:

  • 您应该在 else 部分包含该组行,因为当标志为 false 时,该行将不必要地执行。
【解决方案4】:

你应该分开你的任务...

if (flag)
{
    groupIds = db.Ads.Where(x => ids.Contains(x.Id)).Select(x => x.Group).ToList();
    db.Ads.RemoveRange(db.Ads.Where(x => groupIds.Contains(x.Group)).ToList());
}
else
{
    db.Ads.RemoveRange(db.Ads.Where(x => ids.Contains(x.Id)).ToList());
}

【讨论】:

  • 为什么在查询中使用 distinct?
  • @Learning 假设 ids 包含 1000 个属于同一 groupId 的 id。为什么我要在groupIds 中保留重复项?
  • 这种情况不会像 ids 总是包含来自不同组的 ids 并且来自每个组的过高 ids
  • @Learning 如果你没有在你的问题中写下它,我们试图回答的信息不存在。你的问题没有说明这个限制,所以我没有假设它
  • 对此我很抱歉。让我在我的问题中添加此信息。谢谢 :)
【解决方案5】:

在我看来,您在这里有两个不同的删除。

在第一种情况下,您只删除具有给定 ID 的广告,这非常简单。

在第二种情况下,您将删除具有给定 ID 的广告以及包含最近删除的广告组的所有其他广告。因此,在这种情况下,与其先删除具有给定 ID 的广告,为什么不实际为这些 ID 获取不同的组,而不仅仅是删除这些组。

编辑

你可以这样做。

using (var context = new TestEntities())
{
    if (!flag)
        context.Ads.RemoveRange(context.Ads.Where(a => ids.Contains(a.Id)));
    else
        context.Ads.RemoveRange(context.Ads.Where(a => context.Ads.Where(g => ids.Contains(g.Id)).Select(x => x.Group).Distinct().Contains(a.Group)));
    context.SaveChanges();
}

对于更复杂的情况,我试图为给定的 id-s 获取不同的组。因此,对于 ID-s 3 和 5,我正在选择组,而不是我在组上做的不同,因为可能会发生 id-s 具有相同的组。比我获取所有具有这些组的广告。因此,对于传递的 3 和 5 值,我将获得第 1 组和第 2 组,而不是使用它们来获取具有该组的所有广告。这反过来会产生 id-s 1、2、3、4 和 5,我会删除它们。

编辑 2

如果第二个 Linq 查询的复杂性让您感到困扰,不如编写 SQL 查询。

context.Database.ExecuteSqlCommand( 
        "DELETE Ads WHERE Group IN (SELECT Group FROM Ads WHERE Id IN(@p1, @p2))", new SqlParameter("@p1", ids[0]), new SqlParameter("@p2", ids[1])); 

这应该是额外的性能,而不是依赖 EF 将其一一删除。

【讨论】:

  • 赞成你为帮助我所做的努力,但你不认为更少的 linq 函数等于更高的效率
  • 该规则适用于任何地方 - 更少的功能 = 更高的效率。我描述的伪代码非常简单,缺乏简单性。
猜你喜欢
  • 2019-04-04
  • 1970-01-01
  • 1970-01-01
  • 2021-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多