从Entity Framework中删除单个记录?

237
我如何使用Entity Framework从表中删除单条记录?我在Entity Framework中有一个名为employ的SQL Server表, 其中只有一个名为ID的关键列。

3
db.employ.Remove(db.employ.Find(ID1)) - Carter Medlin
2
@CarterMedlin - 虽然这样可以工作,但这是两个数据库查询:一个 SELECT 和一个 DELETE。大多数人发现这非常浪费,特别是因为 select 可能需要比删除更长的时间。 - Davor
我不建议使用实体框架的Remove或RemoveRange方法,因为它们会导致性能问题。相反,我更倾向于使用以下超级简单的方法: var sql = "DELETE FROM YOUR_TABLE WHERE YOUR_FIELD= @your_parameter";this.your_context.Database.ExecuteSqlCommand(sql, new SqlParameter("@your_parameter", yourParameter)); - curiousBoy
2
@curiousBoy 我认为当你执行像你建议的那样的语句时,EF6缓存不会反映出更改。 - Yitzchak
15个回答

418

不必先查询对象,您可以通过其id将其附加到上下文中。就像这样:

var employer = new Employ { Id = 1 };
ctx.Employ.Attach(employer);
ctx.Employ.Remove(employer);
ctx.SaveChanges();

或者,您可以将附加条目的状态设置为已删除:

var employer = new Employ { Id = 1 };
ctx.Entry(employer).State = EntityState.Deleted;
ctx.SaveChanges();

97
或者,ctx.Entry(employer).State = EntityState.Deleted - Simon Belanger
18
只有在关系定义为级联删除时,此方法才有效。否则,上述代码会因外键异常而失败。 - baruchl
6
@mt_serg,我在考虑未来三步。您最后一次需要从数据库中删除这样一个简单记录是什么时候?通常您处理的是包含外键关系的更复杂记录。这就是我发表评论的原因。 - baruchl
2
@IanWarburton 第二行和第三行(附加和删除) - Simon Belanger
4
@PaulZahra说:有时你会有一个来自其他查询或来源的ID列表,而你需要删除其中的一个。与其加载这些对象再进行删除,你可以通过ID直接删除。你知道,在SQL中普遍使用的就是DELETE语句的这种方式。 - siride
显示剩余13条评论

105
您可以使用SingleOrDefault获取与您的条件匹配的单个对象,然后将其传递给EF表的Remove方法。
var itemToRemove = Context.Employ.SingleOrDefault(x => x.id == 1); //returns a single item.

if (itemToRemove != null) {
    Context.Employ.Remove(itemToRemove);
    Context.SaveChanges();
}

6
这不是一个好方法,因为你正在从数据库中选择所有字段! - Ali Yousefi
2
这是我做事的方式。 - Jack Fairfield
6
@Ali,Jack - 但我认为这种方式更可取,因为它首先检查您要删除的数据是否存在,这可以避免任何麻烦。 接受的答案没有这样的检查。 - Uzair Khan
5
这是更好的方式。想想看,如果John Smith试图删除一个id = 1的项目,而Susie Smith在30秒前已经删除了它,但John并不知道呢?在这种情况下,您需要访问数据库。 - Yusha
8
@Yusha 为什么?两种情况下结果都是记录被删除了。我们真的关心这是现在发生还是30秒前发生的吗?有些竞争条件并不值得跟踪。 - 9Rune5
显示剩余3条评论

14
  var stud = (from s1 in entities.Students
            where s1.ID== student.ID
            select s1).SingleOrDefault();

  //Delete it from memory
  entities.DeleteObject(stud);
  //Save to database
  entities.SaveChanges();

3
FirstOrDefault 很危险。如果你确定只有一个值,应该使用 SingleOrDefault;如果有多个值,则应该使用循环来处理。 - Mark Sowul

9

我正在使用Entity Framework和LINQ。以下代码对我很有帮助;

1- 对于多条记录

 using (var dbContext = new Chat_ServerEntities())
 {
     var allRec= dbContext.myEntities;
     dbContext.myEntities.RemoveRange(allRec);
     dbContext.SaveChanges();
 }

