Generic Session Helper Class for ASP.NET MVC

I just wrote this. I haven’t tested it. I haven’t even run it. But surely it works, right!?!

What it is is a little helper class to allow you to get objects from the session in ASP.NET MVC (or, I assume, WebForms, though I don’t touch the stuff)

  1. namespace AwesomeWebApp.Helpers.Extensions
  2. {
  3.   using System;
  4.   using System.Web;
  5.  
  6.   public static class SessionExtensions
  7.   {
  8.     public static void AddItem<T>(this HttpSessionStateBase session, T item) where T : class
  9.     {
  10.       session[GetKey(item.GetType())] = item;
  11.     }
  12.  
  13.     public static T GetItem<T>(this HttpSessionStateBase session) where T : class
  14.     {
  15.       return session[GetKey(typeof(T))] as T;
  16.     }
  17.  
  18.     public static string GetKey(Type itemType)
  19.     {
  20.       return itemType.FullName;
  21.     }
  22.   }
  23. }

Pretty simple, no? You use it like this

  1. public ActionResult FirstAction()
  2.   {
  3.     var model = GetModelFromInput();
  4.     Session.AddItem(model);
  5.     return RedirectToAction("SecondAction");
  6.   }
  7.  
  8.   public ActionResult SecondAction()
  9.   {
  10.     var model = Session.GetItem<AwesomeModel>();
  11.     return View(model);
  12.   }

So, that’s it! I’m going to try to blog more smaller things like this. If this helped you, then do something nice for someone today! Or else I’ll hurt you. :)

Leave the first comment

Using the Kendo UI Grid with oData on WCF Data Services

While working on a project, one of the requirements was to implement a table that includes filtering and sorting on a collection possibly containing thousands or tens of thousands of items. I started implementing the features myself, but I was reminded that we had an available license to use the Kendo UI Grid.

The control looks good – it’s implemented as a jQuery plugin and has all the features we need. The problem was, as is usually the case when working with third-party components, integrating with our existing system. The data we need for the grid already had an OData service endpoint using WCF Data Services, and the Kendo grid is already set up to use OData as a data source.

However, when we tried to implement the grid “out of the box” we kept receiving JavaScript errors, the most prevalent of which was:

Uncaught TypeError: Cannot read property '__count' of undefined

There is a good post on the Kendo UI blog about using Kendo UI with the ASP.NET Web API, but the code given in that post didn’t fix my problem. As I mentioned, we are using WCF Data Services, not the ASP.NET Web API, for our OData service. That may be the reason the solution wasn’t working. It looked like the response I received from my data source was different than they were expecting, and I kept getting the __count error.

Using the following was the solution that worked for us:


 $(elementId).kendoGrid({
                dataSource: {
                    filter: [{ field: "ParentId", operator: "eq", value: parentId }],
                    type: "odata",
                    transport: {
                        read: "/Address/To/Service",
                        dataType: "json"
                    },
                    schema: {
                        data: function(data) {
                            return data.value;
                        },
                        total: function(data) {
                            return data['odata.count'];

                        },
                        model: {
                            fields: {
                                FieldName: { type: "string" },
                                DateField: { type: "date" },
                                NumericField: { type: "number" }
                            }
                        }
                    },
                    pageSize: 10,
                    serverPaging: true,
                    serverFiltering: true,
                    serverSorting: true
                },
                scrollable: {
                    virtual: true
                },
                height: 250,
                filterable: true,
                sortable: true,
                pageable: true,
                groupable: true,
                selectable: "row",
                columns: [
                    { field: "FieldName", title: "Field Name" },
                    { field: "DateField", title: "Date Field", template: '#= kendo.toString(DateField, "MM/dd/yyyy") #' },
                    { field: "NumericField", template: '#= kendo.toString(NumericField, "c2") #' }
                ]
            });

Hopefully this will help someone else running into the same issue!

5 comments so far, add yours

Clean Code

Last week I attended the Heartland Developers’ Conference here in Omaha, Nebraska. There were a lot of good sessions there, and one of my favorites was the session on Clean Code, given by Cory House. There were a lot of great points in the talk, which Cory admitted were distilled from a few books: Code Complete, The Pragmatic Programmer and, appropriately, Clean Code.

