Transaction handling with spring data and JDBC Template - spring-data-jpa

I would like to do a batch delete without first making a Select for each object. I didn't find a way with spring data jpa and the entity manager to do this.For this reason I try the JDBC template.
However, I get problems with the pageable.
This code workes fine and all deletes are executed by batching. Unfortunately with an unnecessary select for each entity.
#Transactional
public void deleteFoos() {
Page<Foo> page = repository.findAll(PageRequest.of(0,50);
Pageable pageable = null;
do {
if ( pageable != null ) {
page = repository.findAll( pageable );
}
repository.deleteAll( page.getContent() );
} while ( (pageable = page.nextPageable()) != Pageable.unpaged() );
}
What I have tried now is to implement the whole thing directly with the JDBC template.
#Transactional
public void deleteFoos() {
Page<Foo> page = repository.findAll(PageRequest.of(0,50);
Pageable pageable = null;
do {
if ( pageable != null ) {
page = repository.findAll( pageable );
}
//repository.deleteAll( page.getContent() );
jdbcTemplate.batchUpdate(
"DELETE FROM foo WHERE (id = ?)",
page.getContent(),
page.getTotalItems(),
( preparedStatement, argument ) -> {
preparedStatement.setObject( 1, argument.getId() );
} );
} while ( (pageable = page.nextPageable()) != Pageable.unpaged() );
}
However, the third and fourth page is always empty. It seems the delete is executed directly. Auto commit is off, what else could be wrong?
EDIT: Solution from Jens works fine: "You can simply repeatedly request the first page and delete it."

The JdbcTemplate does simply execute SQL statements, so yes, they get immediately executed. There is no caching involved, as with JPA operations.
But even JPA operations get flushed to the database before a query gets executed, so the difference isn't that big.
You can simply repeatedly request the first page and delete it.
But using pagination with deletes doesn't make much sense, you could just delete all rows from the table and be done with it in a single roundtrip to the database:
DELETE FROM foo
If you want to delete in batches you could still do this without loading anything in your application. The details might be database dependent, but something similar to this should work nicely:
DELETE FROM foo
LIMIT 100;

Related

EF Core IgnoreQueryFilters behavior on multiple subsequent queries

i've a problem with the IgnoreQueryFilters.
I've implemented soft-delete using the HasQueryFilter ( in the OnModelCreating i apply the global query filter to every entity which implements a particular interface ).
The problem is that if i launch a query 2 times in the same request:
the first time asking for also the "IsDeleted = true" entities ( so including IgnoreQueryFilters ),
and the second time asking only for the "IsDeleted = false" ( so not including the IgnoreQueryFilters)
the second time i still get also the "deleted" entities.
I think that this happens because when i launch the query for the second time, the entities are already in the context and i get them instead of the right results.
Here how i build the method for "including / excluding" the deleted entities.
// this is my repo pattern implementation
public class MyEntityRepo()
{
....
public Task<List<MyEntity>> GetEntityByUserId(int userId, bool ignoreQueryFilter = false)
{
var query = context.blabla
.Include(c => c.blabla2)
.Where(c => c.ApplicationUserId == userId);
if (ignoreQueryFilter)
{
query = query.IgnoreQueryFilters();
}
var result = await query.ToListAsync();
return result;
}
}
Now if in a service i call it this way:
public void MyServiceMethod()
{
...
var IncludeDeleted = await myEntityRepo.GetEntityByUserId(1, true);
//Here i need to do a sync with other data and for this reason i need also the deleted entities
foreach( var e in includeDeleted)
{
// do something
}
await context.SaveChangesAsync();
//Now that my data is correctly synced i've to get the data again but this time excluding the deleted entities
// and it fails
var ExcludeDeleted = await myEntityRepo.GetEntityByUserId(1, false);
return ExcludeDeleted;
}
The only way i found to solve the problem is to do something like context.ChangeTracker.Clear() before the second call to myEntityRepo.GetEntityByUserId, but is this the right way to go?
Since in real the method is a little bit more complex and can be re-used in other areas, i'm not sure that calling a Clear is a good idea because tomorrow it might be called in a bigger method and cause unexpected problems.
What's the best practice to use when i need to get data with and without query filter?
Is it ok to clear the change tracker?
If yes, what's the best time to clear it? in the GetEntityByUserId if i just ignoredTheFilters ( for consistency ) or after, in the caller method, whenever i find a problem like this one?
Actually i've also thinked about removing the GlobalQueryFilter usage and replace it with methods in the repos that get or exclude deleted entities... yes i've to remember to always filter out but feels more practical.

How to avoid JPA persist directly insert into database?

This is the mysql table.
create table Customer
(
id int auto_increment primary key,
birth date null,
createdTime time null,
updateTime datetime(6) null
);
This my java code
#Before
public void init() {
this.entityManagerFactory=Persistence.createEntityManagerFactory("jpaLearn");
this.entityManager=this.entityManagerFactory.createEntityManager();
this.entityTransaction=this.entityManager.getTransaction();
this.entityTransaction.begin();
}
#Test
public void persistentTest() {
this.entityManager.setFlushMode(FlushModeType.COMMIT); //don't work.
for (int i = 0; i < 1000; i++) {
Customer customer = new Customer();
customer.setBirth(new Date());
customer.setCreatedTime(new Date());
customer.setUpdateTime(new Date());
this.entityManager.persist(customer);
}
}
#After
public void destroy(){
this.entityTransaction.commit();
this.entityManager.close();
this.entityManagerFactory.close();
}
When I reading the wikibooks of JPA, it said "This means that when you call persist, merge, or remove the database DML INSERT, UPDATE, DELETE is not executed, until commit, or until a flush is triggered."
But at same time my code runing, I read the mysql log, I find each time the persist execution, mysql will execute the sql. And I also read the wireShark, each time will cause the request to Database.
I remember jpa saveAll method can send SQL statements to the database in batches? If I wanna to insert 10000 records, how to improve the efficiency?
My answer below supposes that you use Hibernate as jpa implementation. Hibernate doesn't enable batching by default. This means that it'll send a separate SQL statement for each insert/update operation.
You should set hibernate.jdbc.batch_size property to a number bigger than 0.
It is better to set this property in your persistence.xml file where you have your jpa configuration but since you have not posted it in the question, below it is set directly on the EntityManagerFactory.
#Before
public void init() {
Properties properties = new Properties();
properties.put("hibernate.jdbc.batch_size", "5");
this.entityManagerFactory = Persistence.createEntityManagerFactory("jpaLearn", properties);
this.entityManager = this.entityManagerFactory.createEntityManager();
this.entityTransaction = this.entityManager.getTransaction();
this.entityTransaction.begin();
}
Then by observing your logs you should see that the Customer records are persisted in the database in batches of 5.
For further reading please check: https://www.baeldung.com/jpa-hibernate-batch-insert-update
you should enable batch for hibernate
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_size=20
and use
reWriteBatchedInserts&reWriteBatchedStatements=true
end of your connectionString

delete list using JPA

I have list to be deleted.
My code to delete my list is:
for (MyDataModel dataMo: listData) {
testEJB.delete(dataMo.getPkId(), MyDataModel.class);
}
public void delete(Object id, Class<T> classe) {
T entityToBeRemoved = em.getReference(classe, id);
em.remove(entityToBeRemoved);
}
Since my list size may be more than 500, data deletion by this method is much time consuming.I want alternative so that deletion is quicker.I need help.
Ok i have got solution on my own for this i used native query.Here i do not have to generate the list too.My code is:
public int deleteUsingNativeQuery(String query){
Query qry = em.createNativeQuery(query );
return qry.executeUpdate();
}
Here i pass the native query "delete from 'table name' where 'condition'" in function deleteUsingNativeQuery and deletion was also quick.

Why doesn't WebAPI2 with Entity Framework automatically create a transaction for me?

I have a WebAPI2 Restful services API and I am using SQL Server database with Entity Framework. I have PUT methods like this
/*
* This changes the Study Status.
*/
[HttpPut, Route("ResponseSetStatus/{id:int}")]
public IHttpActionResult UpdateResponseSetStatus(int id, [FromUri] string status = null)
{
var db = new MyContext(MyContext.EntityContextString);
var responseSet = db.ResponseSets.FirstOrDefault(x => x.ResponseSetId == id);
if (responseSet == null)
{
return NotFound();
}
// ADD ONE SECOND DELAY HERE FOR TESTING
Thread.Sleep(1000);
responseSet.Status = status;
db.SaveChanges();
return Ok();
}
I thought this would work! But it fails. One of the columns in the database is a rowVersion (to prevent lost updates). When I call this function from multiple clients I get exception...
An exception of type 'System.Data.Entity.Infrastructure.DbUpdateConcurrencyException' occurred in EntityFramework.dll but was not handled in user code
because of rowVersion mismatch. Do I really need an explicit transaction for all my update apis? I thought the framework is supposed to do that for me.
Since no one has answered, I will. Yes, WebAPI2 does not wrap the call in a transaction. That would be silly, if you think about it. Also the code
using (var db = new MyContext()) {
// do stuff
}
does not implicitly create a transaction. Therefore, when you implement a RESTFUL PUT method to update your database, you have three options: (1) call db.SaveChanges() one time only and hope for the best, as the OP code, or (2) you can add a rowVersion column, and call db.SaveChanges() with try-catch in a loop, or (3) you can create an explicit transaction.
In my opinion, option 1 is evil, and option 2 is a terrible hack that was invented because transactions did not exist prior to EF6.
The correct way to implement Update:
[HttpPut, Route("ResponseSetStatus/{id:int}")]
public IHttpActionResult UpdateResponseSetStatus(int id, [FromUri] string status = null)
{
using (var db = new MyContext(MyContext.EntityContextString))
{
using (var tran = db.Database.BeginTransaction())
{
var responseSet = db.ResponseSets.FirstOrDefault(x => x.ResponseSetId == id);
if (responseSet == null)
{
return NotFound();
}
// ADD ONE SECOND DELAY HERE FOR TESTING
Thread.Sleep(1000);
responseSet.Status = status;
tran.Commit();
}
}
return Ok();
}
Please note that try-catch is not necessary. If anything fails, the using tran will automatically rollback, and the WebAPI2 will send a nice 500 response to the client.
p.s. i put the db = new MyContext also in using, because it's the right way to do it.

How do I delete multiple rows in Entity Framework (without foreach)

I want to delete several items from a table using Entity Framework. There is no foreign key / parent object, so I can't handle this with OnDeleteCascade.
Right now I'm doing this:
var widgets = context.Widgets
.Where(w => w.WidgetId == widgetId);
foreach (Widget widget in widgets)
{
context.Widgets.DeleteObject(widget);
}
context.SaveChanges();
It works, but the foreach bugs me. I'm using EF4, but I don't want to execute SQL. I just want to make sure I'm not missing anything -- this is as good as it gets, right? I can abstract the code with an extension method or helper, but somewhere we're still going to be doing a foreach, right?
EntityFramework 6 has made this a bit easier with .RemoveRange().
Example:
db.People.RemoveRange(db.People.Where(x => x.State == "CA"));
db.SaveChanges();
Warning! Do not use this on large datasets!
EF pulls all the data into memory, THEN deletes it. For smaller data sets this might not be an issue but generally avoid this style of delete unless you can guarantee you are only doing very small changes.
You could easily run your process out of memory while EF happily pulls in all the data you specified just to delete it.
using (var context = new DatabaseEntities())
{
context.ExecuteStoreCommand("DELETE FROM YOURTABLE WHERE CustomerID = {0}", customerId);
}
Addition: To support list of ids you can write
var listOfIds = String.Join(',',customerIds.Select(id => $"'{id}'").ToList());
var sql= $#"DELETE [YOURTABLE] WHERE CustomerID in ({listOfIds})";
Note: if CustomerID Is a string, you should double-check for potential SQL injection risks, for integer CustomerID it’s safe
this is as good as it gets, right? I can abstract it with an extension
method or helper, but somewhere we're still going to be doing a
foreach, right?
Well, yes, except you can make it into a two-liner:
context.Widgets.Where(w => w.WidgetId == widgetId)
.ToList().ForEach(context.Widgets.DeleteObject);
context.SaveChanges();
I know it's quite late but in case someone needs a simple solution, the cool thing is you can also add the where clause with it:
public static void DeleteWhere<T>(this DbContext db, Expression<Func<T, bool>> filter) where T : class
{
string selectSql = db.Set<T>().Where(filter).ToString();
string fromWhere = selectSql.Substring(selectSql.IndexOf("FROM"));
string deleteSql = "DELETE [Extent1] " + fromWhere;
db.Database.ExecuteSqlCommand(deleteSql);
}
Note: just tested with MSSQL2008.
Update:
The solution above won't work when EF generates sql statement with parameters, so here's the update for EF5:
public static void DeleteWhere<T>(this DbContext db, Expression<Func<T, bool>> filter) where T : class
{
var query = db.Set<T>().Where(filter);
string selectSql = query.ToString();
string deleteSql = "DELETE [Extent1] " + selectSql.Substring(selectSql.IndexOf("FROM"));
var internalQuery = query.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(field => field.Name == "_internalQuery").Select(field => field.GetValue(query)).First();
var objectQuery = internalQuery.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(field => field.Name == "_objectQuery").Select(field => field.GetValue(internalQuery)).First() as ObjectQuery;
var parameters = objectQuery.Parameters.Select(p => new SqlParameter(p.Name, p.Value)).ToArray();
db.Database.ExecuteSqlCommand(deleteSql, parameters);
}
It requires a little bit of reflection but works well.
If you don't want to execute SQL directly calling DeleteObject in a loop is the best you can do today.
However you can execute SQL and still make it completely general purpose via an extension method, using the approach I describe here.
Although that answer was for 3.5. For 4.0 I would probably use the new ExecuteStoreCommand API under the hood, instead of dropping down to the StoreConnection.
For anyone using EF5, following extension library can be used: https://github.com/loresoft/EntityFramework.Extended
context.Widgets.Delete(w => w.WidgetId == widgetId);
Entity Framework Core
3.1 3.0 2.2 2.1 2.0 1.1 1.0
using (YourContext context = new YourContext ())
{
var widgets = context.Widgets.Where(w => w.WidgetId == widgetId);
context.Widgets.RemoveRange(widgets);
context.SaveChanges();
}
Summary:
Removes the given collection of entities from the context underlying the set
with each entity being put into the Deleted state such that it will be deleted
from the database when SaveChanges is called.
Remarks:
Note that if System.Data.Entity.Infrastructure.DbContextConfiguration.AutoDetectChangesEnabled
is set to true (which is the default), then DetectChanges will be called once
before delete any entities and will not be called again. This means that in some
situations RemoveRange may perform significantly better than calling Remove multiple
times would do. Note that if any entity exists in the context in the Added state,
then this method will cause it to be detached from the context. This is because
an Added entity is assumed not to exist in the database such that trying to delete
it does not make sense.
Still seems crazy to have to pull anything back from the server just to delete it, but at least getting back just the IDs is a lot leaner than pulling down the full entities:
var ids = from w in context.Widgets where w.WidgetId == widgetId select w.Id;
context.Widgets.RemoveRange(from id in ids.AsEnumerable() select new Widget { Id = id });
Finally bulk delete has been introduced in Entity Framework Core 7 via the ExecuteDelete command:
context.Widgets
.Where(w => w.WidgetId == widgetId)
.ExecuteDelete();
Something to note here is that ExecuteDelete does not need a SaveChanges, as per its documentation:
This operation executes immediately against the database, rather than being deferred until DbContext.SaveChanges() is called. It also does not interact with the EF change tracker in any way: entity instances which happen to be tracked when this operation is invoked aren't taken into account, and aren't updated to reflect the changes.
I know that the question was asked for EF4, but if you upgrade this is a good alternative!
EF 6.1
public void DeleteWhere<TEntity>(Expression<Func<TEntity, bool>> predicate = null)
where TEntity : class
{
var dbSet = context.Set<TEntity>();
if (predicate != null)
dbSet.RemoveRange(dbSet.Where(predicate));
else
dbSet.RemoveRange(dbSet);
context.SaveChanges();
}
Usage:
// Delete where condition is met.
DeleteWhere<MyEntity>(d => d.Name == "Something");
Or:
// delete all from entity
DeleteWhere<MyEntity>();
For EF 4.1,
var objectContext = (myEntities as IObjectContextAdapter).ObjectContext;
objectContext.ExecuteStoreCommand("delete from [myTable];");
The quickest way to delete is using a stored procedure. I prefer stored procedures in a database project over dynamic SQL because renames will be handled correctly and have compiler errors. Dynamic SQL could refer to tables that have been deleted/renamed causing run time errors.
In this example, I have two tables List and ListItems. I need a fast way to delete all the ListItems of a given list.
CREATE TABLE [act].[Lists]
(
[Id] INT NOT NULL PRIMARY KEY IDENTITY,
[Name] NVARCHAR(50) NOT NULL
)
GO
CREATE UNIQUE INDEX [IU_Name] ON [act].[Lists] ([Name])
GO
CREATE TABLE [act].[ListItems]
(
[Id] INT NOT NULL IDENTITY,
[ListId] INT NOT NULL,
[Item] NVARCHAR(100) NOT NULL,
CONSTRAINT PK_ListItems_Id PRIMARY KEY NONCLUSTERED (Id),
CONSTRAINT [FK_ListItems_Lists] FOREIGN KEY ([ListId]) REFERENCES [act].[Lists]([Id]) ON DELETE CASCADE
)
go
CREATE UNIQUE CLUSTERED INDEX IX_ListItems_Item
ON [act].[ListItems] ([ListId], [Item]);
GO
CREATE PROCEDURE [act].[DeleteAllItemsInList]
#listId int
AS
DELETE FROM act.ListItems where ListId = #listId
RETURN 0
Now the interesting part of deleting the items and updating Entity framework using an extension.
public static class ListExtension
{
public static void DeleteAllListItems(this List list, ActDbContext db)
{
if (list.Id > 0)
{
var listIdParameter = new SqlParameter("ListId", list.Id);
db.Database.ExecuteSqlCommand("[act].[DeleteAllItemsInList] #ListId", listIdParameter);
}
foreach (var listItem in list.ListItems.ToList())
{
db.Entry(listItem).State = EntityState.Detached;
}
}
}
The main code can now use it is as
[TestMethod]
public void DeleteAllItemsInListAfterSavingToDatabase()
{
using (var db = new ActDbContext())
{
var listName = "TestList";
// Clean up
var listInDb = db.Lists.Where(r => r.Name == listName).FirstOrDefault();
if (listInDb != null)
{
db.Lists.Remove(listInDb);
db.SaveChanges();
}
// Test
var list = new List() { Name = listName };
list.ListItems.Add(new ListItem() { Item = "Item 1" });
list.ListItems.Add(new ListItem() { Item = "Item 2" });
db.Lists.Add(list);
db.SaveChanges();
listInDb = db.Lists.Find(list.Id);
Assert.AreEqual(2, list.ListItems.Count);
list.DeleteAllListItems(db);
db.SaveChanges();
listInDb = db.Lists.Find(list.Id);
Assert.AreEqual(0, list.ListItems.Count);
}
}
You can use extensions libraries for doing that like EntityFramework.Extended or Z.EntityFramework.Plus.EF6, there are available for EF 5, 6 or Core. These libraries have great performance when you have to delete or update and they use LINQ. Example for deleting (source plus):
ctx.Users.Where(x => x.LastLoginDate < DateTime.Now.AddYears(-2))
.Delete();
or (source extended)
context.Users.Where(u => u.FirstName == "firstname")
.Delete();
These use native SQL statements, so performance is great.
This answers is for EF Core 7 (I am not aware if they merged EF Core with EF now or not, before they kept the two separately).
EF Core 7 now supports ExecuteUpdate and ExecuteDelete (Bulk updates):
// Delete all Tags (BE CAREFUL!)
await context.Tags.ExecuteDeleteAsync();
// Delete Tags with a condition
await context.Tags.Where(t => t.Text.Contains(".NET")).ExecuteDeleteAsync();
The equivalent SQL queries are:
DELETE FROM [t]
FROM [Tags] AS [t]
DELETE FROM [t]
FROM [Tags] AS [t]
WHERE [t].[Text] LIKE N'%.NET%'
If you want to delete all rows of a table, you can execute sql command
using (var context = new DataDb())
{
context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}
TRUNCATE TABLE (Transact-SQL) Removes all rows from a table without logging the individual row deletions. TRUNCATE TABLE is similar to the DELETE statement with no WHERE clause; however, TRUNCATE TABLE is faster and uses fewer system and transaction log resources.
You can execute sql queries directly as follows :
private int DeleteData()
{
using (var ctx = new MyEntities(this.ConnectionString))
{
if (ctx != null)
{
//Delete command
return ctx.ExecuteStoreCommand("DELETE FROM ALARM WHERE AlarmID > 100");
}
}
return 0;
}
For select we may use
using (var context = new MyContext())
{
var blogs = context.MyTable.SqlQuery("SELECT * FROM dbo.MyTable").ToList();
}
UUHHIVS's is a very elegant and fast way for batch delete, but it must be used with care:
auto generation of transaction: its queries will be encompassed by a transaction
database context independence: its execution has nothing to do with context.SaveChanges()
These issues can be circumvented by taking control of the transaction. The following code illustrates how to batch delete and bulk insert in a transactional manner:
var repo = DataAccess.EntityRepository;
var existingData = repo.All.Where(x => x.ParentId == parentId);
TransactionScope scope = null;
try
{
// this starts the outer transaction
using (scope = new TransactionScope(TransactionScopeOption.Required))
{
// this starts and commits an inner transaction
existingData.Delete();
// var toInsert = ...
// this relies on EntityFramework.BulkInsert library
repo.BulkInsert(toInsert);
// any other context changes can be performed
// this starts and commit an inner transaction
DataAccess.SaveChanges();
// this commit the outer transaction
scope.Complete();
}
}
catch (Exception exc)
{
// this also rollbacks any pending transactions
scope?.Dispose();
}
In EF 7 you can use bulk delete
var ids = widgets.Select(x => x.Id).ToList();
await _mrVodDbContext.Widgets.Where(x => ids.Contains(x.Id)).ExecuteDeleteAsync();
EF core generate
DELETE FROM [i]
FROM [Widgets] AS [i]
WHERE [i].[Id] IN (4,3,2,1)
More about deleting or updating in release notes. https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#basic-executedelete-examples
You can also use the DeleteAllOnSubmit() method by passing it your results in a generic list rather than in var. This way your foreach reduces to one line of code:
List<Widgets> widgetList = context.Widgets
.Where(w => w.WidgetId == widgetId).ToList<Widgets>();
context.Widgets.DeleteAllOnSubmit(widgetList);
context.SubmitChanges();
It probably still uses a loop internally though.
Thanh's answer worked best for me. Deleted all my records in a single server trip. I struggled with actually calling the extension method, so thought I would share mine (EF 6):
I added the extension method to a helper class in my MVC project and changed the name to "RemoveWhere". I inject a dbContext into my controllers, but you could also do a using.
// make a list of items to delete or just use conditionals against fields
var idsToFilter = dbContext.Products
.Where(p => p.IsExpired)
.Select(p => p.ProductId)
.ToList();
// build the expression
Expression<Func<Product, bool>> deleteList =
(a) => idsToFilter.Contains(a.ProductId);
// Run the extension method (make sure you have `using namespace` at the top)
dbContext.RemoveWhere(deleteList);
This generated a single delete statement for the group.
I came up with a great library Zack.EFCore.Batch. It will convert your expression into simple DELETE FROM .... WHERE query. (Like some answers proposed) https://github.com/yangzhongke/Zack.EFCore.Batch
The usage example:
await ctx.DeleteRangeAsync<Book>(b => b.Price > n);
The Zack.EFCore.Batch library has lots of benefits over Z.EntityFramework.Extended https://entityframework-extensions.net/ which does not have true Async methods. (They are just wrappers around sync methods) You can get lots of unexpected issues by using this library in high load environment.
EF 6.=>
var assignmentAddedContent = dbHazirBot.tbl_AssignmentAddedContent.Where(a =>
a.HazirBot_CategoryAssignmentID == categoryAssignment.HazirBot_CategoryAssignmentID);
dbHazirBot.tbl_AssignmentAddedContent.RemoveRange(assignmentAddedContent);
dbHazirBot.SaveChanges();
Best : in EF6 => .RemoveRange()
Example:
db.Table.RemoveRange(db.Table.Where(x => Field == "Something"));
If you are using Generic Repository:
Inside Generic repository, following could be new method.
public void RemoveMultiple(Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _context.Set<T>().Where(predicate);
_context.Set<T>().RemoveRange(query.AsNoTracking());
}
Usage:
_unitOfWork.YOUR_ENTITY.RemoveMultiple(x => x.AccountId == accountId);
_unitOfWork.Complete();
context.Widgets.RemoveRange(context.Widgets.Where(w => w.WidgetId == widgetId).ToList());
db.SaveChanges();
See the answer 'favorite bit of code' that works
Here is how I used it:
// Delete all rows from the WebLog table via the EF database context object
// using a where clause that returns an IEnumerable typed list WebLog class
public IEnumerable<WebLog> DeleteAllWebLogEntries()
{
IEnumerable<WebLog> myEntities = context.WebLog.Where(e => e.WebLog_ID > 0);
context.WebLog.RemoveRange(myEntities);
context.SaveChanges();
return myEntities;
}
In EF 6.2 this works perfectly, sending the delete directly to the database without first loading the entities:
context.Widgets.Where(predicate).Delete();
With a fixed predicate it's quite straightforward:
context.Widgets.Where(w => w.WidgetId == widgetId).Delete();
And if you need a dynamic predicate have a look at LINQKit (Nuget package available), something like this works fine in my case:
Expression<Func<Widget, bool>> predicate = PredicateBuilder.New<Widget>(x => x.UserID == userID);
if (somePropertyValue != null)
{
predicate = predicate.And(w => w.SomeProperty == somePropertyValue);
}
context.Widgets.Where(predicate).Delete();