CraigWardman.com

[ Blog ]

/Blog

A catalogue of my discoveries in software development and related subjects, that I think might be of use or interest to everyone else, or to me when I forget what I did!

Generic Fabric Cache Implementation

The following code shows an example of implementing the Microsoft AppFabric Caching mechanism (part of the Azure framework).

I wanted a generic caching helper which can be used by calling code without too much effort or consideration for the internal workings, such as how data from different sources is partitioned (to avoid ID conflicts).

The way I have done this is to the fully qualified type name, supplied by the generic parameter.

In the event that the AppFabric server could not be contacted on startup, there is an alternative memory based cache, which allows the programmer to specify names of types which should not be included in the memory based cache.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.ApplicationServer.Caching;
using System.Text.RegularExpressions;
 
namespace Kinetic.KiB.Common.FabricCache
{
    public static class FabricCacheHelper
    {
 
        public static object SyncLock = new object();
        static DataCacheFactory myCacheFactory;
        static DataCache myFabricCache;
 
        static FabricCacheHelper()
        {
            PrepareClient();
        }
 
        public static T GetFromCache<T>(string key)
        {
            if (myFabricCache != null)
            {
                //user the region per type and use the key as the identifier within that region
                return (T)myFabricCache.Get(key, Regex.Replace(typeof(T).FullName, "[^a-z0-9]", "", RegexOptions.IgnoreCase));
            }
            else
            {
                return (T)AlternateCache.Get(key, Regex.Replace(typeof(T).FullName, "[^a-z0-9]", "", RegexOptions.IgnoreCase));
            }
        }
 
        public static void AddToCache<T>(string key, T value)
        {
            if (value != null)
            {
                if (myFabricCache != null)
                {
                    //create a region per type and use the key as the identifier within that region
                    myFabricCache.CreateRegion(Regex.Replace(typeof(T).FullName, "[^a-z0-9]", "", RegexOptions.IgnoreCase));
                    myFabricCache.Put(key, value, Regex.Replace(typeof(T).FullName, "[^a-z0-9]", "", RegexOptions.IgnoreCase));
                }
                else
                {
                    //create a region per type and use the key as the identifier within that region
                    AlternateCache.CreateRegion(Regex.Replace(typeof(T).FullName, "[^a-z0-9]", "", RegexOptions.IgnoreCase));
                    AlternateCache.Put(key, value, Regex.Replace(typeof(T).FullName, "[^a-z0-9]", "", RegexOptions.IgnoreCase));
                }
            }
        }
 
        public static void RemoveFromCache<T>(string key)
        {
            if (myFabricCache != null)
            {
                myFabricCache.Remove(key, Regex.Replace(typeof(T).FullName, "[^a-z0-9]", "", RegexOptions.IgnoreCase));
            }
            else
            {
                AlternateCache.Remove(key, Regex.Replace(typeof(T).FullName, "[^a-z0-9]", "", RegexOptions.IgnoreCase));
            }
        }
 
        private static void PrepareClient()
        {
            //-------------------------
            // Configure Cache Client 
            //-------------------------
 
            //Define Array for 1 Cache Host
            List<DataCacheServerEndpoint> servers = new List<DataCacheServerEndpoint>(1);
 
            //Specify Cache Host Details 
            //  Parameter 1 = host name
            //  Parameter 2 = cache port number
            string fabricHost = System.Configuration.ConfigurationManager.AppSettings["FabricHost"];// = "localhost";
            int fabricPort = int.Parse(System.Configuration.ConfigurationManager.AppSettings["FabricPort"]); // 22233;
            servers.Add(new DataCacheServerEndpoint(fabricHost, fabricPort));
 
            //Create cache configuration
            DataCacheFactoryConfiguration configuration = new DataCacheFactoryConfiguration();
 
            //Set the cache host(s)
            configuration.Servers = servers;
 
            //Set default properties for local cache (local cache disabled)
            configuration.LocalCacheProperties = new DataCacheLocalCacheProperties();
 
            //Disable exception messages since this sample works on a cache aside
            DataCacheClientLogManager.ChangeLogLevel(System.Diagnostics.TraceLevel.Off);
 
            //Pass configuration settings to cacheFactory constructor
            myCacheFactory = new DataCacheFactory(configuration);
 
            //Get reference to named cache
            try
            {
                string fabricCacheName = System.Configuration.ConfigurationManager.AppSettings["FabricCacheName"];
                myFabricCache = myCacheFactory.GetCache(string.IsNullOrEmpty(fabricCacheName) ?  "myFabricCache" : fabricCacheName);
            }
            catch (Exception)
            {
                //if anything goes wrong with the fabric, just carry on which will use the alternate method
                myFabricCache = null;
            }
        }
 
        #region "Alternative Cache - Memory Based"
        private static class AlternateCache
        {
            private static Dictionary<string, Dictionary<string, object>> _alternateMemoryCache = new Dictionary<string, Dictionary<string, object>>();
 
            //some types are known to be large, so we wouldnt want them in the w3wp process memory of this alternative cache - they should be ignored (as if not cached)
            private static string[] _typeNameRegionRestrictions = new string[]
            {
            };
 
            internal static object Get(string key, string region)
            {
                Dictionary<string, object> regionCache;
 
                if (_alternateMemoryCache.TryGetValue(region, out regionCache))
                {
                    object value;
                    if (regionCache.TryGetValue(key, out value))
                    {
                        return value;
                    }
                }
 
                //wasnt in the cache
                return null;
            }
 
            internal static void CreateRegion(string region)
            {
                //is this region allowed, or are we blocking this type?
                if (!IsRestrictedRegionName(region))
                {
                    if (!_alternateMemoryCache.ContainsKey(region))
                        _alternateMemoryCache.Add(region, new Dictionary<string, object>());
                }
            }
 
            internal static void Put(string key, object value, string region)
            {
                Dictionary<string, object> regionCache;
                if (_alternateMemoryCache.TryGetValue(region, out regionCache))
                {
                    if (!regionCache.ContainsKey(key))
                        regionCache.Add(key, value);
                    else
                        regionCache[key] = value;
                }
            }
 
            internal static void Remove(string key, string region)
            {
                Dictionary<string, object> regionCache;
                if (_alternateMemoryCache.TryGetValue(region, out regionCache))
                {
                    if (regionCache.ContainsKey(key))
                        regionCache.Remove(key);
                }
            }
 
            private static bool IsRestrictedRegionName(string typeName)
            {
                return _typeNameRegionRestrictions.Contains(typeName);
            }
        }
        #endregion
 
 
    }
}

Unrecognized tag prefix or device filter ‘asp’

I had this problem recently in Visual Studio that drove me mad! It was a VB ASP.NET web project and Intellisense would not work on ASPX pages, complaining about the asp tag prefix.

There is a lot of documented bugs/fixes for this online (google it), including deleting the solution .suo file, clearing out bin/obj files, deleting the visual studio reflected schemas files (under the app data folder), clearing your VS settings.

I tried them all but to no avail.. My problem though, was quite different in 2 ways.. Firstly my project was building and running absolutely fine (not sure about this for the other people’s problems, but maybe worth noting). Also, in my opinion, the cause of my problem was the way I had set up the project to re-use an existing web projects files and code (I was re-using quite a lot of code files from another project by adding them as linked files for the code-infront and a DLL reference for the code behind.)