2- 单条记录

 using (var dbContext = new Chat_ServerEntities())
 {
     var singleRec = dbContext.ChatUserConnections.FirstOrDefault( x => x.ID ==1);// object your want to delete
     dbContext.ChatUserConnections.Remove(singleRec);
     dbContext.SaveChanges();
 }

针对单个记录,为什么不使用 SingleOrDefault 而使用 FirstOrDefault - Mark Sowul
每当您使用SingleOrDefault时,您明确声明查询应该最多只有一个结果。另一方面,当使用FirstOrDefault时,查询可以返回任意数量的结果,但您声明只想要第一个结果。 - Baqer Naqvi
1
是的,如果存在多个记录,为什么删除任意记录会是正确的呢?特别是在这种情况下,id 是关键字,所以应该只有一个:如果存在多个,则是一个错误(Single 会检测到)。 - Mark Sowul
@MarkSowul 没错。我已经编辑了答案,使用了FirstOrDefault。 - Baqer Naqvi
@BaqerNaqvi 从性能角度来看,RemoveRange是一种可怕的删除实体的方式。特别是当您的实体具有所有外键导航属性时。我宁愿使用var sql =“DELETE FROM YOUR_TABLE WHERE YOUR_FIELD = @your_parameter”; this.your_context.Database.ExecuteSqlCommand(sql,new SqlParameter("@your_parameter", yourParameter)); - curiousBoy

8
Employer employer = context.Employers.First(x => x.EmployerId == 1);

context.Customers.DeleteObject(employer);
context.SaveChanges();

如果没有ID为1的对象,这是否会提供保护?它不会抛出异常吗? - Jack Fairfield
@JackFairfield 我认为你应该检查空对象,然后根据情况执行删除操作。 - Jawand Singh
“First” 是危险的。你要么知道只有一个(所以使用 Single),要么有多个,应该在循环中完成。 - Mark Sowul

4
更通用的方法
public virtual void Delete<T>(int id) where T : BaseEntity, new()
{
    T instance = Activator.CreateInstance<T>();
    instance.Id = id;
    if (dbContext.Entry<T>(entity).State == EntityState.Detached)
    {
        dbContext.Set<T>().Attach(entity);
    }

    dbContext.Set<T>().Remove(entity);
}

4

我想贡献一下我反复斟酌过的三种方法。

方法1:

var record = ctx.Records.FirstOrDefault();
ctx.Records.Remove(record);
ctx.SaveChanges();

方法二:

var record = ctx.Records.FirstOfDefault();
ctx.Entry(record).State = EntityState.Deleted;
ctx.SaveChanges();
ctx.Entry(record).State = EntityState.Detached;

我更喜欢使用方法2的原因之一是,如果将EF或EFCore设置为QueryTrackingBehavior.NoTracking,这样做更加安全。

然后还有方法3:

var record = ctx.Records.FirstOrDefault();
var entry = ctx.Entry(record);
record.DeletedOn = DateTimeOffset.Now;
entry.State = EntityState.Modified;
ctx.SaveChanges();
entry.State = EntityState.Detached;

这里采用软删除的方式,通过设置记录的DeletedOn属性来实现,并且仍然能够保留该记录以备将来使用,不管将来可能是什么情况。基本上就像是把它放进了回收站。
此外,在涉及到方法3时,我们不必将整条记录都设为修改状态:
entry.State = EntityState.Modified;

您也可以只将列DeletedOn设置为已修改:
entry.Property(x => x.DeletedOn).IsModified = true;

2

使用Entity Framework 6,您可以使用Remove方法。此外,使用using语句是一种好的策略,以确保您的连接被关闭。

using (var context = new EmployDbContext())
{
    Employ emp = context.Employ.Where(x => x.Id == id).Single<Employ>();
    context.Employ.Remove(emp);
    context.SaveChanges();
}

1

0
    [HttpPost]
    public JsonResult DeleteCotnact(int id)
    {
        using (MycasedbEntities dbde = new MycasedbEntities())
        {
            Contact rowcontact = (from c in dbde.Contact
                                     where c.Id == id
                                     select c).FirstOrDefault();

            dbde.Contact.Remove(rowcontact);
            dbde.SaveChanges();

            return Json(id);
        }
    }

你觉得这个简单吗?你也可以试试这个:

        var productrow = cnn.Product.Find(id);
        cnn.Product.Remove(productrow);
        cnn.SaveChanges();

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