PhotoMemory WP7 App

The Basic Outline of Functionality

  1. Choose the difficulty mode to play in.
  2. Clear default picture values used in development.
  3. Set the board up based on the difficulty mode.
  4. Get personal photos from the phone.
  5. Shuffle photos and select appropriate number based on difficulty mode.
  6. Create pairs and shuffle all cards/photos.
  7. Display unturned cards and any already matched photos.
  8. Choose card and determine matches.
  9. Track number of moves and time.
  10. Display winner once all cards are matched.

PhotoMemory WP7 App

The Game Board (XAML)

The game board is basically a grid with several cells to hold the gradient cards or overturned photos. At the bottom, I keep track of number of moves and time expired.

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <toolkit:WrapPanel Grid.Row="0" x:Name="LayoutPanel" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <Image Source="Images/1.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/2.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/3.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/6.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/4.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/1.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/2.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/7.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/8.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/5.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/9.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/3.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/4.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/6.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/7.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/8.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/9.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/5.jpg" Width="150" Margin="5"></Image>
        </toolkit:WrapPanel>

        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <TextBlock Text="Moves:"></TextBlock>
            <TextBlock x:Name="MovesLabel" Foreground="Yellow" Text="0" Margin="10,0,0,0"></TextBlock>
            <TextBlock Text="Time:" Margin="50,0,0,0"></TextBlock>
            <TextBlock x:Name="TimeLabel" Foreground="Yellow" Text="0 seconds" Margin="10,0,0,0"></TextBlock>
        </StackPanel>
    </Grid>

PhotoMemory WP7 App

Highlighting a Few Areas of Development

Get Photos

A large part of the charm in this simple application is that it uses your own photos as the cards to match. I had pictures of my daughter and some wildlife we had taken while on a hiking trip (as well as some stock images that came with Windows 7) in my phone when I developed this app. Below is code to retrieve the photos from your phone – stored in MediaLibrary.

private List GetPhotos()
        {
            //Get photos from phone
            List photos = new List();
            var library = new MediaLibrary();
            var pics = library.Pictures.ToArray();

            //Shuffle user pics
            pics.Shuffle();

            //Loop through photos and add to game collection
            foreach (var pic in pics)
            {
                BitmapImage bmp = new BitmapImage();
                bmp.SetSource(pic.GetThumbnail());
                photos.Add(bmp);
            }

            //Check if enough photos are available
            if (photos.Count < _tileCount / 2)
            {
                //Setting default images if user does not have enough pictures of their own
                photos.Add(new BitmapImage(new Uri("Images/1.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/2.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/3.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/4.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/5.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/6.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/7.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/8.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/9.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/10.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/11.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/12.jpg", UriKind.Relative)));

                //Shuffle newly added photos as well
                photos.Shuffle();
            }
            
            return photos;
        }

Display Cards

This is the initial generation of the cards (gradients) and their corresponding actions once a user clicks on them.

private void DisplayCards()
        {
            foreach (var card in _collection.Cards)
            {
                var rect = new System.Windows.Shapes.Rectangle()
                {
                    Fill = new LinearGradientBrush(new GradientStopCollection()
                        {
                            new GradientStop()
                            {
                                Color = "#FF003366".ToColor(),
                                Offset = 0                                
                            },
                            new GradientStop()
                            {
                                Color = "#FF208FFF".ToColor(),
                                Offset = 1
                            },
                        }, 45
                    )
                    {
                        MappingMode = BrushMappingMode.RelativeToBoundingBox
                    },
                    Height = _cardHeight,
                    Margin = new Thickness(_margin),
                    Name = string.Format("rect_{0}", card.ID),
                    Stroke = new SolidColorBrush(Colors.White),
                    StrokeThickness = 2,
                    Width = _cardWidth,
                    Visibility = System.Windows.Visibility.Visible
                };

                rect.MouseLeftButtonUp += new MouseButtonEventHandler(ChooseCard);
                LayoutPanel.Children.Add(rect);

                var img = new Image()
                {
                    Height = _cardHeight,
                    HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
                    Margin = new Thickness(_margin),
                    Name = string.Format("card_{0}", card.ID),
                    Source = card.Bitmap,
                    Width = _cardWidth,
                    VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                    Visibility = System.Windows.Visibility.Collapsed
                };
                
                LayoutPanel.Children.Add(img);
            }
        }

