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;
Posted by pwelter34 | 14 comment(s)
Filed under: ,

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

Posted by pwelter34 | 1 comment(s)
Filed under: , ,

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;

        }

    }

}

 

Posted by pwelter34 | 4 comment(s)
Filed under: ,

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 

Posted by pwelter34 | 11 comment(s)
Filed under: , ,

Easier way to page Linq queries.

The following query extension will make paging a query more natural then skip and take.  Simply append Paginate(page, pageSize) to your query.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Linq
{
    public static class PageQuery
    {
        public static IQueryable<T> Paginate<T>(
            this IQueryable<T> query, int page, int pageSize)
        {
            int skip = Math.Max(pageSize * (page - 1), 0);
            return query.Skip(skip).Take(pageSize);
        }
    }
}

Posted by pwelter34 | with no comments
Filed under: ,

Calculator.NET - Calculator that evaluates math expressions

I'd like to announce the release of a little project I've been working on.  I call it Calculator.NET.  I started this project for a couple reasons.  First, I was annoyed that Windows Vista doesn't come with a better calculator.  Windows XP has Power Calculator, but that doesn't work on Vista.  Next, I was reading a blog about DynCalc by Bart De Smet on how to do mathematical calculations. That gave me the starting point on how to create Calculator.NET. 

As part of the project, I created a MathExpressions library that does the bulk of work.  The library supports math expressions, functions unit conversion and variables. Below are some examples of using the library directly.

MathEvaluator eval = new MathEvaluator();
//basic math
double result = eval.Evaluate("(2 + 1) * (1 + 2)");
//calling a function
result = eval.Evaluate("sqrt(4)");
//evaluate trigonometric
result = eval.Evaluate("cos(pi * 45 / 180.0)");
//convert inches to feet
result = eval.Evaluate("12 [in->ft]");
//use variable
result = eval.Evaluate("answer * 10");
//add variable
eval.Variables.Add("x", 10);
result = eval.Evaluate("x * 10");

Calculator that evaluates math expressions.
Calculator.NET

Calculator.NET Features

  • Evaluate math expressions including grouping
  • Support trigonometry and other function
  • Common unit conversion of the following types
    • Length
    • Mass
    • Speed
    • Temperature
    • Time
    • Volume
  • Variable support including last answer

Download Calculator.NET

Posted by pwelter34 | 6 comment(s)

CodeSmith 4.0 Public Beta Released

 

CodeSmith 4.0 Beta 2 has been released and is now available to the public.  Here are just a few the new capabilities in CodeSmith 4.0:

  • CodeSmith Projects (.csp) - This feature makes automating your code generation process really easy and consistent whether you are working from inside of Visual Studio 2005, MSBuild, Windows Explorer, a command line / batch file, or CodeSmith itself.
  • ActiveSnippets - Imagine Visual Studio 2005 snippets, but with the full power of CodeSmith available to execute any logic or access any complex metadata (including database schema and xml data) to control the output of your snippets.
  • CodeSmith Maps (.csmap) - This feature will allow you to create dictionary style maps of things like SQL to C# data type mappings.
  • .netTiers 2.0 - The .netTiers templates have been greatly enhanced and included with CodeSmith 4.0.
  • Extended Property Management - You can now edit and add new schema extended properties inside of CodeSmith Studio.
  • Property Persistence - CodeSmith now remembers the property values from the last time you executed a template.

You can read the announcement here.

Posted by pwelter34 | 1 comment(s)
Filed under:

XML Serializable Generic Dictionary

XML Serializable Generic Dictionary

For some reason, the generic Dictionary in .net 2.0 is not XML serializable.  The following code snippet is a xml serializable generic dictionary.  The dictionary is serialzable by implementing the IXmlSerializable interface. 

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Xml.Serialization;

 

    [XmlRoot("dictionary")]

    public class SerializableDictionary<TKey, TValue>

        : Dictionary<TKey, TValue>, IXmlSerializable

    {

        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()

        {

            return null;

        }

 

        public void ReadXml(System.Xml.XmlReader reader)

        {

            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));

            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

 

            bool wasEmpty = reader.IsEmptyElement;

            reader.Read();

 

            if (wasEmpty)

                return;

 

            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)

            {

                reader.ReadStartElement("item");

 

                reader.ReadStartElement("key");

                TKey key = (TKey)keySerializer.Deserialize(reader);

                reader.ReadEndElement();

 

                reader.ReadStartElement("value");

                TValue value = (TValue)valueSerializer.Deserialize(reader);

                reader.ReadEndElement();

 

                this.Add(key, value);

 

                reader.ReadEndElement();

                reader.MoveToContent();

            }

            reader.ReadEndElement();

        }

 

        public void WriteXml(System.Xml.XmlWriter writer)

        {

            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));

            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

 

            foreach (TKey key in this.Keys)

            {

                writer.WriteStartElement("item");

 

                writer.WriteStartElement("key");

                keySerializer.Serialize(writer, key);

                writer.WriteEndElement();

 

                writer.WriteStartElement("value");

                TValue value = this[key];

                valueSerializer.Serialize(writer, value);

                writer.WriteEndElement();

 

                writer.WriteEndElement();

            }

        }

        #endregion

    }

Update: Fixed bug Justin pointed out by adding an extra reader.ReadEndElement() and by checking the IsEmptyElement property.

Posted by pwelter34 | 136 comment(s)
Filed under:

MSBuild Community Tasks Project releases new version

Announcement

The MSBuild Community Tasks Project releases version v1.1.0.145. There are many new tasks in this release.  Special thanks to all the contributors to the project.

Current Community Tasks

