如何使用Entity Framework按ID删除对象

149

在我的看法中,使用entity framework删除一个对象之前需要先获取它,代码如下:

var customer = context.Customers.First(c => c.Id == 1);

context.DeleteObject(customer);

context.Savechanges();

所以我需要两次访问数据库。有更简单的方法吗?

13个回答

151
在 Entity Framework 6 中,删除操作是使用 Remove。以下是一个示例。
Customer customer = new Customer () { Id = id };
context.Customers.Attach(customer);
context.Customers.Remove(customer);
context.SaveChanges();

29
为什么要“Attach”?为什么不直接“删除”并“保存更改”? - runeks
4
如果您不将实体附加到上下文中,那么在移除时会出现错误,因此必须这样做。EF 只能从此上下文中删除实体。 - Pierre-Luc
6
根据手册所述,在执行Remove操作之前,实体必须存在于上下文中。请参见此处链接的文档:https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.dbset.remove?view=entity-framework-6.2.0 - dwkd
9
我没有使用附件,它正常运作。 - ILIAS M. DOLAPO
20
有些人可能会混淆Entity Framework Core和Entity Framework之间Remove方法的使用。对于Entity Framework,需要在调用Attach方法后调用Remove方法,正如@Pierre-Luc所提到的那样。然而,在EFCore中,Remove方法将开始跟踪实体并将其标记为已删除。 - Yannick R.
我认为除非你要对对象进行更多操作,否则不需要使用Attach()。据我所见,该方法用于在执行期间将其跟踪到数据库中。 - drethedevjs

63
与 @Nix 相同,只不过有一个小变化,即强类型:

如果您不想查询它,只需创建一个实体,然后删除它。

Customer customer = new Customer () { Id = id };
context.Customers.Attach(customer);
context.Customers.DeleteObject(customer);
context.SaveChanges();

7
如果对象不存在,它并不完美,因为它会抛出一个异常:"DbUpdateConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0)". 我希望它像DELETE语句一样忽略这个异常。 - Dunc
抱歉,这会导致不必要且始终不被期望的验证! - Hamed Zakery Miab

39

类似问题请参考此处

使用Entity Framework时,可以使用扩展库EntityFramework-Plus。该库可在NuGet上获取。然后您就可以编写以下内容:

// DELETE all users which has been inactive for 2 years
ctx.Users.Where(x => x.LastLoginDate < DateTime.Now.AddYears(-2))
     .Delete();

它还可用于批量删除。


59
迄今为止,这不是核心 EF 库的一部分,这是违反常理的。 - nathanchere
2
@FerretallicA - 同意。 - acarlon
3
这个方法已经过时,请使用以下代码: context.Users.Where(user=> user.Id == id).Delete(); 该代码的作用是删除指定id的用户。 - Manuel
由于错误“DELETE语句中当前不支持FROM子句”,它无法与Azure SQL DataWarehouse一起使用。但是,像Jonik的答案中的原始SQL可以正常工作。 - Michael Freidgeim
3
需要使用context.SaveChanges()吗? - Tomas Kubes
@TomasKubes 不需要SaveChanges(), 因为EF Plus的.Delete()调用会自动执行它。 - Tihomir Todorov

23
如果您不想进行查询,只需创建一个实体,然后将其删除。
Customer customer  = new Customer() {  Id = 1   } ; 
context.AttachTo("Customers", customer);
context.DeleteObject(customer);
context.Savechanges();

15

自从Entity Framework Core 7以后,你可以使用以下代码:

await context.Customers.Where(c => c.Id == 1).ExecuteDeleteAsync();

然而,请注意ExecuteDeleteAsync()不会尊重变更跟踪器和事务性的SaveChanges()。因此,您必须手动创建一个外观事务并处理提交/回滚。 - undefined

11

我在我的一个项目中使用以下的代码:

    using (var _context = new DBContext(new DbContextOptions<DBContext>()))
    {
        try
        {
            _context.MyItems.Remove(new MyItem() { MyItemId = id });
            await _context.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            if (!_context.MyItems.Any(i => i.MyItemId == id))
            {
                return NotFound();
            }
            else
            {
                throw ex;
            }
        }
    }
这样一来,只有在尝试删除具有指定ID的项目时发生异常时,它才会查询数据库两次。然后,如果未找到该项目,则返回有意义的消息;否则,它会将异常抛回(您可以使用不同的catch块处理不同的异常类型,使用if块添加更多自定义检查等等)。
[我正在一个MVC .Net Core/.Net Core项目中使用这段代码并结合Entity Framework Core使用。]

这是理想的解决方案。 - MacK

7
在EF Core中,如果您不关心对象是否存在,只关心它不会在数据库中,最简单的方法是:
context.Remove(new Customer(Id: id));  // adds the object in "Deleted" state
context.SaveChanges();                 // commits the removal

你实际上不需要使用Attach()方法-它会将对象添加到Unchanged状态的更改跟踪器中,而Remove()方法会将对象添加到Deleted状态的跟踪器中。然而,最重要的是你只需要进行一次与后端的往返通信。

5

这个答案实际上来自于Scott Allen的ASP.NET MVC 5基础课程。我想分享它,因为我认为它比已有的答案更简单直观。另外请注意,根据Scott Allen和我参加的其他培训,find方法是从数据库检索资源的优化方式,如果已经被检索过,可以使用缓存。在这段代码中,集合指的是一个对象的DBSet,对象可以是任何通用对象类型。

        var object = context.collection.Find(id);  
        context.collection.Remove(object);
        context.SaveChanges();

3
对象可能为空。如果为空,则 .Remove(object); 将抛出异常。 - Kappacake
3
尽管这是最简单的语法,但 OP 特别要求一种在不重复查询数据库的情况下删除条目的方式。此方法可行,但它首先会在数据库中执行 SELECT(查找),然后执行 DELETE(移除/保存更改)操作……正如 @demonicdaron 已经提到的,如果查找返回 null,则操作会失败。 - Doug Clutter
是的,这个答案完全没有抓住重点。 - Gert Arnold

2

来自官方文档(也是我迄今为止找到的最有效的方法):

Student studentToDelete = new Student() { ID = id };
_context.Entry(studentToDelete).State = EntityState.Deleted;
await _context.SaveChangesAsync();

1
你需要链接提到它的“官方文件”。 - Damn Vegetables

2

dwkd的答案对我在Entity Framework Core中大部分有效,除了当我看到这个异常时:

InvalidOperationException:实体类型“Customer”的实例无法被跟踪,因为已经有另一个具有{'Id'}相同键值的实例正在被跟踪。在附加现有实体时,请确保只有一个具有给定键值的实体实例被附加。考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'查看冲突的关键值。

为避免此异常,我更新了代码:

Customer customer = context.Customers.Local.First(c => c.Id == id);
if (customer == null) {
    customer = new Customer () { Id = id };
    context.Customers.Attach(customer);
}
context.Customers.Remove(customer);
context.SaveChanges();

6
我相信您想要使用 FirstOrDefault 吗? - El Mac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接