Play a SoundEffect

This code snippet is all over the place on the internet and WP7 forums, but I thought I would include it here for completeness. In PhotoMemory, I play sound effects both in match and non-match scenarios and at the end when all cards are matched for a fun little “triumph”.

//Play failure sound
                            var snd = SoundEffect.FromStream(TitleContainer.OpenStream("Sounds/failed.wav"));
                            snd.Play();

Navigate to Another Page

Again, another simple snippet but this simply shows a way to navigate to another page within WP7 Silverlight.

this.NavigationService.Navigate(new Uri("/Game.xaml?mode=" + mode, UriKind.Relative));

Create and Use a DispatchTimer

Timers are very important in game/mobile development and using a DispatchTimer appears to be the simplest way to do it. Here I create a timer, set some variables such as the duration and what eventhandler to call, and then start the timer. After the timer has expired, the event is raised and I reset the flipped cards.

DispatcherTimer _cardTimer;

//Need to pause for 1 second before flipping back
                            _cardTimer = new DispatcherTimer();
                            _cardTimer.Interval = TimeSpan.FromSeconds(1);
                            _cardTimer.Tick += new EventHandler(FinishStudyingCards);
                            _cardTimer.Start();

void FinishStudyingCards(object sender, EventArgs e)
        {
            //Stop timer
            _cardTimer.Stop();

            //Flip cards back down after finished viewing
            FlipCard(_firstCard, FlipDirection.Down);
            FlipCard(_secondCard, FlipDirection.Down);

            //Allow user to try again
            _firstCard = null;
            _secondCard = null;
            _lock = false;
        }

PhotoMemory WP7 App

Submission

I would like to have a link to the application on Zune, however, because of complications with Microsoft my submission is still pending after more than a month. You can bet I will be writing a post on the submission process as well. Good luck, and have fun developing WP7 apps of your own!

Not All Roses With Azure

Azure

As many of you who occasionally read my blog know, I tend to be an early adopter of most Microsoft technologies. Let me rephrase that, I am an early explorer of most Microsoft technologies; adoption into production generally comes a little later. However, I did try out Azure two months ago with a sandbox web service hosted in the cloud. I hadn’t thought much about it until recently when I received a bill for around eighty dollars. I am an MSDN subscriber, so I figured this must be a mistake (MSDN subscribers are free for 18 months based on limited usage). After discussing the situation with Microsoft I found out the charge was for a second instance I unknowingly had. As it turns out, maintaining a staging and production environment in Azure is actually listed as two instances, even though only one is “production”. I thought staging was internal and therefore not counted as a paid instance, in fact, at the time I was deploying the web service it suggested adding a second instance to maintain the true service level agreement of max up time. This led me to believe I was only running one true instance, and would be therefore covered under the MSDN subscription.

I was wrong, and because it does clearly state it several lines into the lengthy Terms and Conditions document which you agree to by setting up Azure, I had no leg to stand on in refuting the charge. I will admit, I was pretty irritated that as I sat in front of Visual Studio 2010, calling on my WP7, and only moments before spreading Microsoft praises to fellow employees, that there was no understanding or attempt to reconcile… only the cold abruptness of a Microsoft employee telling me how I “clearly signed in full knowledge and agreement”. Formally, the representative filed a request for reimbursement which was promptly denied.

Hopefully, as you excitedly dive into new technology you do so with caution. In Microsoft’s case, I still believe there are no better development tools, no better programming languages, and no worse pricing structures and convolution. I’m hope by reading this, you will at least avoid making the same mistake I did and save yourself a little cash in the process.

My Windows Phone 7 Christmas Story

Windows Phone 7

Rushing To Check Under the Tree

Finally the day had come; a neat little package arrived at my wife’s office containing my new Samsung Focus WP7. My wife had been away in Chicago for a few snowy days and after missing three flights due to bad weather she finally arrived home. I said hello, hugged her tightly, and promptly asked for her office keys so I could speed over to her office at 8:30PM to leave her and get my phone. Thankfully, she was pretty accepting of this.

I pulled into the parking lot, slightly bumping the parking spot cement block and rushed inside. The weather was cold, Christmas lights decorated nearby buildings, and this was my Geek Christmas. The drive home – a tedious fifteen minutes – was clearly too long to wait, so I opened the little brown package on the spot, swapped out my current SIM card and started it up. The dark room glowed green with the crystal clear picture of the default lock screen. With a graceful flick up of a finger, the endless possibilities of mobile development officially began…