I have had some problems with VB project in the past due to this kind of thing, because VB does not define the root namespace in the code files it has caused some issues, which led me to believe the problem was being caused by the vbproj settings.

I remembered that by default I have option strict set to “on” for VB projects, since the project was building but the designer was just being “whingy” I decided to turn option strict off… It worked! :)


Warning About Not Found Items

Often applications allow a user to specify inputs by providing the IDs, or unique names of items which can be found in the database.

Using the user provided collection of items, you would query your data and hopefully return a collection with the same number of elements as what the user was searching for.

In some cases though, not all the items the user provided will be found in the database. I recently wrote a generic helper method which provides warnings to the user for any “missing” elements (i.e. they asked for it in the source but it wasn’t in the output).

public static class MissingItemsHelper
    {
        /// <summary>
        /// Will display a message to the user showing any items that existing in the itemsToFind collection that did not exist in the collectionToSearch (using the Equals operator)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="itemsToFind">The collection containing the items which need to be checked against the other list</param>
        /// <param name="collectionToSearch">The collection which will be checked for the existance of each item</param>
        /// <param name="missingItemMessageTextSelector">For items that are missing, this selector function should return the text you want to output in the message for each item</param>
        /// <param name="messageNounText">The singular noun which identifies what you are currently searching</param>
        public static string GetWarningsIfItemsNotInCollection<T>(
            IEnumerable<T> itemsToFind,
            IEnumerable<T> collectionToSearch,
            Func<T, string> missingItemMessageTextSelector,
            string messageNounText)
        {
            //any missing items? - find those which dont have an equal in the target list
            IEnumerable<T> missingItems =
                (from sourceItem in itemsToFind
                 where collectionToSearch.Any((f) => f.Equals(sourceItem)) == false
                 select sourceItem);
 
            //if there were some missing items, show the message to the user with the missing items text
            if (missingItems.Count() > 0)
                    return string.Format("The following {0}s were not found:\r\n", messageNounText)
                                    + string.Join("\r\n",missingItems.Select(missingItemMessageTextSelector).ToArray());
 
            return string.Empty;
        }
    }

I’ve tried to keep it as generic as possible, which is why you must provide the type T of the elements and a function which selects the “missing” information as well as a noun which describes that information.

For example:

MissingItemsHelper.GetWarningsIfItemsNotInCollection<long>(
                    inputIds, (from d in dataItems select d.UniqueId),
                    new Func<long, string>((id) => id.ToString()), "Unique ID");

This could be extended further to take various types with a comparison operator, but for simplicity I have kept to one type.


Tracking Property Changes within Silverlight ViewModel

Using Silverlight and MVVM you often use property change tracking mechanisms, such as implementing INotifyPropertyChanged and using ObservableCollections (which implement INotifyCollectionChanged).

This is fine when you are only interested in letting the UI synchronise with your data in the ViewModel (and vice-versa) but sometimes you need to track these changes in the code, either within your own class or in another class elsewhere.

I had this issue recently, where I needed to know in my “Results” ViewModel when anything changed in any of my various “Input” ViewModels so that I could invalidate the results (so the user knows they need to re-calculate).

In order to achieve this I needed to listen to events in the following 3 categories on many properties:

  • Property changed (setter called)
  • Collection changed (items added/removed)
  • Collection item property changed (element of a list has a property changed)

Rather than having to write event handling code and logic everywhere, I decided to design a base class that my “Input” viewmodels can inherit from that would take care of rolling up these 3 scenarios into a single event. In my “Results” class I then can simply add a listener for the one “InputsChanged” event.

My view model base derives from a base implementing INotifyPropertyChanged and looks as follows:

public abstract class ResultsInputViewModelBase : ViewModelBase
    {
 
        #region "Event"
        /// <summary>
        /// Event is fired whenever a tracked property or collection is changed in the derived class
        /// </summary>
        public event EventHandler InputsChanged;
 
        /// <summary>
        /// Provides a way for derived classes to inform that something in their inputs that requires a re-calculation has changed, 
        /// although they shouldnt need to call this manually - put the property name in the TrackedInputProperties instead
        /// </summary>
        protected void OnInputsChanged()
        {
            if (InputsChanged != null)
                InputsChanged(this, EventArgs.Empty);
        }
        #endregion
 
        #region "Auto-Tracking of Properties"
        /// <summary>
        /// Implement in a derived class to signal each property which needs to raise the InputsChanged event
        /// </summary>
        protected abstract string[] TrackedInputProperties
        {
            get;
        }
 
        /// <summary>
        /// A list of tracked properties that implement INotifyCollectionChanged, therefore need the CollectionChanged tracking 
        /// </summary>
        private Dictionary<string, PropertyInfo> _collectionChangingPropertiesToTrack = new Dictionary<string, PropertyInfo>();
 
        /// <summary>
        /// A list of tracked properties that implement IEnumerable, therefore may need the items tracking 
        /// </summary>
        private Dictionary<string, PropertyInfo> _enumerablePropertiesToTrack = new Dictionary<string, PropertyInfo>();
 
        /// <summary>
        /// In the base constructor we setup the auto-tracking feature, which attaches to PropertyChangedEvent and prepares for tracking CollectionChanged and sub-items PropertyChanged events
        /// </summary>
        public ResultsInputViewModelBase()
        {
            //subscribe the property changed event for this instance
            this.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(ResultsInputViewModelBase_PropertyChanged);
 
            //look for properties that implement INotifyCollectionChanged (observable collections) in the tracked properties so we can auto-track these aswel
            foreach (string propertyName in TrackedInputProperties)
            {
                object currentValue = this;
 
                //find the property info using reflection
                if (!string.IsNullOrWhiteSpace(propertyName))
                {
                    PropertyInfo pi = null;
 
                    //nested property?
                    if (propertyName.Contains("."))
                    {
                        //child property - iterate the tree
                        Type currentType = this.GetType();
 
                        string[] props = propertyName.Split('.');
                        int pc = 0;
                        foreach (string p in props)
                        {
                            pc++;
                            pi = currentType.GetProperty(p);
                            if (pi != null && pc<props.Length)
                            {
                                currentType = pi.PropertyType;
                                currentValue = pi.GetValue(currentValue, null);
                            }
                            else
                            {
                                break;
                            }
                        }
 
                    }
                    else
                    {
                        //direct property
                        pi = this.GetType().GetProperty(propertyName);
                    }
 
                    if (pi != null)
                    {
                        //does this property implement INotifyCollectionChanged?
                        if (typeof(INotifyCollectionChanged).IsAssignableFrom(pi.PropertyType))
                        {
                            //add the PropertyInfo to the list of tracked collections, so we can attach when the value != null
                            _collectionChangingPropertiesToTrack.Add(propertyName, pi);
 
                            //incase its already != null, we can attempt to attach now
                            TryAttachCollectionTracker(pi, currentValue);
                        }
 
                        //does this property implement IEnumerable?
                        if (typeof(IEnumerable).IsAssignableFrom(pi.PropertyType))
                        {
                            //add the PropertyInfo to the list of tracked collections, so we can attach when the value != null
                            _enumerablePropertiesToTrack.Add(propertyName, pi);
 
                            //incase its already != null, we can attempt to attach now
                            TryAttachEnumeratedPropertyChangeTracker(pi, currentValue);
                        }
                    }
                }                
            }
        }
 
        /// <summary>
        /// Attempts to get the value of a property that implements INotifyCollectionChanged, if the value is non-null, attaches to the CollectionChanged event
        /// </summary>
        /// <param name="pi">Property Info instance of the target property</param>
        private void TryAttachCollectionTracker(PropertyInfo pi, object container)
        {
            //read the value of the property for the current instance
            INotifyCollectionChanged collection_propValue = pi.GetValue(container, null) as INotifyCollectionChanged;
 
            //if the property has a value, we can attach to its CollectionChanged event
            if (collection_propValue != null)
                collection_propValue.CollectionChanged += new NotifyCollectionChangedEventHandler(ResultsInputViewModelBase_CollectionChanged);
        }
 
        /// <summary>
        /// Attempts to get the value of a property that implements IEnumerable, if the value is non-null, attaches to the child elements PropertyChanged event (if implemented)
        /// </summary>
        /// <param name="pi">Property Info instance of the target property</param>
        private void TryAttachEnumeratedPropertyChangeTracker(PropertyInfo pi, object container)
        {
            //read the value of the property for the current instance
            IEnumerable collection_propValue = pi.GetValue(container, null) as IEnumerable;
 
            //if the property has a value, we can attach to its CollectionChanged event
            if (collection_propValue != null)
            {
                foreach (var element in collection_propValue)
                {
                    if (element is ResultsInputViewModelBase)
                        ((ResultsInputViewModelBase)element).InputsChanged += this.InputsChanged;
                    else if (element is INotifyPropertyChanged)
                        ((INotifyPropertyChanged)element).PropertyChanged += new PropertyChangedEventHandler(ResultsInputViewModelBase_TrackedElementPropertyChanged);
                }
 
            }
        }
 
        /// <summary>
        /// Fires when any property that raises PropertyChanged event is called in the derived class
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ResultsInputViewModelBase_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            //if the property that has raised the event is one of our tracked properties, we can apply further processing logic
            if (Array.IndexOf(TrackedInputProperties, e.PropertyName) >= 0)
            {
                object currentValue = this;
 
                //find the property info using reflection
                if (!string.IsNullOrWhiteSpace(e.PropertyName))
                {
                    PropertyInfo pi = null;
 
                    //nested property?
                    if (e.PropertyName.Contains("."))
                    {
                        //child property - iterate the tree
                        Type currentType = this.GetType();
 
                        string[] props = e.PropertyName.Split('.');
                        int pc = 0;
                        foreach (string p in props)
                        {
                            pc++;
                            pi = currentType.GetProperty(p);
                            if (pi != null && pc < props.Length)
                            {
                                currentType = pi.PropertyType;
                                currentValue = pi.GetValue(currentValue, null);
                            }
                            else
                            {
                                break;
                            }
                        }
 
                    }
                }
 
 
                //if the value of a changeable collection has just been set, we need to (re-)attach our collection tracker
                if (_collectionChangingPropertiesToTrack.ContainsKey(e.PropertyName))
                    TryAttachCollectionTracker(_collectionChangingPropertiesToTrack[e.PropertyName], currentValue);
 
                //if the value of a enumerable has just been set, we need to (re-)attach our collection tracker
                if (_enumerablePropertiesToTrack.ContainsKey(e.PropertyName))
                    TryAttachEnumeratedPropertyChangeTracker(_enumerablePropertiesToTrack[e.PropertyName], currentValue);
 
                OnInputsChanged();
            }
        }
 
        /// <summary>
        /// Fires when any property of an elelement of a collection that is being tracked is fired
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ResultsInputViewModelBase_TrackedElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            OnInputsChanged();
        }
 
        /// <summary>
        /// Fires when a collection of a tracked property changes
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ResultsInputViewModelBase_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            //we need to attach the property changed etc of the new items
            if (e.NewItems != null)
            {
                foreach (var element in e.NewItems)
                {
                    if (element is ResultsInputViewModelBase)
                        ((ResultsInputViewModelBase)element).InputsChanged += this.InputsChanged;
                    else if (element is INotifyPropertyChanged)
                        ((INotifyPropertyChanged)element).PropertyChanged += new PropertyChangedEventHandler(ResultsInputViewModelBase_TrackedElementPropertyChanged);
                }
            }
 
            //we should stop tracking the items leaving the collection
            if (e.OldItems != null)
            {
                foreach (var element in e.OldItems)
                {
                    if (element is ResultsInputViewModelBase)
                        ((ResultsInputViewModelBase)element).InputsChanged -= this.InputsChanged;
                    else if (element is INotifyPropertyChanged)
                        ((INotifyPropertyChanged)element).PropertyChanged -= new PropertyChangedEventHandler(ResultsInputViewModelBase_TrackedElementPropertyChanged);
                }
            }
 
            OnInputsChanged();
        }
 
        #endregion
    }

This class essentially adds internal listeners to all the different kinds of events that the derived class may raise that we are interested in. It leaves one single property that must be implemented in the derived class, where the programmer can list the properties which should be tracked.

For example:

protected override string[] TrackedInputProperties
        {
            get { return new string[] {
                "AgeBandFilters",
                "MaritalStatusFilters"
            }; }
        }

Finally, in the “Results” ViewModel, I pass in an instance of my derived class and then listen for the “InputsChanged” event:

public ExampleInputViewModel ExampleInputViewModel
        {
            get { return _exampleInputViewModel; }
            set {
                //track changes in input configs to invalidate results
                RemoveInputChangedHandler(_exampleInputViewModel);
                _exampleInputViewModel = value;
                AddInputChangedHandler(_exampleInputViewModel);
 
                RaisePropertyChanged("ExampleInputViewModel"); }
        }
 
 private void RemoveInputChangedHandler(ResultsInputViewModelBase inputConfigViewModel)
        {
            if(inputConfigViewModel != null)
                inputConfigViewModel.InputsChanged -= new EventHandler(inputConfigViewModel_InputsChanged);
        }
 
        private void AddInputChangedHandler(ResultsInputViewModelBase inputConfigViewModel)
        {
            if (inputConfigViewModel != null)
                inputConfigViewModel.InputsChanged += new EventHandler(inputConfigViewModel_InputsChanged);
        }
 
        void inputConfigViewModel_InputsChanged(object sender, EventArgs e)
        {
                //invalidate results
                CurrentResults = null;
        }

SQL ARITHABORT Plan Problems

In SQL Server Management Studio the default setting for Query Execution ‘ARITHABORT’ setting is ON.

In ADO.NET the default setting is OFF.

This can cause problems when trying to optimise your slow running queries, as the execution plans used by .NET and SSMS can be different. This can result in different execution times of your RPC completed vs SP completed in SQL Profiler.

Also, it can cause your ADO.NET based queries to run slower, if the plan was optimised for the SSMS based query.

In order to avoid this problem, I would recommend setting the option to OFF in SSMS:

Tools > Options > Query Execution > SQL Server > Advanced..

Also, if you have come across this problem, after having changed the setting it may be worth clearing your plan cache:

DBCC FREEPROCCACHE

Remote Import with Progress using WCF

Following on from my chunked file uploader, I needed a way to initiate a server side import of a file and track the progress on the Silverlight clientside.

To do this, I first wrote my import logic which was to be performed on the server. The important factor with the import logic, is that it raises an event whenever a line of data has been imported.

