118

It seems to me that I have to retrieve an object before I delete it with entity framework like below

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

context.DeleteObject(customer);

context.Savechanges();

So I need to hit database twice. Is there a easier way?

Pac0
  • 16,761
  • 4
  • 49
  • 67
Jeff
  • 1,181
  • 2
  • 7
  • 3

9 Answers9

115

In Entity Framework 6 the delete action is Remove. Here is an example

Customer customer = new Customer () { Id = id };
context.Customers.Attach(customer);
context.Customers.Remove(customer);
context.SaveChanges();
dwkd
  • 2,280
  • 1
  • 15
  • 15
  • 19
    Why `Attach`? Why not just `Remove` and `SaveChanges`? – runeks Sep 25 '18 at 12:45
  • 4
    You have to attach your entity in the context because if you don't do that, you will receive an error while removing. EF can remove entities in this context only – Pierre-Luc Jan 09 '19 at 14:43
  • 5
    @runeks according to the manual the entity must exist in the context before the Remove operation can be performed. See here https://docs.microsoft.com/en-us/dotnet/api/system.data.entity.dbset.remove?view=entity-framework-6.2.0 – dwkd Jan 28 '19 at 23:51
  • 2
    i didn't use attach, and it woks fine – ILIAS M. DOLAPO Jan 15 '20 at 15:22
61

The same as @Nix with a small change to be strongly typed:

If you don't want to query for it just create an entity, and then delete it.

                Customer customer = new Customer () { Id = id };
                context.Customers.Attach(customer);
                context.Customers.DeleteObject(customer);
                context.SaveChanges();
Sawan
  • 6,233
  • 9
  • 49
  • 98
  • 7
    Not perfect as it throws an exception if the object is missing: "DbUpdateConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0)." I'd like it to ignore this, like a DELETE statement would. – Dunc Jan 22 '15 at 15:34
  • sorry, this causes validation which is not needed and expected always! – Hamed Zakery Miab Jul 16 '17 at 12:35
34

Similar question here.

With Entity Framework there is EntityFramework-Plus (extensions library).
Available on NuGet. Then you can write something like:

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

It is also useful for bulk deletes.

Soren
  • 5,295
  • 5
  • 36
  • 43
acarlon
  • 14,908
  • 7
  • 66
  • 87
23

If you dont want to query for it just create an entity, and then delete it.

Customer customer  = new Customer() {  Id = 1   } ; 
context.AttachTo("Customers", customer);
context.DeleteObject(customer);
context.Savechanges();
Nix
  • 52,320
  • 25
  • 137
  • 193
7

I am using the following code in one of my projects:

    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;
            }
        }
    }

This way, it will query the database twice only if an exception occurs when trying to remove the item with the specified ID. Then if the item is not found, it returns a meaningful message; otherwise, it just throws the exception back (you can handle this in a way more fit to your case using different catch blocks for different exception types, add more custom checks using if blocks etc.).

[I am using this code in a MVC .Net Core/.Net Core project with Entity Framework Core.]

demonicdaron
  • 1,236
  • 14
  • 27
3

dwkd's answer mostly worked for me in Entity Framework core, except when I saw this exception:

InvalidOperationException: The instance of entity type 'Customer' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

To avoid the exception, I updated the code:

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();
Jeffrey Rennie
  • 2,436
  • 1
  • 11
  • 14
3

A smaller version (when compared to previous ones):

var customer = context.Find(id);
context.Delete(customer);
context.SaveChanges();
Luis Gouveia
  • 4,095
  • 5
  • 34
  • 51
  • Please provide some context to this code snippet, and perhaps some explanation of what it does better than the other answers left in the past decade. – miken32 Jan 20 '20 at 20:04
3

This answer is actually taken from Scott Allen's course titled ASP.NET MVC 5 Fundamentals. I thought I'd share because I think it is slightly simpler and more intuitive than any of the answers here already. Also note according to Scott Allen and other trainings I've done, find method is an optimized way to retrieve a resource from database that can use caching if it already has been retrieved. In this code, collection refers to a DBSet of objects. Object can be any generic object type.

        var object = context.collection.Find(id);  
        context.collection.Remove(object);
        context.SaveChanges();
andy_coder
  • 109
  • 1
  • 3
  • 10
1

Raw sql query is fastest way I suppose

public void DeleteCustomer(int id)
{
   using (var context = new Context())
   {
      const string query = "DELETE FROM [dbo].[Customers] WHERE [id]={0}";
      var rows = context.Database.ExecuteSqlCommand(query,id);
      // rows >= 1 - count of deleted rows,
      // rows = 0 - nothing to delete.
   }
}
Jonik
  • 954
  • 2
  • 9
  • 20
  • 23
    This defeats the purpose of using strongly typed object funtionality in EF. – LawMan Mar 04 '15 at 17:20
  • 4
    This compromises EF identity cash. After this EF will still return to you your deleted entity. – epox Aug 26 '16 at 23:17
  • 1
    It works with Azure SQL DataWarehouse, when other solutions do not. – Michael Freidgeim Sep 08 '16 at 11:37
  • 1
    If you're doing this, you might as well not use an ORM. I imagine that this would compromise the EF cache. – Storm Muller Oct 06 '18 at 11:31
  • This style is vulnerable to SQL Injection attacks. In this specific example you're protected because the variable is an integer, but never use this pattern with a string variable. – thelem Jun 29 '20 at 11:23