Playing With My New Toy

So a couple of things about the Windows Phone 7; first, setup was so painless I barely remember it. I entered my Windows Live ID and password and all of a sudden my phone just started coming alive. Images, calendars, and contacts that I didn’t even know I had wired up to Windows Live started appearing.

I installed Netflix, a wireless connection, my work Exchange account, and Yelp. At this point, I would be pretty happy with the phone just as is, but of course, I had to dive a bit deeper. I synced with Zune and pulled down Music, Pictures, and Podcasts. As a side note, I really do find the auto-copy to SkyDrive every time you take a picture from the phone a very smart feature.

Of course, not everything was rosy, I did take a silly picture that was immediately placed into my background for the picture hub which I could not change even when deleting the picture from the phone. It turns out that holding the touch while on the background of the picture hub is how to do this and it must make a separate copy not bound to the original picture. It seems trivial at the time, but I can see how discovery of when to use long touches may be a slight usability issue when developing applications.

All and all, I am very excited about the phone and have been showing it off to many of my dev-buddies. The real excitement comes in the next few posts where I will be discussing the creation and deployment of my first WP7 application.

Stay tuned.

WP7 U.S. Launch

Windows Phone 7

Monday, November 8th, 2010 came and went, the release of Windows Phone 7 has occurred. I’m sure many of you piled into a car at lunch time with your work buddies, drove down to the phone store, fought for a parking space, pushed through the throngs of people pounding at the doors trying to get a mere glimpse of the glory that is Windows Phone 7, let alone purchase one… Right, the events didn’t quite unfold that way for me either on the big release date of WP7.

The first part of that recount is true however, we did pile into a car at lunch time and drive down to the local phone store, but instead of thongs of people, we were greeted with two cardboard signs for iPhone 4 and the iPad. After milling about for almost five minutes we finally asked someone if they had a display for WP7, which we were directed to and left without much ado. I had the odd feeling we were the only four people in Naples which had any insight to this historic day when Microsoft finally does mobile right. The Samsung Focus didn’t disappoint with its’ sleek display, lightweight frame, and zippy feel, but the fanfare was lacking even from the experts at the store. There were no other phones on display to try out unfortunately.

I checked with a store rep to ask if they had any in stock and he announced they had sold out. Somewhat good news I thought to myself, but then learned that they had only received a single phone which was sold earlier that morning. I don’t know if that quite counts as “selling out” their initial launch, at least in my neck of the woods. Regardless of the lackluster personal experience during the WP7 launch, I do predict the following: Microsoft WP7 will share a similar release as Xbox originally did when Playstation 2 was deeply entrenched in the market. I believe WP7 will slowly gain adoption in the mainstream and eventually overtake Apple (and other phone OS’s) by enabling developers and phone manufacturers to create purposeful, consistent, quality products – both apps and physical devices.

Hopefully, I’ll have a phone in my hands to help spread the word soon!

P.S. – Blog postings on the two apps I have created coming as soon as I’ve tested on a real phone and deployed to the marketplace, stay tuned!

Testing Web Services with SoapUI

SoapUI

Web services are great aren’t they? You can easily link Java code, .NET code, and even many more obscure languages together with some type of XML-based web service. However, working with all of these different sources of code can be a little concerning as bug tracking and testing is concerned. Often times, when a change occurs it could impact several areas, which is why it’s great to have a tool to test just the web service (or a collection of web services) and experiment with different values passed to the service quickly.

I’m sure there are several options out there but I’ll share the open source one I’ve been using lately, SoapUI. Here are a few steps to get you started:

Step 1 – Download and Install

Head over to SourceForge and download SoapUI community edition. (I think I recently read that SourceForge owns ThinkGeek – if you haven’t been to ThinkGeek you’re not a true geek!)

Step 2 – Create a New SoapUI Project

SoapUI New Project

Enter the project name to whatever you like (CalculatorWebservice, for example).

Enter the WSDL location of the web service; this can either be local or a remote address. If you would like to follow along you can use the following public web service URL: http://soatest.parasoft.com/calculator.wsdl.

By default “Create sample requests for all operations” should be checked, this creates a stub request which you can modify to plug in your own values later on.

