Paul Welter's Weblog

Coding Conundrums of a .NET Developer

  • 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;

            }

        }

    }

     

    Read more...

  • 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);
            }
        }
    }

    Read more...

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

    Read more...

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

    Read more...

  • 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;

        }

     

    }

    Read more...