I then wrote a WCF service wrapper for this, which uses a separate thread to perform the import so that the service call can return immediately after having started the import. The service comprises of 3 functions to; begin the import, check the progress, get the results.

The service interface and implementation are shown below:

[ServiceContract]
    public interface IExampleImporter
    {
 
        [OperationContract]
        void BeginImportUploadedExampleFile(string uploadedFilename, bool hasHeaderRow);
 
        [OperationContract]
        bool CheckImportUploadedExampleFileComplete(string uploadFilename, out int rowsImported);
 
        [OperationContract]
        Response<ImportResult> GetImportUploadedExampleFileResults(string uploadFilename);
 
    }
public partial class ImportService : IExampleImporter
    {
        //create some static dictionaries for tracking the import progress and results across service calls
        private static Dictionary<string, int> _fileRowsImported = new Dictionary<string,int>();
        private static Dictionary<string, Response<ImportResult>> _fileResults = new Dictionary<string, Response<ImportResult>>();
 
        //we will need a class to define the params to the import thread
        private class ImportUploadedExampleFileThreadParams
        {
            public HttpContext HttpContext { get; set; }
 
            public string UploadFileToken { get; set; }
            public bool HasHeaderRow { get; set; }
        }
 
        //this is the service endpoint to begin a new import
        public void BeginImportUploadedExampleFile(string uploadFileToken, bool hasHeaderRow)
        {
            //you can only import a file which isnt already being imported
            if (_fileResults.ContainsKey(uploadFileToken) || _fileRowsImported.ContainsKey(uploadFileToken))
                throw new Exception("This file is already being imported");
 
            //initialise a place in the rows imported list
            _fileRowsImported.Add(uploadFileToken, 0);
 
            //we will run the import in another thread to avoid service timeouts - client can then poll 'IsUploadComplete'
            System.Threading.Thread importThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ImportUploadedExampleFileThreadEntry));
            importThread.IsBackground = true;
 
            importThread.Start(new ImportUploadedExampleFileThreadParams()
            {
                HttpContext = HttpContext.Current,
 
                UploadFileToken = uploadFileToken,
                HasHeaderRow = hasHeaderRow
 
            });
 
        }
 
        public bool CheckImportUploadedExampleFileComplete(string uploadFileToken, out int rowsImported)
        {
            //does this import exist?
            lock (_fileRowsImported)
            {
                if (!_fileRowsImported.ContainsKey(uploadFileToken))
                    throw new Exception("No current import found for the given token");
 
                rowsImported = _fileRowsImported[uploadFileToken];
            }
 
            //is it complete?
            return _fileResults.ContainsKey(uploadFileToken);
 
        }
 
        public Response<ImportResult> GetImportUploadedExampleFileResults(string uploadFileToken)
        {
            Response<ImportResult> res;
 
            //does this import exist and complete?
            lock (_fileResults)
            {
                if (!_fileResults.ContainsKey(uploadFileToken))
                    throw new Exception("Import with given token does not exist or was not yet complete");
 
                res = _fileResults[uploadFileToken];
 
                //now remove the result
                _fileResults.Remove(uploadFileToken);
            }
 
            //also remove the progress since this import is now done
            lock (_fileRowsImported)
            {
                _fileRowsImported.Remove(uploadFileToken);
            }
 
            return res;
        }
 
        private void ImportUploadedExampleFileThreadEntry(object threadParam)
        {
            //parse the thread params
            ImportUploadedExampleFileThreadParams p = (ImportUploadedExampleFileThreadParams)threadParam;
 
            Response<ImportResult> importResponse = new Response<ImportResult>();
 
            //ensure the file exists
            string fullFilePath = p.HttpContext.Server.MapPath("UploadFiles\\" + p.UploadFileToken);
 
            if (System.IO.File.Exists(fullFilePath))
            {
                 //create an instance of the import helper to perform the import
                 ExampleImporterHelper helper = new ExampleImporterHelper();
                 helper.RowImported += new Action<string, bool>(helper_RowImported);
 
                 //invoke the importer and get the result
                 importResponse.Item = helper.Import(fullFilePath, p.HasHeaderRow);
                 importResponse.IsSuccess = true;
 
            }
            else
            {
                importResponse.IsSuccess = false;
                importResponse.MessageKey = "File not found";
            }
 
            lock (_fileResults)
            {
                _fileResults.Add(p.UploadFileToken, importResponse);
            }
        }
 
        void helper_RowImported(string filename, bool successfulRow)
        {
            //parse the token from the filename
            string token = System.IO.Path.GetFileName(filename);
 
            //update the rows imported
            lock (_fileRowsImported)
            {
                if (_fileRowsImported.ContainsKey(token))
                    _fileRowsImported[token] += 1;
            }
 
        }
 
    }

The way this works is, after having uploaded a file to the server, the client calls ‘BeginImport..’ passing the filename/token which initiates the import. The service creates a new thread which runs the actual import code passing in the required parameters.

The service maintains two Dictionaries, one which stores the current number of lines imported by filename (by counting the RowImported events) and one which stores the eventual ‘ImportResult’ – which exists once the import is complete.

Calling the ‘is complete’ function will return true or false and additionally will return the number of lines imported through the ‘out’ parameter. This enables the client to determine if an upload has finished and if not, how many lines are complete so far – which can be reflected in the UI based on the total number of lines.

My particular example relies on the following data contracts to transmit the results:

[DataContract]
    public class ImportResult
    {
        [DataMember]
        public int SuccessCount { get; set; }
 
        [DataMember]
        public int FailCount { get; set; }
 
        private List<ImportException> _Exceptions = new List<ImportException>();
 
        [DataMember]
        public List<ImportException> Exceptions
        {
            get { return _Exceptions; }
            set { _Exceptions = value; }
        }
 
 
    }
    [DataContract]
    public class ImportException
    {
        [DataMember]
        public int LineNumber { get; set; }
 
        public Exception Exception { get; set; }
 
        [DataMember]
        public string ErrorMessage
        {
            get
            {
                return Exception.Message;
            }
            private set { }
        }
 
        public ImportException(int lineNumber, Exception ex)
        {
            this.LineNumber = lineNumber;
            this.Exception = ex;
        }
    }

An example of calling the service in Silverlight (using MVVM viewmodel) is as follows:

public void ImportUploadedFile(string serverToken)
        {
 
            ImportProgressText = string.Format("[{0}] Starting Import. (this may take a while)\r\n", DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"));
 
            //in part 4 we actually import the file and get the results of the import
            IsImporting = true;
 
            //Start the Example importer
            ServiceInerfaceClient svc = ServiceUtility.GetExampleClient();
            svc.BeginImportUploadedExampleFileCompleted += new EventHandler<AsyncCompletedEventArgs>(svc_BeginImportUploadedExampleFileCompleted);
            svc.BeginImportUploadedExampleFileAsync(serverToken, HasHeaderRow);
        }
 
        void svc_BeginImportUploadedExampleFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            ((ServiceInerfaceClient)sender).BeginImportUploadedExampleFileCompleted -= svc_BeginImportUploadedExampleFileCompleted;
 
            //check no errors launching import
            if (e.Error == null)
            {
                //now that the import has successfully started we can begin polling for the results
                PollForImportResults();
            }
            else
            {
                IsImporting = false;
                ShowBusy(false);
 
                if (e.Error != null)
                    ImportProgressText += string.Format("[{0}] ERROR: " + e.Error.Message, System.DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"));
                else
                    ImportProgressText += string.Format("[{0}] ERROR: Unknown Error Importing File", System.DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"));
            }
        }
 
        private void PollForImportResults()
        {
            //set up a client and the event handler
            ServiceInerfaceClient svc = ServiceUtility.GetExampleClient();
            svc.CheckImportUploadedExampleFileCompleteCompleted += new EventHandler<CheckImportUploadedExampleFileCompleteCompletedEventArgs>(svc_CheckImportUploadedExampleFileCompleteCompleted);
 
            //start the recursion on another thread (prevent freezing the UI)
            System.Threading.Thread pollThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(PollForImportResults_Recursion));
            pollThread.IsBackground = true;
            pollThread.Start(svc);
 
        }
 
        private void PollForImportResults_Recursion(object ServiceInerfaceClientInstance)
        {
            //wait 5 secs
            System.Threading.Thread.Sleep(5000);
 
            //call the service and wait for the poll result
            ServiceInerfaceClient svc = (ServiceInerfaceClient)ServiceInerfaceClientInstance;
            svc.CheckImportUploadedExampleFileCompleteAsync(SelectedFile.ServerToken);
        }
 
        void svc_CheckImportUploadedExampleFileCompleteCompleted(object sender, CheckImportUploadedExampleFileCompleteCompletedEventArgs e)
        {
            //make sure no errors reading polling service
            if (e.Error == null)
            {
                //update the rows imported counter
                //update the UI via the dispatcher thread
                Deployment.Current.Dispatcher.BeginInvoke(new Action<ExampleAdminViewModel>((vm) =>
                    {
                        vm.RowsProcessed = e.rowsImported;
                    }), this);
 
                //was the import complete?
                if (e.Result == true)
                {
                    //my job now done, remove the handler
                    ((ServiceInerfaceClient)sender).CheckImportUploadedExampleFileCompleteCompleted -= svc_CheckImportUploadedExampleFileCompleteCompleted;
 
                    //update the UI via the dispatcher thread
                    Deployment.Current.Dispatcher.BeginInvoke(new Action<ExampleAdminViewModel>((vm) =>
                        {
                            vm.ImportProgressText += string.Format("[{0}] Import Complete, retreiving results.\r\n", System.DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"));
                        }), this);
 
                    //finish the import to get the results
                    GetImportResults();
                }
                else
                {
                    //still not finished, continue with the recursion
                    PollForImportResults_Recursion((ServiceInerfaceClient)sender);
                }
            }
            else
            {
                //display errors using dispatcher (UI) thread
                Deployment.Current.Dispatcher.BeginInvoke(new Action<ExampleAdminViewModel, CheckImportUploadedExampleFileCompleteCompletedEventArgs>((vm, UIe) =>
                {
                    vm.IsImporting = false;
                    vm.ShowBusy(false);
 
                    if (UIe.Error != null)
                        vm.ImportProgressText += string.Format("[{0}] ERROR: " + UIe.Error.Message, System.DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"));
                    else
                        vm.ImportProgressText += string.Format("[{0}] ERROR: Unknown Error Importing File", System.DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"));
                }), this, e);
 
            }
        }
 
        private void GetImportResults()
        {
            ServiceInerfaceClient svc = ServiceUtility.GetExampleClient();
 
            svc.GetImportUploadedExampleFileResultsCompleted += new EventHandler<GetImportUploadedExampleFileResultsCompletedEventArgs>(svc_GetImportUploadedExampleFileResultsCompleted);
            svc.GetImportUploadedExampleFileResultsAsync(SelectedFile.ServerToken);
        }
 
        void svc_GetImportUploadedExampleFileResultsCompleted(object sender, GetImportUploadedExampleFileResultsCompletedEventArgs e)
        {
 
            if (e.Error == null && e.Result != null && e.Result.IsSuccess)
            {
                ImportResult res = e.Result.Item;
 
                //file has finished importing, calculate a string to represent the results
                string importResultsText = string.Format(@"[{0}] Results:
 
----------------------------------------
Successful rows: {1}
Failed rows: {2}
 
Failure breakdown:
---------------------------------------
 
",
                                System.DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"),
                                res.SuccessCount,
                                res.FailCount);
 
                foreach (ImportException iEx in res.Exceptions)
                {
                    importResultsText += string.Format("Line: {0} - {1}\r\n", iEx.LineNumber, iEx.ErrorMessage);
                }
 
                //display results using dispatcher (UI) thread
                Deployment.Current.Dispatcher.BeginInvoke(new Action<ExampleAdminViewModel>((vm) =>
                {
                    vm.ImportProgressText += importResultsText;
                    vm.IsImporting = false;
                    vm.ShowBusy(false);
                }), this);
            }
            else
            {
 
                //display errors using dispatcher (UI) thread
                Deployment.Current.Dispatcher.BeginInvoke(new Action<ExampleAdminViewModel, GetImportUploadedExampleFileResultsCompletedEventArgs>((vm, UIe) =>
                {
                    vm.IsImporting = false;
                    vm.ShowBusy(false);
 
                    if (UIe.Result != null)
                        vm.ImportProgressText += string.Format("[{0}] ERROR: " + UIe.Result.MessageKey, System.DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"));
                    else if (UIe.Error != null)
                        vm.ImportProgressText += string.Format("[{0}] ERROR: " + UIe.Error.Message, System.DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"));
                    else
                        vm.ImportProgressText += string.Format("[{0}] ERROR: Unknown Error Importing File", System.DateTime.Now.ToString("dd-MMM-yyyy hh:mm:ss"));
                }), this, e);
 
 
            }
 
        }

This is again using threads to perform the polling of the server so that calls to Thread.Sleep can be made on the polling thread without freezing the UI. I have used ‘BeginInvoke’ on the ‘Dispatcher’ thread when setting ViewModel properties, as this indirectly updates the UI (through the bindings) and so must take place on the main UI thread.


Chunked File Uploader in Silverlight and WCF

In order to upload a file from a Silverlight application to your webserver you generally provide a WCF service that accepts the filestream.

However, accepting a large file as a single chunk can sometimes lead to problems with service timeouts, request size restrictions etc. so I took the time to write a chunked file uploader service, which allows you to send the file in several parts. The way this works is similar to how a normal file stream works, in that you open a target file in exchange for a token/handle, write to the file using the token/handle and then close the file when you are finished. In addition to this, because we are dealing with async operations over HTTP we are required to ensure the file parts are committed in the correct order as they were intended when sent (i.e. sequencing the chunks).

Firstly, the interface for the IChunkedFileUploader is as follows:

[ServiceContract]
    public interface IChunkedFileUpload
    {
        [OperationContract]
        string OpenChunkedUploadFile();
 
        [OperationContract]
        Response WriteToChunkedUploadFile(string fileToken, int seq, byte[] data);
 
        [OperationContract]
        Response CloseChunkedUploadFile(string fileToken, int totalPartsSent);
    }

The ‘Response’ class simply wraps two properties { bool, string } for the success flag and any error message.

The implementation of this interface in my WCF layer looks as follows:

public class FileUploadService : IChunkedFileUpload
    {
        /* In order to allow the chunking of upload data in Silverlight we will write a light sequenced data wrapper */
        private class FilePart
        {
            public int Seq { get; set; }
            public byte[] Data { get; set; }
        }
        private static Dictionary<string, List<FilePart>> _FileParts = new Dictionary<string, List<FilePart>>();
 
        public string OpenChunkedUploadFile()
        {
            string fileToken = "";
 
 
            //incase of simultaneous calls, lock the static variable
            lock (_FileParts)
            {
                //find a unique name for the file
                while (fileToken == "" || _FileParts.ContainsKey(fileToken))
                {
                    fileToken = DateTime.Now.Ticks.ToString() + ".ccf";
                }
 
                //create the "file"
                _FileParts.Add(fileToken, new List<FilePart>());
            }
 
            //return the token (filename)
            return fileToken;
        }
 
        public Response CloseChunkedUploadFile(string fileToken, int totalPartsSent)
        {
 
            try
            {
 
            //check its a valid token, else its 'file not found'
            if(_FileParts.ContainsKey(fileToken))
            {
                //check the number of parts received matches the 'total parts' check passed in by client (to ensure all parts were received, including the final part)
                if (_FileParts[fileToken].Count == totalPartsSent)
                {
                    List<byte> fileData = new List<byte>();
 
                    //reconstruct the file data from the file parts
                    //ensuring the sequences are valid (no gaps, duplicates etc.)
                    for (int i = 0; i <= _FileParts[fileToken].Max(p=>p.Seq); i++)
                    {
                        IEnumerable<FilePart> seqPart = _FileParts[fileToken].Where(p => p.Seq == i);
 
                        //should be 1 match
                        if (seqPart.Count() == 1)
                            fileData.AddRange(seqPart.First().Data);
                        else
                            throw new System.IO.InvalidDataException("Data was invalid, not all sequences were present or were duplicated");
                    }
 
                    //if we reach this point then the total number of parts was correct and the sequences were correctly sent/received
                    //we can now write the bytes to disk
                    string fullFilePath = HttpContext.Current.Server.MapPath("UploadFiles\\" + fileToken);
                    System.IO.File.WriteAllBytes(fullFilePath, fileData.ToArray());
 
                    //and now kill the version in memory
                    _FileParts[fileToken].Clear();
                    _FileParts.Remove(fileToken);
 
                    //success!
                    return new Response() { IsSuccess = true };
                }
                else
                {
                    throw new System.IO.InvalidDataException("Data was invalid, total parts check failed.");
                }
            }
            else{
                throw new System.IO.FileNotFoundException();
            }
            }
            catch (Exception ex)
            {
                return new Response<string>() { IsSuccess = false, MessageKey = ex.Message };
            }
        }
 
        public Response WriteToChunkedUploadFile(string fileToken, int seq, byte[] data)
        {
            try
            {
                //check its a valid token, else its 'file not found'
                if (_FileParts.ContainsKey(fileToken))
                {
                    _FileParts[fileToken].Add(new FilePart() { Data = data, Seq = seq });
                    return new Response() { IsSuccess = true };
                }
                else
                {
                    throw new System.IO.FileNotFoundException();
                }
            }
            catch (Exception ex)
            {
                return new Response() { IsSuccess = false, MessageKey = ex.Message };
            }
        }
 
    }

Basically, what this does is creates a shared ‘File system’ on the server which is backed by a Dictionary<string, List<FilePart>> – the token/handle and the file parts received.

In Silverlight, or any other client, you call ‘OpenChunkedUploadFile’ to receive a unique token which you will use to send your file parts. This initialises a new item in the dictionary of files. The token will also be used as the final filename, so that you can calculate the remote URL of the eventual file.

You then loop through your target file bytes, sending chunks of your desired size to the ‘WriteToChunkedUploadFile’ function. You mark each file part with the sequence number to ensure the file is rebuilt in the correct order. At this point all the service is doing is adding to the List for the current file in the Dictionary.

When all of your bytes are sent, you call ‘CloseChunkedUploadFile’ passing your token and the count of total file parts (to ensure all parts were received). After performing some checks on the file parts (e.g. number of parts, sequence contiguity) the file parts are arranged in sequence order to create the full file bytes of the file, which is then committed to the physical disk on the server (using a pre-defined folder and the token file name).

I have written an upload helper class which shows this:

public class ChunkedFileUploadHelper
    {
        #region "Fields"
        private int _FileChunkSize;
        private int _fileUploadSequence = 0;
        private int _fileUploadPartSuccessCount = 0;
 
        private ChunkedUploadFileInfo _fileInfo;
        #endregion
 
        #region "Events"
        public delegate void UploadAsyncCompletedEventHandler(ChunkedFileUploadHelper sender, string serverFileToken);
        public event UploadAsyncCompletedEventHandler UploadAsyncCompleted;
        protected void OnUploadAsyncCompleted()
        {
            if (UploadAsyncCompleted != null)
                UploadAsyncCompleted(this, _fileInfo.ServerToken);
        }
 
        #endregion
 
        /// <summary>
        /// Create an instance of the helper to be used with the provided file data instance
        /// </summary>
        /// <param name="fileInfo">The instance of chunked file data to be used</param>
        public ChunkedFileUploadHelper(ChunkedUploadFileInfo fileInfo) : this(fileInfo, 512000) //default chunk size 512KB
        { }
 
        /// <summary>
        /// Create an instance of the helper to be used with the provided file data instance, specifying a custom chunk size
        /// </summary>
        /// <param name="fileInfo">The instance of chunked file data to be used</param>
        /// <param name="chunkSize">Custom chunk size, default is 512KB</param>
        public ChunkedFileUploadHelper(ChunkedUploadFileInfo fileInfo, int chunkSize)
        {
            if (fileInfo == null)
                throw new ArgumentNullException("fileInfo", "fileInfo cannot be NULL");
 
            _fileInfo = fileInfo;
            _FileChunkSize = chunkSize;
        }
 
        /// <summary>
        /// Asynchronously uploads a file using the chunked service and calls the callback passing in the server file token
        /// </summary>
        public void UploadAsync()
        {
            if (_fileInfo != null && _fileInfo.FileData.Length > 0)
            {
                //reset the file upload data
                _fileUploadSequence = 0;
                _fileUploadPartSuccessCount = 0;
                _fileInfo.BytesUploaded = 0;
 
                //begin with making the initial service call
                ServiceInerfaceClient svc = ServiceUtility.GetChunkedFileUploadClient();
 
                //first thing to do is to create a new empty file on the server
                svc.OpenChunkedUploadFileCompleted += new EventHandler<OpenChunkedUploadFileCompletedEventArgs>(svc_OpenChunkedUploadFileCompleted);
                svc.OpenChunkedUploadFileAsync();
            }
            else
            {
                throw new InvalidOperationException("Cannot start upload as there was no file data to upload");
            }
        }
 
        void svc_OpenChunkedUploadFileCompleted(object sender, OpenChunkedUploadFileCompletedEventArgs e)
        {
            ((ServiceInerfaceClient)sender).OpenChunkedUploadFileCompleted -= svc_OpenChunkedUploadFileCompleted;
 
            if (e.Error == null && !string.IsNullOrEmpty(e.Result))
            {
                //we have been given a filename/token
                _fileInfo.ServerToken = e.Result;
 
                //now can move on to part 2
                Upload_part2();
            }
            else
            {
                if (e.Error != null)
                    throw e.Error;
                else
                    throw new Exception("Did not receive valid token from the server");
            }
        }
 
        private void Upload_part2()
        {
            //create a service client and add the handle for the part uploaded event
            ServiceInerfaceClient svc = ServiceUtility.GetChunkedFileUploadClient();
            svc.WriteToChunkedUploadFileCompleted += new EventHandler<WriteToChunkedUploadFileCompletedEventArgs>(svc_WriteToChunkedUploadFileCompleted);
 
            //start the recursion
            Upload_part2_recursion(svc);
        }
 
 
 
        private void Upload_part2_recursion(ServiceInerfaceClient svc)
        {
            //now that we have a file name we can send chunks of data
            byte[] bytesToSend = _fileInfo.FileData.Skip(_fileUploadSequence * _FileChunkSize).Take(_FileChunkSize).ToArray();
 
            if (bytesToSend.Length > 0)
            {
                svc.WriteToChunkedUploadFileAsync(_fileInfo.ServerToken, _fileUploadSequence, bytesToSend, bytesToSend.Length);
                _fileUploadSequence++;
            }
        }
 
        void svc_WriteToChunkedUploadFileCompleted(object sender, WriteToChunkedUploadFileCompletedEventArgs e)
        {
            if (e.Error == null && e.Result != null && e.Result.IsSuccess)
            {
                //bytes were successfully uploaded
                _fileInfo.BytesUploaded += (int)e.UserState;
                _fileUploadPartSuccessCount++;
 
                //were all bytes accounted for and successfully received?
                if (_fileInfo.BytesUploaded == _fileInfo.FileData.Length && _fileUploadPartSuccessCount == _fileUploadSequence)
                {
                    //we can now remve the handler
                    ((ServiceInerfaceClient)sender).WriteToChunkedUploadFileCompleted -= svc_WriteToChunkedUploadFileCompleted;
 
                    //file is completely upload, we can now go to step 3
                    Upload_part3();
                }
                else
                {
                    //keep sending the parts 
                    Upload_part2_recursion(((ServiceInerfaceClient)sender));
                }
 
            }
            else
            {
                if (e.Error != null)
                    throw e.Error;
                else if (e.Result != null)
                    throw new Exception(e.Result.MessageKey);
                else
                    throw new Exception("Unknown Error Uploading File to Server");
            }
        }
 
        private void Upload_part3()
        {
            ServiceInerfaceClient svc = ServiceUtility.GetChunkedFileUploadClient();
 
            //now we want to tell the server to close the file and commit to disk
            svc.CloseChunkedUploadFileCompleted += new EventHandler<CloseChunkedUploadFileCompletedEventArgs>(svc_CloseChunkedUploadFileCompleted);
            svc.CloseChunkedUploadFileAsync(_fileInfo.ServerToken, _fileUploadSequence);
        }
 
        void svc_CloseChunkedUploadFileCompleted(object sender, CloseChunkedUploadFileCompletedEventArgs e)
        {
            if (e.Error == null && e.Result != null && e.Result.IsSuccess)
            {
                //the file is now on the server
                OnUploadAsyncCompleted();
            }
            else
            {
                if (e.Error != null)
                    throw e.Error;
                else if (e.Result != null)
                    throw new Exception(e.Result.MessageKey);
                else
                    throw new Exception("Unknown Error Committing File to Server");
            }
        }
 
    }

The ‘ChunkedFileUploadInfo’ class is as follows:

public class ChunkedUploadFileInfo : INotifyPropertyChanged
    {
        #region "Client Side File Info"
        private string _OriginalFilename;
        public string OriginalFilename
        {
            get { return _OriginalFilename; }
            set { _OriginalFilename = value; OnPropertyChanged("OriginalFilename"); }
        }
 
        private byte[] _fileData;
        public byte[] FileData
        {
            get { return _fileData; }
            set { _fileData = value; OnPropertyChanged("FileData"); }
        }
        #endregion
 
        #region "Upload FIle Info"
        private string _ServerToken;
        public string ServerToken
        {
            get { return _ServerToken; }
            set { _ServerToken = value; OnPropertyChanged("ServerToken"); }
        }
 
        private int _bytesUploaded;
        public int BytesUploaded
        {
            get { return _bytesUploaded; }
            set { _bytesUploaded = value; OnPropertyChanged("BytesUploaded"); }
        }
        #endregion
 
        #region "Notify Property Changed"
        public event PropertyChangedEventHandler PropertyChanged;
 
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }

Fixing Dodgy Characters in SQL

When dealing with Unicode/nvarchar data in SQL, sometimes some unwanted characters can sneak into the database which may break things, or look wrong if you’re not expecting them.

One example of this is if you use an XML serializer on data containing character 0x0B, it will throw an exception.

To find and replace this data, you need to use the BINARY collation as SQL will not find any characters outside of the normal set using the normal collations.

Example of a replace 0x0B:

UPDATE
MyTable
SET
MyField = REPLACE(MyField COLLATE 	
Latin1_General_BIN, NCHAR(11), '')

Creating a Multi-Select Drop Down List in Silverlight

This is quite a common task/requirement, to have a drop down/combo interface with checkable options inside.

Silverlight is quite good at this due to its databound approach to displaying and manipulating data.

As a preface, lets assume you have wrapped your IEnumerable data in a wrapper class that contains the data item and a ‘Selected’ property, for example:

public class SelectableObject : ComponentModel.INotifyPropertyChanged
{
 
    private bool _selected;
    public bool Selected
    {
        get { return _selected; }
        set
        {
            _selected = value;
 
            OnPropertyChanged("Selected");
        }
    }
 
    public object DataItem { get; set; }
 
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Now that you have a datasource which has the properties needed to bind a selectable list you can display the data in a modified combo box which is designed to work with this kind of data. I based my combo box on the RadComboBox (prism version), but this can likely be easily translated to any other base implementation:

XAML:

<UserControl x:Class="FQNS.MultiSelectDropDown"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
             xmlns:my="clr-namespace:ExternalControls.TelerikForPrism;assembly=ExternalControls.TelerikForPrism"
    d:DesignHeight="300" d:DesignWidth="400">
 
    <Grid x:Name="LayoutRoot">
        <my:PrismRadComboBox ItemsSource="{Binding}"  Name="cmbComboBox" EmptyText="-All-" Margin="0,0,0,2" SelectionChanged="cmbComboBox_SelectionChanged" DropDownClosed="cmbComboBox_DropDownClosed">
            <!-- Data Template is defined in code behind-->
        </my:PrismRadComboBox>
    </Grid>
</UserControl>

Code-behind:

public partial class MultiSelectDropDown : UserControl
{
    public MultiSelectDropDown()
    {
        InitializeComponent();
 
    }
 
    private string _displayMemberPath;
    public string DisplayMemberPath
    {
        get { return _displayMemberPath; }
        set
        {
            _displayMemberPath = value;
            cmbComboBox.ItemTemplate = (DataTemplate)XamlReader.Load(@"
            <DataTemplate xmlns=""http://schemas.microsoft.com/client/2007"">
                <StackPanel Orientation=""Horizontal"">
                    <CheckBox IsChecked=""{Binding Selected, Mode=TwoWay}""></CheckBox>
                    <TextBlock Text=""{Binding Path=" + DisplayMemberPath + @"}""></TextBlock>
                </StackPanel>
            </DataTemplate>");
        }
    }
 
    private void cmbComboBox_SelectionChanged(object sender, Telerik.Windows.Controls.SelectionChangedEventArgs e)
    {
        //we actually dont want 'selections' to be made, so always select -1 but tick the selected item
        SetItemSelectedProperty(cmbComboBox.SelectedItem, true);
 
        cmbComboBox.SelectedIndex = -1;
    }
 
    private void SetComboText()
    {
        //at this point the data context should be at least IEnumerable
        IEnumerable objectList = DataContext as IEnumerable;
        if (objectList != null)
        {
            List<object> selectedItems = (from object o in objectList where GetItemSelectedProperty(o) == true select o).ToList();
            switch (selectedItems.Count)
            {
                case 0:
                    cmbComboBox.EmptyText = "-All-";
                    break;
                case 1:
                    cmbComboBox.EmptyText = GetItemDisplayProperty(selectedItems[0]);
                    break;
                default:
                    cmbComboBox.EmptyText = "Multiple Selections..";
                    break;
            }
 
        }
    }
 
 
    private void SetItemSelectedProperty(object dataItem, bool value)
    {
        if (dataItem != null)
        {
            //get the 'Selected' property and confirm its a correct type
            PropertyInfo selectedProp = dataItem.GetType().GetProperty("Selected");
            if (selectedProp != null && selectedProp.PropertyType == value.GetType())
            {
                selectedProp.SetValue(dataItem, value, null);
            }
        }
    }
 
    private bool GetItemSelectedProperty(object dataItem)
    {
        if (dataItem != null)
        {
            //get the 'Selected' property and confirm its a boolean type
            PropertyInfo selectedProp = dataItem.GetType().GetProperty("Selected");
            if (selectedProp != null && selectedProp.PropertyType == typeof(bool))
            {
                return (bool)selectedProp.GetValue(dataItem, null);
            }
        }
 
        //default to not selected
        return false;
    }
 
    private string GetItemDisplayProperty(object dataItem)
    {
        if (dataItem == null)
            throw new ArgumentNullException("dataItem", "Data Item cannot be NULL");
 
        //get the 'DisplayMemberPath' property
        if (DisplayMemberPath.Contains("."))
        {
            //child property - iterate the tree
            Type currentType = dataItem.GetType();
            object currentValue = dataItem;
 
            string[] props = DisplayMemberPath.Split('.');
            foreach (string p in props)
            {
                PropertyInfo thisProp = currentType.GetProperty(p);
                if (thisProp != null)
                {
                    currentType = thisProp.PropertyType;
                    currentValue = thisProp.GetValue(currentValue, null);
                }
                else
                {
                    break;
                }
            }
 
            return (currentValue != null ? currentValue.ToString() : dataItem.ToString());
        }
        else
        {
            //direct property
            PropertyInfo displayProp = dataItem.GetType().GetProperty(DisplayMemberPath);
 
            if (displayProp != null)
            {
                object propVal = displayProp.GetValue(dataItem, null);
                if (propVal != null)
                    return propVal.ToString();
            }
        }
        //default to return the obejct itself
        return dataItem.ToString();
    }
 
 
    private void cmbComboBox_DropDownClosed(object sender, EventArgs e)
    {
            SetComboText();
    }
 
}

Example Usage:

<my:MultiSelectDropDown DataContext="{Binding MySelectableDataSource, Mode=TwoWay}" DisplayMemberPath="DataItem.Name"></my:MultiSelectDropDown>

Dynamic OrderBy using LINQ to SQL

Following on from my last post on Virtual Paging with Silverlight/WCF Services, the next topic to go hand in hand with this is dynamically ordering your data when using LINQ to SQL as a backend.

Ordinarily I use my own data architecture, so haven’t run into this problem of using LINQ to SQL and trying to pass your order by clause from the UI before; but recently I was working on a project which does use LINQ to SQL (actually its Entity Framework but I guess these things fit together) so this came up, after having accomplished proper server side paging.

When you are paging data you want to page it based on a certain order. The order of the data is seldom hard coded, you want the user to define at runtime how the data should be ordered, e.g. by selecting the column in a grid view. In LINQ to SQL this isn’t as simple as it should be, unless you download the ‘Dynamic LINQ Library’ that Microsoft have published in some of their samples.

Not one for including code libraries unnecessarily I decided to tackle it with a little extra Googling and understanding.

What I came across was basically the use of LINQ Expressions in conjunction with reflection to dynamically build the parameters to the LINQ order by clause at runtime.

I only needed this to be very simple, i.e. one column at a time. I therefore defined a simple string which would represent a ‘sort expression’ of one column and one direction.

Here is the code to my solution:

private IQueryable<YouDataObject> SearchYourDataQuery(
EFDBConnection conn, 
//some search criteria here,
string orderBy)
{
 
//default sort order is start date asc
if (string.IsNullOrEmpty(orderBy)) orderBy = "StartDate";
orderBy=orderBy.Trim();
 
//parse the order by statement 
string sortExprRegex = @"^(?<orderBy>.+?)\s?(?<direction>ASC|DESC)?$";
string sortDirection = "ASC";
 
if (Regex.IsMatch(orderBy, sortExprRegex, RegexOptions.IgnoreCase))
{
    Match orderByParts = Regex.Match(orderBy, sortExprRegex, RegexOptions.IgnoreCase);
    orderBy = orderByParts.Groups["orderBy"].Value;
    if(!string.IsNullOrEmpty(orderByParts.Groups["direction"].Value)) sortDirection = orderByParts.Groups["direction"].Value;
}          
 
//generate the query to get the filtered records
IQueryable<YouDataObject> dataQry = (from a in conn.YourDatas
                                            //put your joins, where clauses etc
                                            select a);
 
    //build an order by statement based on the column name passed to us
ParameterExpression tableParam = Expression.Parameter(typeof(YouDataObject), "d");
 
//awlays starting property access from the top table
Expression expr = tableParam;
 
//if it has dots then its a child property, i.e of another type
if (orderBy.Contains("."))
{
    string[] props = orderBy.Split('.');
 
    Type type = typeof(YouDataObject);
    foreach (string prop in props)
    {
        // use reflection (not ComponentModel) to mirror LINQ
        PropertyInfo pi = type.GetProperty(prop);
        expr = Expression.Property(expr, pi);
        type = pi.PropertyType;
    }
 
}
else
{
    //no dots means a direct property accessor of the top level table
    expr = Expression.Property(tableParam, orderBy);
}
 
//create a test of data type, but default is to use 'object'
if (expr.Type == typeof(DateTime))
{
    if(sortDirection.ToUpper() == "ASC")
        dataQry = compQry.OrderBy(Expression.Lambda<Func<YouDataObject, DateTime>>(expr, tableParam));
    else
        dataQry = compQry.OrderByDescending(Expression.Lambda<Func<YouDataObject, DateTime>>(expr, tableParam));
}
else
{
    if (sortDirection.ToUpper() == "ASC")
        dataQry = compQry.OrderBy(Expression.Lambda<Func<YouDataObject, object>>(expr, tableParam));
    else
        dataQry = compQry.OrderByDescending(Expression.Lambda<Func<YouDataObject, object>>(expr, tableParam));
}
 
//return the resulting query
return dataQry;
 
}

There are probably ways to neaten up the code and make it more reusable, but I think as a starting point that is hopefully helpful.


CraigWardman.com
Car Leasing | Lease Cars