Step 3 – Open Stubbed Request

SoapUI Navigation

Use the navigation tree on the left side of the window to open your new project you’ve created, this should read “CalculatorWebservice”. Choose an operation to expand (we’ll use “Add”) until you expose “Request 1”. This is the stubbed request that you will alter to begin testing the web service. Double click “Request 1” to open the XML request in a new window.

Step 4 – Modify and Send Request

SoapUI Request

The nodes will originally display a “?”, expecting you to enter a value for the nodes. Enter all values that you would like to pass into the web service. If you are following along with the example, you can enter 1 for x and 4 for y. Click the green run button in the top left to send the web service request.

Step 5 – Evaluate Response

SoapUI Response

The split screen will now favor the response window which should have the XML response returned from the web service (if you passed in bad data or if the web service failed, you should see the failure related response as well).

You now have a quick and easy way to load up multiple web services, adjust the input values and test the responses before complicating the situation with your own code. Hope this helps!

VS2010 Schema Compare Order of Operations

Take the formulas 1 + 2 x 3 and (1 + 2) x 3; two completely different answers. In the case of Visual Studio Schema Compare, what I would assume the order of operations to be for updating a database would be to first update tables, then updates views (which could be dependent on the tables), and finally update stored procedures (which could be dependent on either the tables or the views). It seems that Schema Compare attempts to do stored procedures first, and when it comes across a reference to a database object that does not yet exist, it dies out with the following error message.

Order of Operations

In this example, we have two tables which have been created and the corresponding Create and Update stored procedures which interact with the new tables. This is a pretty basic example of typical database schema updates and one I would think Schema Compare could figure out.

The current workaround I have found for this is to:

  1. Mark all Stored Procedures as Skip All.
  2. Run Write Updates.
  3. Refresh the compare.
  4. Mark all stored procedures as Create/Update All.
  5. Run Write Updates.

A few extra steps, but it seems to work every time. Hopefully this is something that Microsoft will correct or provide some information as to why the order of operations proceeds with stored procedures first.

VS2010 Schema Compare Crashing

VS2010 Schema Compare Crash

I’ve been using Visual Studio 2010 Schema Compare for deploying database changes from one environment to another (development to staging, staging to production), but I have ran into an annoying bug that I would love some enlightenment on.

I am trying to compare a database on our staging SQL server to our production server and after selecting the two databases and clicking “Compare”, Visual Studio immediately crashes.

Problem
signature:

Problem Event Name: CLR20r3

Problem Signature 01: devenv.exe

Problem Signature 02: 10.0.30319.1

Problem Signature 03: 4ba1fab3

Problem Signature 04: Microsoft.Data.Schema.Sql

Problem Signature 05: 10.0.0.0

Problem Signature 06: 4ba1d812

Problem Signature 07: 2f6c

Problem Signature 08: 24

Problem Signature 09: System.NullReferenceException

OS Version: 6.1.7600.2.0.0.256.48

Locale ID: 1033

 

Additional
information about the problem:

LCID: 1033

 

Read
our privacy statement online:

http://go.microsoft.com/fwlink/?linkid=104288&clcid=0x0409

 

If the
online privacy statement is not available, please read our privacy statement
offline:

C:\Windows\system32\en-US\erofflps.txt

What’s odd about this particular problem, is that other databases on the staging server can be compared to their production counterparts without problem. Additionally, this particular database was able to be backed up and restored to our development environment and a schema compare could be ran between development and production.

If anyone has any tips on this, please leave a comment explaining. If you’re having this issue, please note that as a workaround, it seems to be working to backup and restore the database. I also plan on posting this to Microsoft as well and I will update the post if I find anything.

Qxtend Query Service, .NET, and Dexter

Work, a new house, young daughter, and watching Dexter from the beginning doesn’t leave a lot of time for writing a software blog. Fortunately, tonight I finished the last episode (terrible ending) and it’s time to get back to writing!