Task Description
AppPoolController Allows control for an application pool on a local or remote machine with IIS installed.
AppPoolCreate Creates a new application pool on a local or remote machine.
AppPoolDelete Deletes an existing application pool on a local or remote machine.
AssemblyInfo Generates an AssemblyInfo file using the attributes given.
Attrib Changes the attributes of files and/or directories
FileUpdate Replace text in file(s) using a Regular Expression.
FtpUpload Uploads a file using File Transfer Protocol (FTP).
FxCop Uses FxCop to analyze managed code assemblies and reports on their design best-practice compliance.
Mail Sends an email message.
Math.Add Add numbers.
Math.Divide Divide numbers.
Math.Multiple Multiple numbers.
Math.Subtract Subtract numbers.
Move Moves files on the filesystem to a new location.
NDoc Runs NDoc to create documentation.
NUnit Runs tests using the NUnit.
RegistryRead Reads a value from the Registry.
RegistryWrite Writes a value to the Registry.
Script Executes code contained within the task.
ServiceController Task that can control a Windows service.
ServiceQuery Task that can determine the status of a service.
Sleep A task for sleeping for a specified period of time.
SqlExecute Executes a SQL command
SvnCheckout Checkout files from Subversion
SvnClient Subversion Client
SvnCommit Commit files to Subversion
SvnExport Export files from Subversion
SvnInfo Get Subversion information for a file or directory.
SvnUpdate Update files from Subversion
SvnVersion Get Subversion revision number of a local copy
TaskSchema Generates a XSD schema of the MSBuild tasks in an assembly.
Time Gets the current date and time.
Unzip Unzip a file to a target directory.
Version Increments a four-part version number stored in a text file
VssAdd Adds files to a Visual SourceSafe database.
VssCheckin Checks in files to a Visual SourceSafe database.
VssCheckout Checks out files from a Visual SourceSafe database.
VssClean Removes Visual SourceSafe binding information and status files from a Visual Studio solution tree.
VssDiff Generates a diff between two versions of an item in a Visual SourceSafe database.
VssGet Gets the latest version of a file or project from a Visual SourceSafe database.
VssHistory Generates an XML file containing the history of an item in a Visual SourceSafe database between two dates or labels.
VssLabel Labels an item in a Visual SourceSafe database.
VssUndoCheckout Cancels a checkout of an item from a Visual SourceSafe database.
WebDirectoryCreate Creates a new web directory on a local or remote machine.
WebDirectoryDelete Deletes a web directory on a local or remote machine
WebDownload Downloads a resource with the specified URI to a local file.
XmlRead Reads a value from a XML document using a XPath.
XmlWrite Updates a XML document using a XPath.
Xslt Merge and transform a set of xml files.
Zip Create a zip file with the files specified.

Join Project

Please join the MSBuild Community Tasks Project and help contribute in building the tasks. 

http://msbuildtasks.tigris.org/

Download The Latest Release

The latest binaries, source and installer for Windows can be found in this directory of the Tigris file-sharing area.

Posted by pwelter34 | 22 comment(s)
Filed under:

Create a Relative path code snippet

Here is a code snippet that is equivalent to the windows API PathRelativePathTo as native c#. The function creates a relative path from one file or folder to another.

public class PathUtil

{

    /// <summary>

    /// Creates a relative path from one file

    /// or folder to another.

    /// </summary>

    /// <param name="fromDirectory">

    /// Contains the directory that defines the

    /// start of the relative path.

    /// </param>

    /// <param name="toPath">

    /// Contains the path that defines the

    /// endpoint of the relative path.

    /// </param>

    /// <returns>

    /// The relative path from the start

    /// directory to the end path.

    /// </returns>

    /// <exception cref="ArgumentNullException"></exception>

    public static string RelativePathTo(

        string fromDirectory, string toPath)

    {

        if (fromDirectory == null)

            throw new ArgumentNullException("fromDirectory");

 

        if (toPath == null)

            throw new ArgumentNullException("toPath");

 

        bool isRooted = Path.IsPathRooted(fromDirectory)

            && Path.IsPathRooted(toPath);

 

        if (isRooted)

        {

            bool isDifferentRoot = string.Compare(

                Path.GetPathRoot(fromDirectory),

                Path.GetPathRoot(toPath), true) != 0;

 

            if (isDifferentRoot)

                return toPath;                         

        }               

 

        StringCollection relativePath = new StringCollection();

        string[] fromDirectories = fromDirectory.Split(

            Path.DirectorySeparatorChar);

 

        string[] toDirectories = toPath.Split(

            Path.DirectorySeparatorChar);

 

        int length = Math.Min(

            fromDirectories.Length,

            toDirectories.Length);

 

        int lastCommonRoot = -1;

 

        // find common root

        for (int x = 0; x < length; x++)

        {

            if (string.Compare(fromDirectories[x],

                toDirectories[x], true) != 0)

                break;

 

            lastCommonRoot = x;

        }

        if (lastCommonRoot == -1)

            return toPath;

 

        // add relative folders in from path

        for (int x = lastCommonRoot + 1; x < fromDirectories.Length; x++)

            if (fromDirectories[x].Length > 0)

                relativePath.Add("..");

 

        // add to folders to path

        for (int x = lastCommonRoot + 1; x < toDirectories.Length; x++)

            relativePath.Add(toDirectories[x]);

 

        // create relative path

        string[] relativeParts = new string[relativePath.Count];

        relativePath.CopyTo(relativeParts, 0);

 

        string newPath = string.Join(

            Path.DirectorySeparatorChar.ToString(),

            relativeParts);

 

        return newPath;

    }

 

}

Posted by pwelter34 | 8 comment(s)
Filed under:
More Posts Next page »