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:

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;

Optimize LINQ to SQL with PLINQO Futures

PLINQO future queries are a way to defer query execution until it is needed. The difference between standard IQueryable deferred queries and future queries is that the future queries are batched up and executed in a single round trip to the database.

Read more at PLINQO.com


SvnBackup - Backup Tool For Subversion Repositories

Overview

The SvnBackup command line tool is used to create backup copies of your subversion repositories. The source code is the life blood of your application. Keeping the source repository backed up is major part in keeping your team going in case something goes wrong with your repository.

Features

  • Backup repository using hotcopy command
  • Backup folder management
  • Support repository parent directory
  • Keep x number of backups
  • Compress backups

Backup Process

SvnBackup follows the recommend way of backing up your subversion repository. While you can xcopy your repository, it may not always be the safest. SvnBackup automates the process by using svnadmin hotcopy command. The hotcopy command is the only safe way to make a backup copy of your repository.

SvnBackup also support starting from a parent folder that has all your repositories. The tool will loop through all the repositories in that folder backing each up. The following folder layout contains imaginary repositories: calculator, calendar, and spreadsheet.

repo/
   calculator/
   calendar/
   spreadsheet/

The backups are stored in a root backup folder. SvnBackup will create a subfolder for each repository. Then it will create a folder for the current revision being backed up. The hotcopy will be placed in the revision folder. This allows you to keep multiple backup versions of your repository. The following is an example of the backup folder structure created by SvnBackup.

backup/
   calculator/
      v0000001/
      v0000008/
      v0000017/
   calendar/
      v0000001/
      v0000014/
      v0000127/
   spreadsheet/
      v0000001/
      v0000023/
      v0000047/

SvnBackup supports pruning your backups to only keep so many. For example, you can keep the last 10 backups.

Another feature of SvnBackup is to compress the backup. If you have a lot of repositories, zipping up the backup can save a lot of space.

Command Line Options

SvnBackup.exe /r:<directory> /b:<directory> /c

     - BACKUP OPTIONS -

/history:<int>        Number of backups to keep. (/n)
/compress             Compress backup folders. (/c)
/repository:<string>  Repository root folder. (/r)
/backup:<string>      Backup root folder. (/b)
/svn:<string>         Path to subversion bin folder. (/s)

Project Page


Caching the results from LinqDataSource

I wanted to be able to cache the results of a query from the LinqDataSource that was used in multiple places on the page.  I whipped up this little class to do the work of caching for me.  The class, LinqCacheDataSource, handles the Selecting and Selected events.  The Selected handler inserts the result of the query into cache.  The Selecting handler gets the result from the cache.  If it doesn't find the result, the query runs as normal.  The caching will only work for selecting data.  It works great for dropdowns and other fairly static data.

The Code ...

/// <summary>
/// A LinqDataSource that provides caching for Linq queries.
/// </summary>
public class LinqCacheDataSource : LinqDataSource
{
    /// <summary>
    /// Initializes a new instance of the <see cref="LinqCacheDataSource"/> class.
    /// </summary>
    public LinqCacheDataSource()
        : base()
    {
        this.Selecting += new EventHandler<LinqDataSourceSelectEventArgs>(OnSelecting);
        this.Selected += new EventHandler<LinqDataSourceStatusEventArgs>(OnSelected);
    }

    private void OnSelecting(object sender, LinqDataSourceSelectEventArgs e)
    {
        if (!EnableCache)
            return;

        string key = GetKey();
        object source = Context.Cache[key];
        if (source == null)
            return;

        Debug.WriteLine("Cache Hit: " + key);
        e.Result = source;
    }

    private void OnSelected(object sender, LinqDataSourceStatusEventArgs e)
    {
        if (!EnableCache)
            return;

        if (e.Exception != null || e.Result == null)
            return;

        string key = GetKey();
        object source = Context.Cache[key];
        if (source != null)
            return;

        Debug.WriteLine("Cache Insert: " + key);
        Context.Cache.Insert(key, e.Result, null,
            DateTime.Now.AddSeconds(Duration), Cache.NoSlidingExpiration);
    }

    private string GetKey()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append(this.ContextTypeName);
        sb.Append(" from ");
        sb.Append(this.TableName);

        if (!string.IsNullOrEmpty(this.Select))
        {
            sb.Append(" select ");
            sb.Append(this.Select);
        }
        if (!string.IsNullOrEmpty(this.Where))
        {
            sb.Append(" where ");
            sb.Append(this.Where);
        }
        if (!string.IsNullOrEmpty(this.OrderBy))
        {
            sb.Append(" OrderBy ");
            sb.Append(this.OrderBy);
        }
        return sb.ToString();
    }

    /// <summary>
    /// Gets or sets a value indicating whether query caching is enabled.
    /// </summary>
    [DefaultValue(true)]
    [Category("Cache")]
    [Description("Enable caching the linq query result.")]
    public bool EnableCache
    {
        get
        {
            object result = this.ViewState["EnableCache"];
            if (result != null)
                return (bool)result;

            return true;
        }
        set
        {
            this.ViewState["EnableCache"] = value;
        }
    }

    /// <summary>
    /// Gets or sets the time, in seconds, that the query is cached.
    /// </summary>
    [DefaultValue(30)]
    [Category("Cache")]
    [Description("The time, in seconds, that the query is cached.")]
    public int Duration
    {
        get
        {
            object result = this.ViewState["Duration"];
            if (result != null)
                return (int)result;

            return 30;
        }
        set
        {
            this.ViewState["Duration"] = value;
        }
    }
} 

PLINQO - CodeSmith LINQ to SQL Templates

PLINQO, which stands for Professional LINQ to Objects, is a collection of CodeSmith templates that are meant to replace and extend the LINQ to SQL designers that are included with Visual Studio 2008.

Features

  • Generate or update a LINQ to SQL dbml file from a database schema.
    • Includes all tables, stored procedures, functions, and views with the ability to exclude objects based on regex patterns.
    • Ability to automatically remove object prefix and suffixes (ie. tbl_ and usp_).
    • Dbml file can still be customized with the normal Visual Studio 2008 designer.
    • Dbml file can be refreshed based on the current database schema without losing customizations. (See Safe Attributes)
  • Generation of the LINQ to SQL DataContext class.
  • Generation of the LINQ to SQL entity classes.
    • Generates one file per entity instead of one massive file.
    • Generates partial classes where custom code can be written and won't be overwritten.
    • Generated entity files are added to the project as code behind files to their corresponding custom entity files.
  • Generation of entity manager classes.
    • Adds customizable business rules engine to enforce entity validation, business and security rules.
    • Provides access to common queries based on primary keys, foreign keys, and indexes.
    • Common queries are exposed as IQueryable so they can be extended.
  • All templates can be customized to meet your needs.

Read More

http://community.codesmithtools.com/blogs/pwelter/archive/2007/08/08/plinqo.aspx

Download

http://www.codeplex.com/codesmith/Release/ProjectReleases.aspx