To catch you up to the current season in my storyline; I’ve been working in the enterprise world, the land of three letter acronyms (TLA). I recently asked a few of my co-workers to help me come up with a short list to capture the type of work we’ve been doing, here’s what we came up with:

  • ERP – Enterprise Resource Planning
  • MES – Manufacturing Execution System
  • MRP – Material Requirements Planning
  • CRM – Customer Relationship Management
  • EAM – Enterprise Asset Management
  • PLM – Product Lifecycle Management
  • DHR – Device History Record
  • DHF – Device History File
  • MDR – Master Device Record
  • NCR – Non-Conformance Record
  • ECO – Electronic Change Order
  • BOM – Bill of Materials
  • BPR – Business Process Reengineering
  • ATP – Available To Promise
  • ISO – International Standardization Organization
  • CNC – Computer Numerical Control
  • RMA – Return Merchandise Authorization
  • ROI – Return On Investment
  • CSR – Customer Service Representative
  • DNS – Domain Name Service
  • EFT – Electronic Funds Transfer
  • FTP – File Transfer Protocol
  • JIT – Just In Time
  • POS – Point of Sale
  • CAD – Computer Aided Design
  • CAE – Computer Aided Engineering
  • RFP – Request For Proposal
  • WIP – Work In Process

In addition to all of the TLA’s, we’ve been working with a ton of new (and old) technology which is something that I thought I would post about. What’s interesting about working with many of these ERP-related systems is that if they are not SAP or Oracle, they don’t seem to get a lot of exposure. This makes working on these systems so much harder because information is no longer a Google (or Bing) search away. Not only is the technology older, but the way to access information about the technology is a throw-back to the days of (gasp) reading manuals and asking real people.

The particular ERP system we use is called QAD, which uses a web service based interface called Qxtend to communicate to other systems. Trust me, more than once I tried searching for interfacing with QAD via Qxtend and .NET, but to no avail. Working with Qxtend as a .NET developer is extremely different than consuming typical web services, at least for us at this stage in our understanding. Instead of adding a service reference or using WCF or similar framework, we had to do this an older more manual way which I will outline below. It does seem to work quite reliably, but there were several gotcha’s both in setting up the Query Service on the part of Qxtend and consuming it from .NET. All code on the .NET side can be used to connect to other SOAP based web services, so if you aren’t using QAD, don’t worry – you can still get a bit out of this code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Xml;

namespace QxtendSoapCall
{
    class Program
    {
        static void Main(string[] args)
        {
            //Prep information related to Qxtend 
            //(these values are made up for demo purposes)
            string url = "http://qad-qxtend/services/QDocWebService";
            string receiver = "qad_rcv";
            string domain = "abc";
            string version = "v1_1";
            string sourceApp = "abc";
            string profile = "GetCustomerReps";
            int maxRows = 0;  //unlimited

            //Set filter for query service (optional)
            //(these values are made up for demo purposes)
            string part = "ABC-123";
            string filter = string.Format("pt_part = {0}", part);

            //Load generic xml request template from external file
            //(create an empty QueryService QDOC request to begin and add placeholders)
            string templatePath = @"C:\XmlData\QueryService.xml";
            string requestXML = string.Empty;
            using (StreamReader streamReader = new StreamReader(templatePath))
            {
                requestXML = streamReader.ReadToEnd();
            }

            //Replace template values with values for this query service
            requestXML = requestXML.Replace("{receiver}", receiver);
            requestXML = requestXML.Replace("{domain}", domain);
            requestXML = requestXML.Replace("{version}", version);
            requestXML = requestXML.Replace("{sourceApplication}", sourceApp);
            requestXML = requestXML.Replace("{profile}", profile);
            requestXML = requestXML.Replace("{filter}", filter);
            requestXML = requestXML.Replace("{maxRows}", maxRows.ToString());

            //Clean up template
            requestXML = requestXML.Replace("\n", "").Replace("\r", "");

            //Prep service call variables for qxtend
            WebRequest request = null;
            WebResponse response = null;
            string xmlResponse = string.Empty;

            try
            {
                //Prepare web request
                request = WebRequest.Create(url) as HttpWebRequest;
                request.Method = "POST";  //post method
                request.ContentType = "text/xml";  //xml
                request.Headers.Add("soapaction", url);  //soapaction header
                request.Headers.Add("Synchronous", "Yes");  //synchronous
                request.Timeout = 30000;  //30 seconds timeout expiry

                //Encode xml string into byte array
                byte[] byteData = Encoding.UTF8.GetBytes(requestXML);
                request.ContentLength = byteData.Length;

                //Post byte array
                using (Stream postStream = request.GetRequestStream())
                {
                    postStream.Write(byteData, 0, byteData.Length);
                    postStream.Close();
                }

                //Get web response
                response = request.GetResponse() as HttpWebResponse;

                //Pull response into stream
                Stream stream = response.GetResponseStream();

                //Read stream
                StreamReader reader = new StreamReader(stream);
                xmlResponse = reader.ReadToEnd();
            }
            catch (WebException webEx)
            {
                //TODO: Handle your web exceptions here
            }
            catch (Exception ex)
            {
                //TODO: Handle your general exceptions here
            }

            //Convert string to XmlDocument (or XDocument)
            XmlDocument xdoc = new XmlDocument();
            if (!string.IsNullOrEmpty(xmlResponse))
            {
                xdoc.LoadXml(xmlResponse);
            }

            //TODO: Do something with XML now that you have data from QAD
        }
    }
}

