How to delete an object by id with entity framework
Asked Answered
C

14

157

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?

Carbide answered 18/3, 2010 at 16:11 Comment(0)
D
155

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();
Dormie answered 20/1, 2015 at 16:33 Comment(6)
Why Attach? Why not just Remove and SaveChanges?Hepsibah
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 onlyOpprobrious
@Hepsibah according to the manual the entity must exist in the context before the Remove operation can be performed. See here learn.microsoft.com/en-us/dotnet/api/…Dormie
i didn't use attach, and it woks fineByre
People might confuse the usage of Remove between Entity Framework Core and Entity Framework. It is true that for Entity Framework, the Remove method needs to be called after the Attach method, as mentioned by @Pierre-Luc. However, for EFCore, the Remove method will begin tracking the entity as deleted.Vacation
I don't think Attach() is necessary unless you're doing more with the object. From what I can see, that method is used the track it in the database during execution.Supernal
G
63

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();
Guacin answered 9/12, 2012 at 12:39 Comment(2)
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.Checkers
sorry, this causes validation which is not needed and expected always!Monroy
F
39

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.

Fascicle answered 23/7, 2013 at 7:11 Comment(6)
It defies reason that this isn't part of the core EF library by now.Maxwell
@FerretallicA - agreed.Fascicle
this method is obsolete use: context.Users.Where(user=> user.Id == id).Delete();Mince
It doesn't work with Azure SQL DataWarehouse due to error "A FROM clause is currently not supported in a DELETE statement.". But the raw SQL as in Jonik's answer works.Lamontlamontagne
Is context.SaveChanges() needed?Katharina
@TomasKubes SaveChanges() is not needed since the EF Plus's .Delete() call automatically does it.Whitt
C
26

Since Entity Framework Core 7 you can use this:

await context.Customers.Where(c => c.Id == 1).ExecuteDeleteAsync();
Celanese answered 20/1, 2023 at 14:34 Comment(1)
However, be aware that the ExecuteDeleteAsync() doesn't respect the Change Tracker and transactional SaveChanges(). Thus, you have to create a facade transaction manually and handle the commit/rollback.Progenitor
G
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();
Galilean answered 25/3, 2010 at 1:16 Comment(0)
D
12

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.]

Decoteau answered 24/6, 2017 at 14:34 Comment(1)
This is the ideal solution.Blastocyst
D
10

In EF Core, if you don't care if the object exists or not, and you just care that it will not be in the DB, the simplest would be:

context.Remove(new Customer(Id: id));  // adds the object in "Deleted" state
context.SaveChanges();                 // commits the removal

You don't really need Attach() - it adds the object to the change tracker in the Unchanged state and Remove() adds the object to the tracker in the Deleted state. The most important thing, however, is that you do only one roundtrip to the backend.

Dewberry answered 21/11, 2022 at 20:31 Comment(0)
S
5

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();
Semiramis answered 10/3, 2020 at 12:19 Comment(3)
object could be null. If it null, .Remove(object); will throw an exception.Decoteau
While this is the easiest syntactically, the OP specifically asked for a way to delete without hitting the database twice. This works but it performs a SELECT (Find) and then a DELETE (Remove/SaveChanges) on the database...and as @demonicdaron already mentioned, it fails if the Find returns a null.Coattail
Yes, this answer complete misses the point.Unshod
I
2

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();
Icono answered 25/4, 2019 at 15:56 Comment(1)
I believe you want to use FirstOrDefault?Calcifuge
P
2

From official documentation (and the most efficient one I have found so far):

Student studentToDelete = new Student() { ID = id };
_context.Entry(studentToDelete).State = EntityState.Deleted;
await _context.SaveChangesAsync();
Prefabricate answered 11/4, 2022 at 5:34 Comment(1)
You need to link that "official document" that mentions it.Smarmy
H
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.
   }
}
Harrisharrisburg answered 28/2, 2014 at 9:49 Comment(7)
This defeats the purpose of using strongly typed object funtionality in EF.Biogen
This compromises EF identity cash. After this EF will still return to you your deleted entity.Patisserie
It works with Azure SQL DataWarehouse, when other solutions do not.Lamontlamontagne
If you're doing this, you might as well not use an ORM. I imagine that this would compromise the EF cache.Grubby
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.Plotinus
great answer for when your Id property is privatePedagogue
@Plotinus There is no SQL injection attack possible here. id is an int, so there is no way to change to be 1 OR 1=1; like there would be with a string parameter.Anele
F
1

A smaller version (when compared to previous ones):

var customer = context.Find(id);
context.Delete(customer);
context.SaveChanges();
Feldman answered 20/1, 2020 at 16:2 Comment(2)
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.Ibarra
No. The very point of the question is how to prevent this roundtrip.Unshod
H
0

Easier and more understandable version.

var customer = context.Find<Customer>(id);
context.Remove(customer);
context.SaveChanges();
Holst answered 2/11, 2022 at 22:8 Comment(0)
G
0

Use this for ios 15++

ForEach(Array(viewModel.fetchResultsHistoryList.enumerated().reversed()), id: \.offset) { _, resultModel in
    Text("Text")
    .swipeActions(allowsFullSwipe: true) {
        Button(role: .destructive) {
            delete(id: resultModel.id)
        } label: {
            Label("Delete", systemImage: "trash")
                .foregroundColor(.white)  // Changes the color of the text and icon
        }
        .buttonStyle(.bordered)
        .tint(.red)  // Changes the background color of the button when using .bordered style
    }
}

Happy Coding !!!

Greerson answered 30/4 at 4:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.