Entity Framework Batch Update and Future Queries

Entity Framework Extended Library

A library the extends the functionality of Entity Framework.

Features

  • Batch Update and Delete
  • Future Queries
  • Audit Log
Project Package and Source

NuGet Package

PM> Install-Package EntityFramework.Extended

Batch Update and Delete

A current limitations of the Entity Framework is that in order to update or delete an entity you have to first retrieve it into memory. Now in most scenarios this is just fine. There are however some senerios where performance would suffer. Also, for single deletes, the object must be retrieved before it can be deleted requiring two calls to the database. Batch update and delete eliminates the need to retrieve and load an entity before modifying it.

Deleting

//delete all users where FirstName matches
context.Users.Delete(u => u.FirstName == "firstname");

Update

//update all tasks with status of 1 to status of 2
context.Tasks.Update(
    t => t.StatusId == 1, 
    t => new Task {StatusId = 2});

//example of using an IQueryable as the filter for the update
var users = context.Users
   .Where(u => u.FirstName == "firstname");

context.Users.Update(
    users, 
    u => new User {FirstName = "newfirstname"});
Future Queries

Build up a list of queries for the data that you need and the first time any of the results are accessed, all the data will retrieved in one round trip to the database server. Reducing the number of trips to the database is a great. Using this feature is as simple as appending .Future() to the end of your queries. To use the Future Queries, make sure to import the EntityFramework.Extensions namespace.

Future queries are created with the following extension methods...

  • Future()
  • FutureFirstOrDefault()
  • FutureCount()

Sample

// build up queries
var q1 = db.Users
    .Where(t => t.EmailAddress == "one@test.com")
    .Future();

var q2 = db.Tasks
    .Where(t => t.Summary == "Test")
    .Future();

// this triggers the loading of all the future queries
var users = q1.ToList();

In the example above, there are 2 queries built up, as soon as one of the queries is enumerated, it triggers the batch load of both queries.

// base query
var q = db.Tasks.Where(t => t.Priority == 2);
// get total count
var q1 = q.FutureCount();
// get page
var q2 = q.Skip(pageIndex).Take(pageSize).Future();

// triggers execute as a batch
int total = q1.Value;
var tasks = q2.ToList();

In this example, we have a common senerio where you want to page a list of tasks. In order for the GUI to setup the paging control, you need a total count. With Future, we can batch together the queries to get all the data in one database call.

Future queries work by creating the appropriate IFutureQuery object that keeps the IQuerable. The IFutureQuery object is then stored in IFutureContext.FutureQueries list. Then, when one of the IFutureQuery objects is enumerated, it calls back to IFutureContext.ExecuteFutureQueries() via the LoadAction delegate. ExecuteFutureQueries builds a batch query from all the stored IFutureQuery objects. Finally, all the IFutureQuery objects are updated with the results from the query.

Audit Log

The Audit Log feature will capture the changes to entities anytime they are submitted to the database. The Audit Log captures only the entities that are changed and only the properties on those entities that were changed. The before and after values are recorded. AuditLogger.LastAudit is where this information is held and there is a ToXml() method that makes it easy to turn the AuditLog into xml for easy storage.

The AuditLog can be customized via attributes on the entities or via a Fluent Configuration API.

Fluent Configuration

// config audit when your application is starting up...
var auditConfiguration = AuditConfiguration.Default;

auditConfiguration.IncludeRelationships = true;
auditConfiguration.LoadRelationships = true;
auditConfiguration.DefaultAuditable = true;

// customize the audit for Task entity
auditConfiguration.IsAuditable<Task>()
    .NotAudited(t => t.TaskExtended)
    .FormatWith(t => t.Status, v => FormatStatus(v));

// set the display member when status is a foreign key
auditConfiguration.IsAuditable<Status>()
    .DisplayMember(t => t.Name);

Create an Audit Log

var db = new TrackerContext();
var audit = db.BeginAudit();

// make some updates ...

db.SaveChanges();
var log = audit.LastLog;
Published Tuesday, November 29, 2011 10:24 AM by pwelter34
Filed under: ,

Comments

# re: Entity Framework Batch Update and Future Queries

great features! i will try them for sure!

Wednesday, November 30, 2011 8:58 AM by Alexandre Jobin

# re: Entity Framework Batch Update and Future Queries

I have a question question about some of your code posted here.  Is there any particular license for it if I want to include it in a commercial product?

Wednesday, November 30, 2011 5:19 PM by Lee

# re: Entity Framework Batch Update and Future Queries

It would be nice if i could use the batch update to increment a value and not only overwrite it.

// this example would increment the UnitPrice with 1 for all products with a UnitPrice above 50

using (var db = new NorthwindEntities()) {

  var rowsUpdated = db.Products.Update(

      prod => prod.UnitPrice > 50,

      prod => new Product { UnitPrice = prod.UnitPrice + 1 });

  Console.WriteLine(rowsUpdated);

}

Saturday, December 3, 2011 8:22 AM by sonnemaf

# re: Entity Framework Batch Update and Future Queries

The project is licensed under the bsd license.

www.opensource.org/.../bsd-license.php

I'll add a license file to the project to be more clear.

Saturday, December 3, 2011 11:11 AM by pwelter34

# re: Entity Framework Batch Update and Future Queries

sonnemaf, that is a good idea. I'll work on adding that.

Saturday, December 3, 2011 11:12 AM by pwelter34

# re: Entity Framework Batch Update and Future Queries

Thanks Peter!  I was also thinking about using your XML Serializable Generic Dictionary.  Is that also under BSD?

Friday, December 9, 2011 2:33 PM by Lee

# re: Entity Framework Batch Update and Future Queries

"The source query must be of type ObjectQuery or DbQuery."

Is this incompatible with LinqKit's AsExpandable method?

Tuesday, December 13, 2011 3:33 PM by sam mckoy

# re: Entity Framework Batch Update and Future Queries

Forgot to mention I was using the .Future() methods.

Tuesday, December 13, 2011 3:38 PM by sam mckoy

# re: Entity Framework Batch Update and Future Queries

Hi Paul... Thanks again. To Lee's question above, I too could not find your license for XML Serializable Generic Library.  Is it safe to assume BSD as well?

Monday, December 19, 2011 2:44 PM by Bryan G

# re: Entity Framework Batch Update and Future Queries

Yes, all software I release is under BSD license.

Monday, December 19, 2011 8:52 PM by pwelter34

# re: Entity Framework Batch Update and Future Queries

Sam, i have not tried that combination.  I might be able to work something out with using LinqKit.  It will take some debugging.

Monday, December 19, 2011 8:54 PM by pwelter34

# re: Entity Framework Batch Update and Future Queries

FutureFirstOrDefault should be overloaded as FirstOrDefault is

FutureFirstOrDefault(Expression<Func<TSource, bool>> predicate)

Friday, December 23, 2011 6:40 PM by DotNetWise