That’s it, now you’ve got a way to generically call QAD Qxtend Query Service from .NET without needing an XSD or creating service references to .NET. This is fairly new for me, so if you see any bugs or better approaches, please leave a comment!

Cache Management Helper Class

As a follow up to my post on creating a Web Session Management and Centralization Class, I thought I would also touch on one method of presentation level caching. Caching, as well all know, stores a local version of some data closer to the actual requestor making the call, whether it is in the BLL, the presentation, or even page/control specific. Caching can be a significant boost to performance and is fairly easy to implement. Additionally, there are several types of caching, such as image, page, custom, etc. In this brief example, we’re going to explore a custom implementation of caching to minimize trips to the database.

When Would I Use This?

In my opinion the ideal candidates for presentation level caching are things like dropdown lists which are dynamically pulled from the database. I’ve been working on a project which utilizes several dropdowns throughout the application, and each one is to be administered dynamically. This means for each page, I may have several calls to simply setup the page and populate the dropdowns.

In this application, I’m calling a business logic layer which calls down to the data access layer and finally to the database. We can minimize the amount of calls to the database by instead of calling directly to the business layer, call our WebCacheManager class, which is going to first check if the data exists in local cache, or if it needs to make a call to retrieve the data. If the call is made down to the business layer, the results are then stored in the local cache until either the cache expires based on an absolute date/time or a dependent file (acting as a trigger) is modified.

The external file, located in _cacheDependencyFilePath in the following code is a textfile, which can be manually or programmatically modified to expire the cache without having to reset IIS or wait out the expiration time period. The following code sets up a helper class which could sit on the presentation (web application), and be called to retrieve cached data back very quickly.

WebCacheManager

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Common.Entities.Complaints;  //This is just a reference to my business entities project
using System.Web.Caching;
using System.IO;

namespace UI.ComplaintsWeb.Code
{
    public class WebCacheManager
    {
        /// 
        /// A reference to the current context cache.
        /// 
        private Cache _cache;

        /// 
        /// A filepath to the cachedependency file which triggers expiration of cache.
        /// 
        private string _cacheDependencyFilePath;

        /// 
        /// The absolute date/time of cache expiration.
        /// 
        private DateTime _expirationDate;

        public WebCacheManager()
        {
            _cache = HttpContext.Current.Cache;
            _cacheDependencyFilePath = Path.GetFullPath("../CacheDependency.txt");
            _expirationDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 59, 59);
        }

        public InspectionType[] GetInspectionTypes()
        {
            InspectionType[] data;
            string cacheKey = "InspectionTypes";

            if (_cache[cacheKey] != null)
            {
                //Get data from cache
                data = _cache[cacheKey] as InspectionType[];
            }
            else
            {
                //Get data from database                
                data = WebHelper.ComplaintsLogic.GetInspectionTypes();

                //Assign to cache
                _cache.Add(cacheKey, data, new CacheDependency(_cacheDependencyFilePath), _expirationDate, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
            }

            return data;
        }        
    }
}

Fancy, How Do I Call It?

No sweat, let’s imagine we’re binding to a DropDownList called “InspectionTypeList”, instead of your typically business call (or direct DB call if you’re into that sort of thing, and you probably shouldn’t be, but that’s another topic); simple use the following similar syntax:

var cacheManager = new WebCacheManager();
//Inspection Types
InspectionTypeList.DataSource = cacheManager.GetInspectionTypes();
InspectionTypeList.DataBind();