One of the best tips in the talk that I took home was “If you’re about to write a comment, think about writing a method instead”. Awesome. For instance, if I have a block of code that checks to see whether an account is valid, instead of writing a comment about that, I can make a method called AccountIsValid(account, otherThings). Then I just do if(!AccountIsValid()) throw; Done!

Since I took pretty good notes, here they are (taken in a tool called FreeMind) :

Clean Code

One comment so far, add another

A Type-Safe Cache with Delegates for ASP.NET (also MVC)

Caching is great. It’s what allows all of our favorite websites to run lightning-fast. ASP.NET has a robust caching system built in which can hold value types or serializable reference types. It can also perform caching of output from an ActionResult or PartialViewResult rendered from a controller action method.

However, the existing ASP.NET caching has some fairly severe limitations, especially if you are trying to use the Cache API directly and not the handy-dandy [OutputCache] attribute. The two patterns I find myself repeating over and over are:

  • Checking if an item exists in the cache, then writing it if not
  • Casting an item once I receive it back from the cache

I have previously used a class called CacheHelper from Ben Griswold which works great for the casting problem. However, it does not address the pattern of check, add, repeat. I’ve recently been getting more comfortable with Delegates and Expressions in C#, and this seemed like a great opportunity to put that knowledge to use.

My solution uses an Expression<Func<T>> to fetch results. The main implementation method is as follows:

        private static int DefaultCacheTime { get { return Properties.Settings.Default.DefaultCacheMinutes; } }
        private static Cache HttpCache { get { return HttpContext.Current == null ? null : HttpContext.Current.Cache; } }

        public T Get(string key, int expirationMinutes, Expression> expr)
        {
            // Get the delegate to evaluate
            var func = expr.Compile();

            // If we don't have a cache, just return
            if (HttpCache == null) return func();

            // Default the cache time if we didn't receive it
            if (expirationMinutes == 0) expirationMinutes = DefaultCacheTime;

            var item = Get(key);
            if (item == null || item.Equals(default(T)))
            {
                item = func();
                HttpCache.Insert(
                    key,
                    item,
                    null,
                    DateTime.Now.AddMinutes(expirationMinutes),
                    Cache.NoSlidingExpiration
                );
            }
            return item;
        }

This works great, but it leaves unsolved another issue, which I despise: having to create a string key for each item you want to retrieve from the cache. Luckily, we can use the Expression to generate a string key for us! Specifically, we inspect the body of the expression and just use that as the key. There are also a couple of overloads for convenience.

        public T Get(Expression> expr)
        {
            return Get(DefaultCacheTime, expr);
        }

        public T Get(string key, Expression> expr)
        {
            return Get(key, DefaultCacheTime, expr);
        }

        public T Get(int expirationMinutes, Expression> expr)
        {
            var key = GetKey(expr);
            return Get(key, expirationMinutes, expr);
        }

Our key function generates a string that should be unique per expression, but may turn out to collide if the text of two expressions are identical but used in different contexts. I haven’t found a solution to that issue yet – if you have one, please offer it!

        private static string GetKey(Expression> expr)
        {
            var body = expr.Body.ToString();
            var bodyBytes = Encoding.UTF8.GetBytes(body);
            var CryptoProvider = new SHA1CryptoServiceProvider();
            string bodyHash = BitConverter.ToString(CryptoProvider.ComputeHash(bodyBytes));
            var typeName = typeof (T).Name;
            return String.Concat(typeName, "-", bodyHash);
        }

Below is the complete interface for the caching service – the remaining methods should be easily implementable for those who need them.

    public interface ICacheService
    {
        T Get(Expression> expr);
        T Get(string key);
        T Get(string key, Expression> expr);
        T Get(int expirationMinutes, Expression> expr);
        T Get(string key, int expirationMinutes, Expression> expr);
        void Add(object item, string key);
        void Delete(String key);
        void Delete(Expression> expr);
    }
Leave the first comment