Not too much to it. If you have any questions on how the WebCacheManager is being used, or suggestions on improvements, please leave a comment below. Don’t be shy, you know you want to comment.

A Generic Data Mapper for Simple Data

Recently, a buddy and I were working on a project and we came across an almost abnormal amount of simple data entities which were used primarily for populating dropdowns. You know the type of data, there is essentially an ID, Name, and not much else.

We needed to do several things with these simple entities, namely populate dropdown, but also associate the entities with other more complex entities. In our architecture, we’re using a layered approach and manual mappers of data retrieval to business entities. There were several ways that we could have gone about propagating this data through the DAL, BLL, and to the presentation, but we chose to create individual entities to represent each of these lists of data. Our reasoning for this was to optimize the ability to separate the code and provide for future classes that could take on additional responsibility if these entities became more complex.

For these simple entities, we didn’t want to write an individual mapper for each one (essentially a whole lot of cut and paste work); so we opted to create a generic mapper class which is constrained by a base class which all of the simple entities inherit from (SimpleData).

The SimpleData class is just that, simple, and looks like this:

The SimpleData Entity

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

namespace Common.Entities.General
{
    [DataContract()]
    public abstract class SimpleData
    {
        #region Constructors

        public SimpleData()
        {
        }

        public SimpleData(int id)
            : this(id, string.Empty, string.Empty)
        {
        }

        public SimpleData(string text, string value) : this(0, text, value)
        {            
        }

        public SimpleData(int id, string text, string value)
        {
            this.ID = id;
            this.DataText = text;
            this.DataValue = value;
        }

        #endregion

        #region Properties

        public DateTime Created { get; set; }
        public string DataText { get; set; }
        public string DataValue { get; set; }
        public int ID { get; set; }
        public DateTime? LastUpdated { get; set; }

        #endregion

        #region Methods

        public abstract SimpleData Create(int id, string text, string value);

        #endregion
    }
}

Now that we had a base class to inherit from, we needed to apply this to our simple entity to be mapped; here is one example of our entity inheriting SimpleData:

The Entity to be Mapped

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

namespace Common.Entities.General
{
    /// 
    /// An arbitrary class implementing SimpleData.
    /// 
    public class CallType : SimpleData
    {
        #region Constructors

        public CallType()
        {
        }

        public CallType(int id)
            : this(id, string.Empty, string.Empty)
        {
        }

        public CallType(string text, string value) : base(text, value)
        {            
        }

        public CallType(int id, string text, string value)
            : base(id, text, value)
        {
        }

        #endregion

        public override SimpleData Create(int id, string text, string value)
        {
            return new CallType(id, text, value);
        }
    }
}

Now that we had a consistent approach to marking up the simple entities, we could create a single generic mapper class called “SimpleMapper”. The class takes in a DataRow, the name of the ID field, the name of the textual (Name) field, and the name of the value field (specifically, if this is different than the ID).

The reason we have a few extra input parameters here is so the SimpleMapper could handle for these two scenarios (we’ll use our Call Type as the example):

  • Scenario 1: My data has an integer ID for the unique identifier of the database record and the name of the Call Type as the textual value. My database table could look like this:
CallTypes Database Table
ID int
Name varchar(50)
  • Scenario 2: My data has an integer ID for the unique identifier of the database record, a name of the Call Type as the textual value, and a 3-digit textual code for the CallType. My database table could look like this:
CallTypes Database Table
ID int
Name varchar(50)
Code varchar(3)

By specifying which column should be returned as the DataValue field (either the ID, the code, or even the textual name itself) the class has a little more flexibility. Below is the SimpleMapper class in its entirety:

The SimpleMapper Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Entities.General;
using System.Data;

namespace DataAccess.Full
{
    public class SimpleMapper where T : SimpleData, new()
    {
        public static T Map(DataRow row, string idName, string textName, string valueName)
        {
            var creator = new T();
            int id = (int)row[idName];
            string text = (string)row[textName];

            string value = id.ToString();
            if (idName != valueName)
            {
                value = (string)row[valueName];
            }

            var obj = creator.Create(id, text, value);
            
            return (T)obj;
        }

        public static T[] Map(DataTable data, string idName, string textName, string valueName)
        {
            var entities = new List();
            foreach (DataRow row in data.Rows)
            {
                entities.Add(Map(row, idName, textName, valueName));
            }

            return entities.ToArray();
        }
    }
}