Who Dat!!!!


Here's a little joke that’s timely if you follow American Football:

Old Cajun Boudreaux died and unfortunately he found himself  in hell.  The devil decided to make him particularly miserable so when Boudreaux arrived he really turned up the heat.
Boudreaux said, "Man, this heat reminds me of the days I spent fishing on the bayou in the middle of the summer."
This made the devil angry that he was not making him suffer, so he turned the heat up to maximum.
Again Boudreaux said "Man, if I knew hell would be like this I would not have feared it so much.  This reminds me of  a hot summer day hunting alligator in the swamp".

This made the devil furious.  He thought on this a minute and said to himself: "I know what to do.  Since Boudreaux likes the heat so much, I'm going to punish him another way”, and he turned the temperature way down.
When Boudreaux woke up the next morning, his room was freezing, with icicles hanging from the ceiling, a foot of snow on the ground, and even a snowman in the corner.  When Boudreaux saw this frozen picture he exclaimed excitedly "My God, the Saints done won the Super Bowl."!!!!

Who Dat!!! to all my fellow Saints fans.

Technorati Tags:

author: Eric Swann | posted @ Saturday, December 19, 2009 12:01 PM | Feedback (1)

LightSpeed 3.0 App – Part 2 – Logging and IOC Initial Setup


image This is the second installment in the development of my home automation system using LightSpeed 3.0 and various other tools.  I’ve got an overview here and of course you are encouraged to read Part 1 before diving in here.

I should mention before starting that I’ve changed a couple of projects in the solution.  I’ve renamed the admin web application to: Gigabode.Admin.Web.UI.  In addition, I’ve added a Gigabode.Admin.Web class library for classes that are specifically created to support the Admin Web UI. 

I decided to start by setting up some stuff that I’m going to be using throughout the application, namely the logging and (Inversion of Control) IOC concerns.  For the logging and exception handling, I’m going to be using the Enterprise Library.  I’m going to go ahead and use StructureMap for the IOC container.  I was going to use Unity since it’s already included with the Enterprise Library, but it doesn’t have a good per request lifetime manager, and although people have published a few, I’m not convinced they won’t lead to poor disposal of objects.  StructureMap already has a per HttpContext built in.  I’ve used Spring, Unity, and Windsor, but I’ve never used StructureMap.  I have to say that it’s the most impressive IOC I’ve tried out so far.  It’s very intuitive and full of features.

Logging Setup

In this project, I’m generally going to try to not start with the database.  I’m going to try to model stuff in the LightSpeed designer and have it build my tables for me.  Of course I’ll need to add scripts for indexes and such, but at least I can leverage LightSpeed to generate the DB tables.  However, for the logging database, the Enterprise Library includes a script to set up the default logging tables and stored procedures, so I’ll use this to set up the database.  There are a few things I don’t like about this script out of the box:

  1. The default script tries to set up a different database just for logging.  For this project, I’m just going to add it to the existing database.
  2. The names of the tables are things like “Category”, much too general if they are going to exist in my overall database.  So I’ve renamed them to all begin with “Logging”.

So I’ve updated the script to cover these changes.  If you would like them for your own project, they can be downloaded here.

Setting Up  Logging in the App

image So now I’ve got the data structure in place, and I need to add in a way to actually log to the database.  I usually set up three basic levels of logging:  Error, Info, and Verbose.  So to make this easier, I’m going to set up an interface to log to each of these levels in my domain project (ILogService), and then set up the implementation in the AppServices project (LogService). 

image Since I’m using the Enterprise Library (EL), I need to set up the configuration for this application block.  Configuration is usually stored in the application config file (web.config in my case), but the EL includes an easy GUI tool for putting together the configuration so you don’t have to hack through all of the gnarly XML needed to set up the various properties.  I’m not going into how to use this in detail, but I’ve pasted a shot of the config I’ve set up in the GUI.  If you look under the Category Sources node, I’ve set up a category for Error, Information, and Verbose.  I’ve also set up two listeners, one that writes to the database, and a backup that writes to a flat file source.  The flat file listener is only used if a logging error or warning is produced by the EL logging block itself.  Say for example it fails while logging to the database.  There are several other listeners available, such as email and event log, but this will do for now.

Now that I’ve got a logging interface and an implementation, I need something to tie them together, so I need to go ahead and set up my IOC implementation.  A lot of people like to wait a while before setting up IOC, but from my perspective, I know I’m going to be using it throughout the app, so I like to set it up right off the bat so that I can get my IOC pattern in place.  I’m definitely in agreement with Chad Myers on this point.

   1: using System;
   2: using System.Diagnostics;
   3:  
   4: namespace Gigabode.Domain.AppServices.Core
   5: {
   6:     public interface ILogService
   7:     {
   8:         int LogVerbose(string message);
   9:  
  10:         int LogInfo(string message);
  11:  
  12:         int LogError(string message);
  13:  
  14:         int LogError(Exception ex);
  15:  
  16:         int LogMessage(string message, TraceEventType severity, int priority, params string[] categories);
  17:     }
  18: }

ILogService Interface 

   1: using System;
   2: using System.Diagnostics;
   3: using Gigabode.Domain.AppServices.Core;
   4: using Microsoft.Practices.EnterpriseLibrary.Logging;
   5:  
   6: namespace Gigabode.AppServices.Core
   7: {
   8:     /// <summary>
   9:     /// Logging utility class.
  10:     /// </summary>
  11:     public class LogService : ILogService
  12:     {
  13:         /// <summary>
  14:         /// Logging Categories
  15:         /// </summary>
  16:         public static class Category
  17:         {
  18:             public const string Error = "Error";
  19:             public const string Information = "Information";
  20:             public const string Verbose = "Verbose";
  21:         }
  22:  
  23:         public int LogVerbose(string message)
  24:         {
  25:             return LogMessage(message, TraceEventType.Verbose, 1, Category.Verbose);
  26:         }
  27:  
  28:         public int LogInfo(string message)
  29:         {
  30:             return LogMessage(message, TraceEventType.Information, 2, Category.Information);
  31:         }
  32:  
  33:         public int LogError(string message)
  34:         {
  35:             return LogMessage(message, TraceEventType.Error, 5, Category.Error);
  36:         }
  37:  
  38:         public int LogError(Exception ex)
  39:         {
  40:             if (ex != null)
  41:             {
  42:                 return LogError(ex.ToString());
  43:             }
  44:             return 0;
  45:         }
  46:  
  47:         public int LogMessage(string message,
  48:             TraceEventType severity, int priority, params string[] categories)
  49:         {
  50:             var entry = new LogEntry
  51:             {
  52:                 Message = message,
  53:                 Priority = priority,
  54:                 Severity = severity
  55:             };
  56:             foreach (var category in categories)
  57:             {
  58:                 entry.Categories.Add(category);
  59:             }
  60:  
  61:             return LogEntry(entry);
  62:         }
  63:  
  64:         private static int LogEntry(LogEntry entry)
  65:         {
  66:             int eventID = 0;
  67:  
  68:             if (Logger.ShouldLog(entry))
  69:             {
  70:                 eventID = GenerateEventID();
  71:                 entry.EventId = eventID;
  72:                 Logger.Write(entry);
  73:             }
  74:             return eventID;
  75:         }
  76:  
  77:         private static int GenerateEventID()
  78:         {
  79:             return int.Parse(DateTime.Now.ToString("yy").Substring(1)
  80:                 + DateTime.Now.DayOfYear.ToString().PadLeft(3, '0')
  81:                 + DateTime.Now.TimeOfDay.TotalMilliseconds.ToString().Substring(0, 5));
  82:         }
  83:  
  84:     }
  85: }

LogService Implementation

Setting Up the IOC

I’m going to start by using the CommonServiceLocator(CSL).  Even though I’m using StructureMap, I’d like to leave the door open in case I decide to switch it out for another IOC container at some point.  CSL just sets up some common GetObject methods so that you don’t tightly couple to the IOC implementation.    You still have to use the implementation of the container at the client application level to register the objects, so some work will be required if you decide to switch.

   1: public interface IServiceLocator : IServiceProvider
   2: {    
   3:     object GetInstance(Type serviceType);    
   4:     object GetInstance(Type serviceType, string key);    
   5:     IEnumerable<object> GetAllInstances(Type serviceType);    
   6:     TService GetInstance<TService>();    
   7:     TService GetInstance<TService>(string key);    
   8:     IEnumerable<TService> GetAllInstances<TService>();
   9: }

Typically, like most people, I set up a thirdParty (or lib) folder at the same level as my source code folder.  I put all my third party dll’s here, even if they are in the GAC.  This just makes it easier to keep track of them and deploy them.  So I’m dropping the CSL dll here as well as the StructureMap adapter implementation for the CSL.

Now, I’m going to go ahead and set up an object registrar class in the web application.  This is why I went ahead and added the Gigabode.Admin.Web class library.  This registration will be particular to the Admin web application, but I don’t want to stick this in the Global.asax because it’s just kind of messy and it’s hard to unit test. 

Structure map follows a great pattern whereby you can set up “Registry” classes that perform IOC registration for a given application area.  I really like this approach.  Then when you create your container, you initialize it with the registry classes to set up your dependency associations.  In order to facilitate this and tie it to the CommonServiceLocator, I made a little Registrar class to perform basic startup operations.  This registrar is then initialized in the web application’s Global.asax.

   1: using Microsoft.Practices.ServiceLocation;
   2: using StructureMap;
   3: using StructureMap.ServiceLocatorAdapter;
   4:  
   5: namespace Gigabode.Admin.Web.Ioc
   6: {
   7:     public class Registrar
   8:     {
   9:         public Container Container{ get; protected set; }
  10:  
  11:         public void Initialize()
  12:         {
  13:             Container = new Container();
  14:             Container.Configure(x =>
  15:             {
  16:                 x.AddRegistry<AppServicesRegistry>();
  17:             });
  18:             ServiceLocator.SetLocatorProvider(() => new StructureMapServiceLocator(Container));
  19:         }
  20:     }
  21: }

Registrar Class

   1: public class Global : HttpApplication
   2: {
   3:     private Registrar registrar;
   4:  
   5:     protected void Application_Start(object sender, EventArgs e)
   6:     {
   7:         registrar = new Registrar();
   8:         registrar.Initialize();
   9:     }
  10: }

Global.asax

Setting Up the First Test

Now the everything is in place, I want to set up a unit test to make sure that my IOC system with the combination of StructureMap and the Common Service Locator actually works.  Just a simple first unit test to verify that after the Registrar is initialized, the Common Service Locator returns my concrete LogService when I ask for an ILogService. 

   1: using SoftwareApproach.TestingExtensions;
   2:  
   3: namespace Gigabode.Test.UnitTest.Web.Ioc
   4: {
   5:     [TestClass]
   6:     public class RegistrarFixture:BaseUnitTest
   7:     {
   8:         [TestMethod]
   9:         public void LogService_Is_Registered()
  10:         {
  11:             var registrar = new Registrar();
  12:             registrar.Initialize();
  13:  
  14:             var logService = ServiceLocator.Current.GetInstance<ILogService>();
  15:             logService.ShouldNotBeNull();
  16:         }
  17:     }
  18: }

First Test

I’m using MSTest as my framework, and it’s little different on the setup than NUnit.  You can access various resources made available to the test framework through a TestContext object in the unit test.  So the first thing I do is make a base unit test class to encapsulate this and derive my test classes from it.  I’ll expand this as needed in the future.  You may also notice in the test above that I’m not using an “Assert” statement.  I found a library of extension methods that I really like that add a more readable interface for MSTest.

   1: namespace Gigabode.Test.UnitTest
   2: {
   3:     /// <summary>
   4:     /// Summary description for BaseUnitTest
   5:     /// </summary>
   6:     public abstract class BaseUnitTest
   7:     {
   8:         protected BaseUnitTest(){}
   9:  
  10:         /// <summary>
  11:         ///Gets or sets the test context which provides
  12:         ///information about and functionality for the current test run.
  13:         ///</summary>
  14:         public TestContext TestContext { get; set; }
  15:     }
  16: }

Base Unit Test

So at some point, I’m sure you’ve probably been saying….”Enough with the setup! Go do something already!”.  I’ll start the next installment with the actual use of LightSpeed 3.0 to pull the log entries and populate them into a data grid which will also allow a detail view of the information.

author: Eric Swann | posted @ Saturday, December 19, 2009 12:13 AM | Feedback (4)

Crystal Metronome


Just FYI, if you are a runner, and like myself want a metronome to help keep your cadence, I would recommend this little product:  Crystal Metronome.  It’s a very simple little app that lets you set a beats per minute (bpm) rate.  You can then output it to an MP3, drop it on your very small MP3 player/IPod, and set it to repeat.  For running I set it to 180 bpm.  You can also select from some different sounds for the beats like a high-hat or wood block.  A lot of Pose or Chi runners will use these little clip-on metronomes that cost ~30 bucks and don’t even have earphones.  This is way better.  Plus when you’re sick of the metronome, you can switch to some music for a bit.

Brief explanation: Elite runners tend to have a stride rate of 90 or more full strides (a step with each leg equals my definition of a full stride here) per minute, hence the 180 bpm, or one beat per step.  Faster distance runners actually take very efficient fast strides while slower runners take less (around 70 per minute) which tend to be much less efficient.  In fact, if you increase your stride rate by matching a metronome, you are kind of forced to take more compact and more efficient strides.  Combine that with barefoot running, and it won’t be long before you have halfway decent running form.  I’ve always been a relatively fast runner, but also an over-strider and heel-striker (those tend to go together).  This wastes a ton of energy and results in heavy body impact, so the course I’ve taken is just that: Metronome + Vibram Five Fingers.

If you ever want to watch the distance runner with the most awesome form of all time, YouTube a video of Haile Gebrselassie breaking the world marathon record, 4:44 per mile for 26.2 miles….unreal.

Technorati Tags: ,

author: Eric Swann | posted @ Tuesday, December 15, 2009 6:16 PM | Feedback (0)

LightSpeed 3.0 App – Part 1 – Project Setup


In my previous post, I mentioned the creation of a home automation application using LightSpeed 3.0 and ASP.Net.  In this post, I’m going to start with an explanation of basic project setup.  In the posts that follow, I’ll elaborate on the development of individual areas of the application.

Initial Solution Structure

The initial solution contains the following projects:GigabodeProject

  1. Domain – Includes the core domain model as well as the interfaces that may be used to operate on the model.  Most of these interfaces will be implemented by the AppServices, DataAccess, and DomainServices libraries.
  2. AppServices – Services that support core application functionality, such as IOC and configuration functionality.
  3. DataAccess – Mainly a container for the repository functionality of the application.
  4. DomainServices – Services that apply rules to the domain.  Think of this as the business rules dll.
  5. Test.UnitTest – I’ll attempt to just have a single unit test project and split it out into separate projects later as needed.  Using the MSTest framework (booo!)
  6. Web – Kind of an aggregator for base web controls, service locators, http modules, and other items needed to support the various UI’s.
  7. Web.AdminUI – Though I’d start with an Admin UI for basic administrative functionality like controlling Membership, Roles, Lookups, etc…..
  8. Database – Just a place to put database scripts, not a “SQL Server” project, just the basic database project that houses script files.

Right now I’m just looking for an easy starting spot so I have something to help me get the infrastructure organized.  I figured I’d start with the membership functionality of the application and use that to drive some of the infrastructure concerns, such as logging, IOC, auditing, test setup, getting stuff into source control etc…  In part 2, I’ll pull a number of these items into the solution.

Source Control and Task Tracking

So I need a basic source control system and task tracking system.  I know you might say…”seriously, do you really need task tracking if it’s just you”?  My response is: yes!  It works in two ways:

  1. Serves as a notebook for all the tasks and “features” that pop into my head.  I want to stay focused but have a place to log these and remember them later.
  2. If somebody else starts helping me, we can split up and prioritize tasks.  Always good to get the system in place at the beginning of the project.

image

I’m not really planning on releasing this stuff as open source software, so I’m just going with a local solution for now.  I was going to start with SourceGear Fortress because it’s easy to set up and has issue tracking and source control as well as nice integration with Visual Studio, plus it’s free for one user.  But I decided against it in case somebody else gets involved because a license for Fortress isn’t cheap.  So I went with Subversion and Trac instead.  In addition, I did download and install SourceGear DiffMerge.  It’s free and it’s way better than the standard Tortoise merge tool.

Future Enhancements

As the solution progresses, I’ll add some more projects.  For example, I’ll need a windows service to communicate with the Z-Wave stick on the user’s local box.  I’ll obviously need the customer-facing website, and some web services or other communications mechanism (WCF?) for the server to collect data from the user’s house via the previously mentioned Windows service.  There’s a lot here….this could take a while.

Next: LightSpeed 3.0 App – Part 2 – Logging and IOC Initial Setup

Technorati Tags: ,,,

author: Eric Swann | posted @ Monday, December 14, 2009 9:38 PM | Feedback (1)

LightSpeed 3.0 App, Introduction


I’ve been thinking about putting together a physical training application for a while.  I do a lot of running and working out, but I think I may have stress-fractured my foot a couple of weeks ago.  So no better time than the present to work on something to help me in the future.  I know there are a lot of training apps and running apps out there, but I thought I’d take a shot a writing one. 

Hmm, I've made a change of decision on this one.  A while back, a couple of friends and I were talking about a home automation solution using Z-Wave technologies.  In addition, it's a lot nerdier than the previous idea, so might generate more interest in general.  So the basic idea is to create a web based application that can do the following:

  1. Allow control of Z-Wave devices via an ASP.Net application.
  2. Allow these devices to be controlled remotely via the web.

One of the reasons is that I’ve been looking to do an app is that Mindscape has a new version of their ORM tool out:  LightSpeed 3.0.  I really like this ORM, check out my previous entry on LS 3.0.  Another reason is that for an application at work, I had to build an Ad Hoc Reporting system for an application I work on.  I started out using the SQL Server Reporting Services (SSRS) Ad Hoc Reporting support.  It’s a really cool tool.  But I build applications for a customer that is relatively serious about security.  Nothing super secret or all that exciting, but there are a number of locations that use this application, and some of them have their systems locked down very tightly. The down side was that some of them simply couldn’t download the report builder that is used with SSRS.  Instead, I had to go with a “pure web” approach.  While looking for something that would save me needed time and resources, I came across the DevExpress ASP.Net product library and used it extensively to build my Ad Hoc functionality.  It was a huge help.  They have a grid product that’s probably the best I’ve seen.  AJAX enabled, all the bells and whistles, and it can use a LINQ data source and actually leverages the LINQ provider to perform all of the data operations on the server, saving a lot of overhead on the client.  So I had an idea to try and do a RAD application with a rooting in Onion Architecture

So my basic approach is:

  1. ASP.Net/C# application
  2. SQL Server Database
  3. LightSpeed 3.0 ORM
  4. Dev Express ASP.Net tools in the UI
  5. Attempt at pulling it together in an Onion-style architecture.
  6. For Z-Wave connectivity, I'm using the ControlThink .Net API.

The domain name I found was Gigabody GigAbode.  Yeah it’s goofy, but I’ll stick with it for now cause I don’t have anything better.  I’ll get started in the next installment on the basic project setup and first steps.

Articles in this series:

LightSpeed 3.0 App – Part 1 – Project Setup

LightSpeed 3.0 App – Part 2 – Logging and IOC Initial Setup

Technorati Tags: ,,

author: Eric Swann | posted @ Sunday, December 13, 2009 10:41 PM | Feedback (4)

Mindscape LightSpeed 3.0 – Advocating a Commercial ORM


I’ve been a fan of Mindscape’s LightSpeed ORM for a while.  I think that it’s a great alternative to LINQ to SQL (L2S) and NHibernate (NHib).  I work in a shop where we use both NHibernate and L2S very heavily and I have great respect for both of these products, but I’d really like to attempt some real work projects with LightSpeed.  Up to this point, I’ve used it to put together my own prototypes, which typically get moved to a different technology when I apply these technologies in the work environment.  I plan on following up with some articles actually using LightSpeed in a “personal application” I’ve been thinking about for a while, but I also wanted to make an attempt at defending such an ORM for commercial use.  It’s a pretty hard argument in many shops, based on how entrenched NHib and L2S have become, but I’d like to attempt to make a case to the contrary.  I think the most obvious reason that it’s hard to justify a commercial ORM is pretty valid:

L2S and NHib are both free, why would I want to pay for an ORM?

I think this is a great question.  You definitely get a lot for free with NHibernate and L2S.  I would boil the answer down to a simple question:  What is the true cost of ownership and ROI when you factor in developer productivity.  Consider the cost of LightSpeed.  I believe that they are changing the licensing a little with 3.0, but I think it’s going to be something on the order of $149-199 per developer according to the guys at Mindscape.  Let's say this adds up to 3-4 man hours.  So the question is, does the LightSpeed provide productivity enhancements that would save this 3-4 hours per developer over the course of a project.  Not to mention that it could be used across several projects, I think the answer is an absolute yes.  I’m basing this projection based on two factors:  features and support.  If you feel that I’m shortchanging either NHib or L2S with any of these comments, please feel free to set me straight.

Features

NHib is very feature rich, has a lot of third party add-ons, and it’s probably the most flexible ORM as far as scenarios that it will support.  For example, it supports some inheritance approaches (table per subclass for example) that LightSpeed doesn’t support.  So there are some scenarios where NHib is the obvious choice.  For example, if you are plugging into a legacy app, NHib may support some scenarios that are necessary for you.  But to be honest, LightSpeed will support a surprising number of setups (especially with some of the 3.0 improvements, such as support for compound keys) and as a green-field tool, it’s hard to beat.  There are also some great features that LightSpeed 3.0 has right out of the box that NHib doesn’t have, or that you have to track down as a third party tool.  On the other end of the spectrum, there is no doubt that L2S is a great RAD tool, but when it comes to enterprise features, it is pretty weak in some key areas.  I think LightSpeed finds a great middle ground with awesome RAD features, but at the same time, great support for some necessary enterprise functionality.  Here is a list of things that I just think are easier with LightSpeed or just plain don’t exist in L2S or NHib.

  1. Multiple Database Support - OK, NHib supports a lot of databases, and we know L2S is SQL Server specific.  Like NHib, LightSpeed supports a ton of databases, my guess is that it supports some that even NHib doesn’t cover, such as limited support for Amazon SimpleDB.
  2. LINQ – OK, obviously L2S has great LINQ support.  While I know NHib has LINQ support, it’s not very thorough, and to be honest, I don’t know of many people that are comfortable leveraging it in production environments.  Steve Strong has been working on a more comprehensive LINQ provider and I’ve been following him with keen interest, but this is no small task for any developer.  LightSpeed has supported basic LINQ functionality for quite a while, and with LightSpeed 3.0, the LINQ support is greatly expanded.  I have a fair amount of confidence in the guys at Mindscape because this isn’t new ground for them, they’ve been doing LINQ for a while.
  3. Visual Designer Support – In contrast to NHib, LightSpeed works well as a true RAD tool.  Want to throw together a quick prototype, quick and done with LightSpeed.  Straight up, I love the LightSpeed designer.  And I completely reject the idea that “real developers” don’t use designers.  Real developers use the tools at their disposal to deliver solutions in the most efficient, maintainable, communicable, and cost-efficient manner possible.  Their designer is just plain done well.  It’s simple, provides a visual representation of the model (which is invaluable when discussing entity relationships), and supports additional features such as model filtering, inheritance visualization, name refactoring, and too many others to mention.  Short story is that NHib doesn’t have a decent designer, and while L2S has one, it’s extremely limited.  I know you can use Sculpture with NHib, but you’re getting into a quasi-commercial tool at that point.  OK, so you don’t like designers?  Well you don’t have to use the designer with LightSpeed if you just can’t bring yourself to use it: It’s got command line tools as well and you can even provide your own custom code generation templates.
  4. DB Schema Synchronization - LightSpeed has a simple helper built in for forward and reverse engineering from the database, which will let you select the individual items to synchronize and give you an option to spit out the scripts or just apply them.  Sweet!  Yes, I know NHib has support for forward engineering from the model, but the difference is that LightSpeed makes it so darned easy.  Just a couple of clicks on the designer and you’re done.  Synchronization with L2S typically has me pulling my hair out.  Sometimes it’s just easier to delete the Entity from the L2S designer and re-drop it.  Hope you didn’t make any changes!
  5. Migrations - Migrate the database schema from one version of your app to the next.  This is new with 3.0.  I’m hoping this will really help me cut down on the DB scripts that I write.  There are some great third-party tools that can accomplish this, such as Tarantino, but now you are launching into a whole new product and learning curve.  Having a migration tool built into LightSpeed is just plain nice.  One stop shopping in short.
  6. Validation Framework – LightSpeed has a great built-in validation framework that supports declarative attributes.  Sure there is Castle and Enterprise Library, but again, one-stop shop.
  7. Level 2 Caching Support – NHib has great level 2 caching support, but this is sorely lacking in L2S.  You can implement your own, but LightSpeed has a couple of options, including the standard web cache as well as support for Memcached right out of the box.  You just state that an item will be cached right in the designer.
  8. Little Time-Savers – LightSpeed has some nice little “convention” productivity features that save you time, such as automatic setting of created and updated dates, soft delete, and concurrency handling if you just add the correct fields to your table for the entity.  In addition, it has built in change tracking with LightSpeed 3.0 if you need it for auditing purposes.
  9. Random Things You Need for Enterprise Apps – A few other things that are problematic for L2S, but that LightSpeed handles out of the box:  Many-to-many, flexible eager loading, efficient query batching.
Support

A major lacking factor in both L2S and NHib is support.  This is an unbelievable differentiator when you consider ROI.  Sure you can buy support for NHib, but then it wouldn’t be free anymore, would it?  And good luck getting anything related to L2S fixed at all, much less in a reasonable timeframe.  There is a lot of literature published on both of L2S and NHib, but I have spent countless hours accumulating enough knowledge on both of these products to become relatively proficient.  And still sometimes, I’m combing the web, hunting for answers for quite some time.

In contrast, I’ve always gotten amazing support from the Mindscape guys when I had a question about their product.  This is considering that I’m a measly 1 license holder, and not even their enterprise license.  In fact, Mindscape was extremely responsive before I had purchased anything from them, I was just using their free version for a while.  If you find an issue, chances are that it will be corrected in the next nightly release.  If you have a compelling suggestion, chances are that your suggestion will also be included in a nightly release.  I had just such a case when I suggested that they add GUID.Comb as an identity strategy.  A couple of days later, it was implemented and available in a nightly build. 

How about the response:  NHibernate is open source; if there is something I want to implement, I can do it myself!  More power to you, but honestly, I’ve got deadlines and a set number of billable hours on most projects, and while I use NHib a lot, I’m not really interested in becoming an expert on it’s inner workings.  Isn’t it great to have a team dedicated to helping you with these issues instead of having to address them yourself?  For the sum they are asking…no question.

My Biggest Issue

My  biggest gripe with LightSpeed is that it relies on inheritance.  So you end up polluting your model a bit with a LightSpeed reference and base class, and if you use LightSpeed in the most common manner, the classes will be generated on your behalf by the designer and code generator.  When you are going with an “Onion Architecture” approach, which is what I try to do in general, this can make you feel like you are sinning a bit.  However, LightSpeed does a few things to mitigate their interference, which I’ve decided I can live with given everything LightSpeed does for me:

  1. All generated classes are partial, making them easy to extend.
  2. You can insert your own base class(es) into the class hierarchy.  So in my case, I have a BaseEntity class that contains some core entity commonalities.  I want to use this as the basis for most of my model entities.  I can inherit my BaseEntity from the provided LightSpeed Entity class and tell LightSpeed to substitute it as the basis for one or more of the model classes generated by the designer.
  3. You can import any of your custom types into the designer.  If you want to reference your own type, such as an enumeration in place of an Int32, it’s very easy to pull the type into the designer and tell LightSpeed to map to your custom type.
  4. With LightSpeed 3.0, the generated classes give you a number of productivity features that I would have to implement anyway, such as change tracking and the other features mentioned previously. 

So I’m ceding some tight coupling to the ORM.  And frankly, if I decide I’m going to Production with an ORM technology, I’m not going to rip it out and replace it for a while.  So I accept that I am going to pay a price if I decide to change course on the ORM.  I think this is a given with any of the ORM technologies though.  Think about all of the HQL queries to refactor if you decide to drop NHib, all the mappings to update, etc….  I recently upgraded a project from NHib 1.2 to 2.x, and although it wasn’t too bad, even this was far from frictionless.

As mentioned above, I plan to follow with a few articles using LightSpeed 3.0 in an actual application, highlighting some of the features I’ve mentioned in this article.  Looking forward to any comments you may have.

kick it on DotNetKicks.com

 

Technorati Tags: ,,

author: Eric Swann | posted @ Sunday, December 13, 2009 2:53 PM | Feedback (14)

Resharper 4.5


Looks like Resharper 4.5 is out the door.  The big initiative this time seems to have been performance.  I have to admin, sometimes 4.x seemed to take forever to load on large projects.  I’ve been using the beta for a little bit, and it definitely seems like the project load times are shorter.  I like the solution-wide code inspection as well.  That’s been on my wishlist for a while.

Technorati Tags: ,

author: Eric Swann | posted @ Wednesday, April 08, 2009 10:23 PM | Feedback (0)

LINQ to SQL DataContext Provider Revisited


A complete sample code project for this article is available here.

In a previous post, I had proposed an idea on how to control the lifetime of the DataContext in LINQ to SQL (L2S).  I basically created a provider class that would detect the presence of an HttpContext.  If the context was present, it would use the HttpContext and store the DataContext on a per-request scope.  Otherwise, it would store the DataContext in the CallContext, basically creating a per-thread scope for the DataContext. 

I actually felt a little dirty after having done this, but for some reason the idea of injecting the provider eluded me until I read Steve Sanderson’s blog entry on this.  Duh!  Basically: dependency Injection of the caching strategy instead of trying to detect the HttpContext and figure it out for the user.  Of course!  So I refactored in this way.  Then I came across a different issue.  A coworker of mine had a project with multiple clients that used the same L2S model.  They had both a web and a windows app riding on the same service layer. 

The problem was that he wanted to be able to flush the DataContext in his windows app so that he could ensure the identity map of L2S wasn’t causing the build up of stale data on the client.  However, he still wanted the lifetime control so that he could use ADO.Net transactions without escalating to DTC.  If you weren’t aware of this, every time the DataContext is newed up, it’s pulling a new connection from the pool.  With our datacontext-instance-per-repository approach, transactions across repositories were resulting in the transaction being elevated to Distributed Transaction Coordinator (DTC)….definitely something you want to avoid if possible.  So the thread-based lifetime needed a way to allow flushing of cached contexts.  The problem I had with constructor injecting the caching strategy into my context provider was that I didn’t have a clean way to register the caching provider so that I could flush it later.

To solve this issue, I stuck with a similar theme as I had previously.  A user registers their DataContext using static method of the DataContextProvider, but at the same time they also register a caching strategy.  This will typically be dependent on the type of application.  This now allows tracking of the caching provider and the ability to flush the cached DataContexts.  Now my windows app can perform a transaction across a few repositories and then get a fresh DataContext.  Here’s the approach:

DataContextProvider Class

This is the class that does all of the heavy lifting.  To start with, it has some static methods for registering the DataContext and caching strategies.  It has a few overloads, but they siphon into the following static method:

   1: /// <summary>
   2:  /// Registers the data context.
   3:  /// </summary>
   4:  /// <typeparam name="T">Type of the datacontext to register.</typeparam>
   5:  /// <typeparam name="CacheProviderType">The type of the cache provider.</typeparam>
   6:  /// <param name="contextKey">The context key to uniquely identify this context.</param>
   7:  /// <param name="connectionString">The connection string to use with the context.</param>
   8:  public static void RegisterDataContext<T, CacheProviderType>(string contextKey, string connectionString) 
   9:      where T : DataContext, new()
  10:      where CacheProviderType : IDataContextCache 
  11:  {
  12:      lock (syncLock)
  13:      {
  14:          var contextInfoKey = GetContextInfoKey<T>(contextKey);
  15:          contextRegistry[contextInfoKey] = new DataContextInfo
  16:            {
  17:                ConnectionString = connectionString,
  18:                ContextInfoKey = contextInfoKey,
  19:                Type = typeof (T),
  20:                CacheType = typeof (CacheProviderType)
  21:            };
  22:      }
  23:  }

This method registers a DataContext and it’s caching strategy (with optional connection string and key) to a static method for retrieval when the DataContext is needed.  It doesn’t store the DataContext class itself, but instead stores information about the DataContext using a DataContextInfo class.  The DataContext is registered using it’s type as the key.  If a contextKey is passed in, it’s added to the end of the key.  It’s really just there to differentiate if a user has multiples of the same DataContext registered.  

When a context is needed, it is requested using the GetDataContext method.  This is an instance method.  I’ll get to how it’s used in a second, but first an explanation of how this works. 

   1: /// <summary>
   2: /// Gets the data context.
   3: /// </summary>
   4: /// <typeparam name="T">Type of the data context to retrieve.</typeparam>
   5: /// <param name="contextKey">The context key to uniquely identify the context.</param>
   6: /// <returns>The data context.</returns>
   7: public T GetDataContext<T>(string contextKey) where T : DataContext, new()
   8: {
   9:     var contextInfoKey = GetContextInfoKey<T>(contextKey);
  10:     var contextInfo = GetRegisteredDataContextInfo(contextInfoKey);
  11:  
  12:     var dataContext = RetrieveDataContextFromCache<T>(contextInfo) ??
  13:                       CreateAndCacheDataContext<T>(contextInfo);
  14:  
  15:     return dataContext;
  16: }

The context info key is reconstructed and the DataContextInfo is pulled from the DataContext registry.  Using this key, the DataContextProvider will first attempt to retrieve the DataContext from cache.  If this is null, it will new up the DataContext and cache it for later use. 

   1: /// <summary>
   2: /// Retrieves the data context from cache.
   3: /// </summary>
   4: /// <typeparam name="T">Type of the data context.</typeparam>
   5: /// <param name="contextInfo">The context info for the requested context.</param>
   6: /// <returns>Data context if already cached.</returns>
   7: protected T RetrieveDataContextFromCache<T>(DataContextInfo contextInfo) where T : DataContext
   8: {
   9:     var contextCache = GetDataContextCache(contextInfo);
  10:     if (contextCache.Items.ContainsKey(contextInfo.ContextInfoKey))
  11:     {
  12:         return (T)contextCache.Items[contextInfo.ContextInfoKey];
  13:     }
  14:     return default(T);
  15: }
  16:  
  17: /// <summary>
  18: /// Creates and caches the data context.
  19: /// </summary>
  20: /// <typeparam name="T">Type of the data context.</typeparam>
  21: /// <param name="contextInfo">The context info for the requested context.</param>
  22: /// <returns>A new DataContext of the type requested.</returns>
  23: protected T CreateAndCacheDataContext<T>(DataContextInfo contextInfo) where T : DataContext
  24: {
  25:     if(contextInfo == null)
  26:         throw new ArgumentException("DataContext was not registered with the application.");
  27:  
  28:     if(contextInfo.Type != typeof(T))
  29:         throw new InvalidOperationException("Context is registered, but it's type does not match the type requested.");
  30:  
  31:     var dataContext = string.IsNullOrEmpty(contextInfo.ConnectionString)
  32:         ? (T)Activator.CreateInstance(typeof(T))
  33:         : (T)Activator.CreateInstance(typeof(T), new object[] { contextInfo.ConnectionString });
  34:  
  35:     var contextCache = GetDataContextCache(contextInfo);
  36:     if (contextCache != null)
  37:     {
  38:         contextCache.Items[contextInfo.ContextInfoKey] = dataContext;
  39:     }
  40:  
  41:     return dataContext;
  42: }
  43:  
  44: protected IDataContextCache GetDataContextCache(DataContextInfo contextInfo)
  45: {
  46:     if (dataContextCache == null)
  47:     {
  48:         dataContextCache = CreateDataContextCache(contextInfo);
  49:     }
  50:     return dataContextCache;
  51: }
  52:  
  53: private static IDataContextCache CreateDataContextCache(DataContextInfo contextInfo)
  54: {
  55:     return (IDataContextCache)Activator.CreateInstance(contextInfo.CacheType);
  56: }

So how does this work?  The GetDataContextCache method is called first.  If the cache has already been instantiated, this will be present in a local variable, otherwise CreateDataContextCache retrieves the caching strategy type from the DataContextInfo and instantiates it.  The caching strategy isn’t the cache itself, it just provides a standard interface for us to access whichever cache we are using to store the DataContext.  I’ll get to the implementation of this strategy in a little bit. 

Once the cache strategy is obtained, the ContextInfoKey is used to retrieve the DataContext from the cache.  If the DataContext doesn’t exist, it’s instantiated, optionally using the connection string provided when the DataContext was registered.  Now we’ve got our cached DataContext!  

Caching Strategies

So all the caching strategy has to do is implement the IDataContextCache interface which exposes an Items property getter and a Clear() function.  I put together three basic strategies out of the box:  HttpRequest lifetime, Thread lifetime, and “No Cache”.  The interface and cache provider code is shown below:

   1: public interface IDataContextCache
   2: {
   3:     /// <summary>
   4:     /// Gets the data context cache.
   5:     /// </summary>
   6:     IDictionary<string, DataContext> Items { get; }
   7:  
   8:     /// <summary>
   9:     /// Clears items (DataContexts) in the cache and disposes resources.
  10:     /// </summary>
  11:     void Clear();
  12: }
   1: /// <summary>
   2: /// Caches the context in http context storage.
   3: /// </summary>
   4: public class DataContextWebCache : DataContextCacheBase
   5: {
   6:  
   7:     protected override IDictionary<string, DataContext> GetDataContextCache()
   8:     {
   9:         var dataContextCache = (Dictionary<string, DataContext>)HttpContext.Current.Items[DataContextCacheKey];
  10:  
  11:         if (dataContextCache == null)
  12:         {
  13:             dataContextCache = new Dictionary<string, DataContext>();
  14:             HttpContext.Current.Items[DataContextCacheKey] = dataContextCache;
  15:         }
  16:         return dataContextCache;
  17:     }
  18: }
  19:  
  20: /// <summary>
  21: /// Caches the context in thread local storage.
  22: /// </summary>
  23: public class DataContextThreadCache : DataContextCacheBase
  24: {
  25:  
  26:     protected override IDictionary<string, DataContext> GetDataContextCache()
  27:     {
  28:         var dataContextCache = (Dictionary<string, DataContext>)CallContext.GetData(DataContextCacheKey);
  29:  
  30:         if (dataContextCache == null)
  31:         {
  32:             dataContextCache = new Dictionary<string, DataContext>();
  33:             CallContext.SetData(DataContextCacheKey, dataContextCache);
  34:         }
  35:         return dataContextCache;
  36:     }
  37:  
  38: }
  39:  
  40: /// <summary>
  41: /// Just return an empty dictionary because we are not supporting caching in this provider.
  42: /// </summary>
  43: public class DataContextNoCache : DataContextCacheBase
  44: {
  45:     /// <summary>
  46:     /// Creates the data context cache.
  47:     /// </summary>
  48:     protected override IDictionary<string, DataContext> GetDataContextCache()
  49:     {
  50:         return new Dictionary<string, DataContext>();
  51:     }
  52:  
  53:     /// <summary>
  54:     /// Gets the data context cache.
  55:     /// </summary>
  56:     public override IDictionary<string, DataContext> Items
  57:     {
  58:         get { return GetDataContextCache(); }
  59:     }
  60: }
  61:  
  62: public abstract class DataContextCacheBase : IDataContextCache
  63: {
  64:     protected const string DataContextCacheKey = "ApplicationDataContext";
  65:  
  66:     /// <summary>
  67:     /// Gets or sets the data context cache.
  68:     /// </summary>
  69:     /// <value>The data context cache.</value>
  70:     private IDictionary<string, DataContext> dataContextCache;
  71:  
  72:     /// <summary>
  73:     /// Creates the data context cache.
  74:     /// </summary>
  75:     protected abstract IDictionary<string, DataContext> GetDataContextCache();
  76:  
  77:     /// <summary>
  78:     /// Gets the data context cache.
  79:     /// </summary>
  80:     public virtual IDictionary<string, DataContext> Items
  81:     {
  82:         get
  83:         {
  84:             if (dataContextCache == null)
  85:             {
  86:                 dataContextCache = GetDataContextCache();
  87:             }
  88:             return dataContextCache;
  89:         }
  90:     }
  91:  
  92:     /// <summary>
  93:     /// Clears items (DataContexts) in the cache and disposes resources.
  94:     /// </summary>
  95:     public void Clear()
  96:     {
  97:         // Dispose managed resources.
  98:         if (dataContextCache != null)
  99:         {
 100:             foreach (var item in dataContextCache)
 101:             {
 102:                 if (item.Value != null)
 103:                 {
 104:                     item.Value.Dispose();
 105:                 }
 106:             }
 107:             dataContextCache.Clear();
 108:         }
 109:     }
 110:  
 111: }

Refreshing

You may remember that towards the beginning, I mentioned the need to clear the cached contexts to prevent data staleness.  I also mentioned previously that each cache strategy exposes a Clear method.  To clear all currently cached DataContexts, the DataContextProvider exposes a Refresh method.  This method simply iterates all the registered DataContexts and gets the associated caching strategy using the same CreateDataContextCache method that the GetDataContext method used.  Once the caching strategy is obtained, it’s Clear method is called to clear out all existing DataContexts.

   1: /// <summary>
   2: /// Refreshes the data contexts in the current application scope.
   3: /// </summary>
   4: public static void RefreshDataContexts()
   5: {
   6:     foreach (var item in contextRegistry.Values)
   7:     {
   8:         var dataContextCache = CreateDataContextCache(item);
   9:         dataContextCache.Clear();
  10:     }
  11: }

Using the DataContextProvider

To use the provider, I would set up my repositories to accept an IDataContextProvider using constructor injection.  The repository can then request the DataContext.

   1: public class MemberRepository : IMemberRepository
   2: {
   3:     private readonly AppDataContext dataContext;
   4:  
   5:     public MemberRepository(IDataContextProvider dataContextProvider)
   6:     {
   7:         dataContext = dataContextProvider.GetDataContext<AppDataContext>();
   8:     }
   9:  
  10:     protected AppDataContext DataContext
  11:     {
  12:         get { return dataContext; }
  13:     }
  14:  
  15:     public IEnumerable<Member> GetMembersByCountry(string country)
  16:     {
  17:         return dataContext.Members.Where(m => m.Country == country).ToList();
  18:     }
  19: }

I now set up my DataContextProvider, basically at the same level that I’d set up my IOC container for the given project.  Notice that when I register the DataContext, I also register the caching strategy.

   1: public class ObjectRegistrar
   2: {
   3:     private static readonly UnityContainer container = new UnityContainer();
   4:  
   5:     public void RegisterDataContext()
   6:     {
   7:         DataContextProvider.RegisterDataContext<AppDataContext, DataContextThreadCache>();
   8:     }
   9:  
  10:     public void RegisterTypes()
  11:     {
  12:         container
  13:             .RegisterType<IDataContextProvider, DataContextProvider>()
  14:             .RegisterType<IMemberRepository, MemberRepository>();
  15:     }
  16: }

That’s it!  Any comments are appreciated.

kick it on DotNetKicks.com

Technorati Tags: ,,,

author: Eric Swann | posted @ Monday, April 06, 2009 10:44 PM | Feedback (4)

LINQ to SQL DataContext Provider


I’ve significantly altered the approach presented below in the following post.  However, the post below may still be helpful for further reference.

So I’ve been a user of NHibernate for a while on my main project at work.  I got a change to refactor some services related to the application, and decided to use LINQ to SQL as the ORM, mainly so I could do a little more LINQ and check out the features of LINQ to SQL in a little more detail.  One issue that seems to keep coming up among users is the lifetime of a DataContext.  This is much like a session in NHibernate, in which case I usually use the Session-Per-Request approach in which the session lifetime is controlled in an http module.  In this case however, I actually have a service which gets kicked off via a timer.  This is kind of my catch-all utility service for this application, and a lot of different little tasks are scheduled and kicked off at regular intervals by a central scheduler.  The tasks all follow the command pattern, so they each expose an Execute() method and a factory provides the correct ICommand to execute depending on parameters passed to the main program logic.  All of the commands are pretty short lived, so I decided to take a similar approach: Create a context per command execution.  In my case, I’m just dealing with one database, so this also means that transactions across multiple repositories used by a command should be easy to manage because all database interactions will occur on the same connection.  This doesn’t mean the the context will be created at the beginning of the command.  It will still be instantiated in a lazy fashion, it will just be available for the duration of the command unless it is specifically released.  In addition, a coworker needed similar functionality, but specified that they would like to specify a connection string rather than rely on the connection string in the config file.  In addition, he suggested that I introduce generics in some way to allow the creation of the correct context in reusable code.  I decided to call this class the DataContextProvider.  The idea is that all of my repositories will accept an IDataContextProvider to allow access to the correct context.

Registering a Data Context

The first concept I wanted was some way to register a data context.  I decided to create some static Register Data Context methods.  I suppose these don’t need to be static and I could have done fluent interface, but I don’t anticipate the registering of a bunch of contexts.  The code sample is demonstrated below:

   1: private static readonly object syncLock = new object();
   2: private static readonly IDictionary<string, DataContextInfo> contextRegistry = new Dictionary<string, DataContextInfo>();
   3:  
   4: /// <summary>
   5: /// Registers the data context.
   6: /// </summary>
   7: /// <typeparam name="T">Type of the datacontext to register.</typeparam>
   8: /// <param name="contextKey">The context key to uniquely identify this context.</param>
   9: /// <param name="connectionString">The connection string to use with the context.</param>
  10: public static void RegisterDataContext<T>(string contextKey, string connectionString) where T : DataContext, new()
  11: {
  12:     lock (syncLock)
  13:     {
  14:         contextRegistry[GetContextInfoKey<T>(contextKey)] = new DataContextInfo
  15:                                                                 {
  16:                                                                     ConnectionString = connectionString,
  17:                                                                     ContextKey = contextKey,
  18:                                                                     Type = typeof (T)
  19:                                                                 };
  20:     }
  21: }
  22:  
  23: /// <summary>
  24: /// Registers the data context.
  25: /// </summary>
  26: /// <typeparam name="T">Type of the datacontext to register.</typeparam>
  27: /// <param name="contextKey">The context key to uniquely identify this context.</param>
  28: public static void RegisterDataContext<T>(string contextKey) where T : DataContext, new()
  29: {
  30:     RegisterDataContext<T>(contextKey, null);
  31: }
  32:  
  33: /// <summary>
  34: /// Registers the data context.
  35: /// </summary>
  36: /// <typeparam name="T">Type of the datacontext to register.</typeparam>
  37: public static void RegisterDataContext<T>() where T : DataContext, new()
  38: {
  39:     RegisterDataContext<T>(null, null);
  40: }
  41:  
  42: /// <summary>
  43: /// Gets the context info key based on the contextKey and the DataContext type.
  44: /// Used when registering and accessing a Data Context.
  45: /// </summary>
  46: /// <typeparam name="T">DataContext type.</typeparam>
  47: /// <param name="contextKey">The context key.</param>
  48: /// <returns>Key to be used when retrieving the data context.</returns>
  49: protected static string GetContextInfoKey<T>(string contextKey)
  50: {
  51:     return typeof(T).Name + (contextKey ?? "");
  52: }
  53:  
  54:  
  55: /// <summary>
  56: /// Holds information on the data context
  57: /// </summary>
  58: private class DataContextInfo
  59: {
  60:     public string ContextKey { get; set; }
  61:     public string ConnectionString { get; set; }
  62:     public Type Type { get; set; }
  63: }

The RegisterDataContext method has a few overloads which all feed into the main version.  This takes a contextKey (optional) and a connectionString (optional).  The contextKey is only needed if there will be multiple versions of the same context registered.  The connectionString is necessary if the user wishes to use a specific connectionString, rather than the string provided in the config file.  All this method does is register the information regarding the data context.  It stores it by key composed of the type name and the contextKey into a static dictionary so that it may be accessed throughout the lifetime of the application.  The application would register the data context whenever it was convenient; typically on the startup of the application.  The lock statement is pretty basic, but again, I don’t expect a lot of contention registering contexts.

Retrieving a Data Context

When a data context is actually required, the GetDataContext methods are called.  There are two overloads available:  One which takes the type and another which accepts the type and contextKey. 

   1: /// <summary>
   2: /// Gets the data context.
   3: /// </summary>
   4: /// <typeparam name="T">Type of the data context to retrieve.</typeparam>
   5: /// <returns>The data context.</returns>
   6: public T GetDataContext<T>() where T : DataContext, new()
   7: {
   8:     return GetDataContext<T>(null);
   9: }
  10:  
  11: /// <summary>
  12: /// Gets the data context.
  13: /// </summary>
  14: /// <typeparam name="T">Type of the data context to retrieve.</typeparam>
  15: /// <param name="contextKey">The context key to uniquely identify the context.</param>
  16: /// <returns>The data context.</returns>
  17: public T GetDataContext<T>(string contextKey) where T : DataContext, new()
  18: {
  19:     var contextInfoKey = GetContextInfoKey<T>(contextKey);
  20:  
  21:     var dataContext = RetrieveDataContextFromCache<T>(contextInfoKey) ??
  22:                       CreateAndCacheDataContext<T>(contextInfoKey);
  23:  
  24:     return dataContext;
  25: }
  26:  
  27:  
  28: /// <summary>
  29: /// Application should access the datacontextcache in this manner, don't use the private variable
  30: /// which is for local optimization only.
  31: /// </summary>
  32: protected Dictionary<string, DataContext> DataContextCache
  33: {
  34:     get
  35:     {
  36:         if (dataContextCache == null)
  37:         {
  38:             if (IsWebApplication)
  39:             {
  40:                 dataContextCache = IsWebApplication
  41:                                    ? (Dictionary<string, DataContext>)HttpContext.Current.Items[dataContextCacheKey]
  42:                                    : (Dictionary<string, DataContext>)CallContext.GetData(dataContextCacheKey);
  43:             }
  44:             if (dataContextCache == null)
  45:             {
  46:                 dataContextCache = new Dictionary<string, DataContext>();
  47:                 if (IsWebApplication)
  48:                     HttpContext.Current.Items[dataContextCacheKey] = dataContextCache;
  49:                 else
  50:                     CallContext.SetData(dataContextCacheKey, dataContextCache);
  51:             }
  52:  
  53:         }
  54:         return dataContextCache;
  55:     }
  56: }
  57:  
  58:  
  59: /// <summary>
  60: /// Retrieves the data context from cache.
  61: /// </summary>
  62: /// <typeparam name="T">Type of the data context.</typeparam>
  63: /// <param name="contextInfoKey">The context info key.</param>
  64: /// <returns>Data context if already cached.</returns>
  65: protected T RetrieveDataContextFromCache<T>(string contextInfoKey)
  66: {
  67:     object contextObject = DataContextCache[contextInfoKey];
  68:  
  69:     if (contextObject != null)
  70:     {
  71:         return (T)contextObject;
  72:     }
  73:     return default(T);
  74: }
  75:  
  76: /// <summary>
  77: /// Creates and caches the data context.
  78: /// </summary>
  79: /// <typeparam name="T">Type of the data context.</typeparam>
  80: /// <param name="contextInfoKey">The context info key.</param>
  81: /// <returns>A new DataContext of the type requested.</returns>
  82: protected T CreateAndCacheDataContext<T>(string contextInfoKey) where T : DataContext
  83: {
  84:     var contextInfo = contextRegistry[contextInfoKey];
  85:  
  86:     if(contextInfo == null)
  87:         throw new ArgumentException("DataContext was not registered with the application.");
  88:  
  89:     if(contextInfo.Type != typeof(T))
  90:         throw new InvalidOperationException("Context is registered, but it's type does not match the type requested.");
  91:  
  92:     var dataContext = string.IsNullOrEmpty(contextInfo.ConnectionString)
  93:         ? (T)Activator.CreateInstance(typeof(T))
  94:         : (T)Activator.CreateInstance(typeof(T), new object[] { contextInfo.ConnectionString });
  95:  
  96:     DataContextCache[contextInfoKey] = dataContext;
  97:  
  98:     return dataContext;
  99: }
 100:  
 101:  
 102: private static bool IsWebApplication
 103: {
 104:     get { return HttpContext.Current != null; }
 105: }
 106:  
 107: /// <summary>
 108: /// Gets the context info key based on the contextKey and the DataContext type.
 109: /// Used when registering and accessing a Data Context.
 110: /// </summary>
 111: /// <typeparam name="T">DataContext type.</typeparam>
 112: /// <param name="contextKey">The context key.</param>
 113: /// <returns>Key to be used when retrieving the data context.</returns>
 114: protected static string GetContextInfoKey<T>(string contextKey)
 115: {
 116:     return typeof(T).Name + (contextKey ?? "");
 117: }

This code essentially looks up the context info from the registered entries previously entered.  If it finds the context entry and then attempts to access the data context. The data context is accessed using RetrieveDataContextFromCache().  If the data context can’t be found in the local cache, a new data context is created and put in the local cache using CreateAndCacheDataContext().  If it’s a web application, it uses the HttpContext.Current to cache the data context, otherwise it caches it in the thread’s CallContext.  This approach basically limits the scope of this context to the current request for web applications or the current thread for non-web apps.

Repository Example

The repository then just accepts an IDataContextProvider (only exposes the GetDataContext methods) in the constructor.  This provides the context to the repository for query operations.  Could use an IOC container to facilitate this.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4:  
   5: namespace Test.Repositories
   6: {
   7:     public class TestRepository
   8:     {
   9:         private readonly AppDataContext dataContext;
  10:  
  11:         public ChargeImportRepository(IDataContextProvider dataContextProvider)
  12:         {
  13:             dataContext = dataContextProvider.GetDataContext<AppDataContext>();
  14:         }
  15:  
  16:  
  17:         public List<Thang> GetImportNotificationCards()
  18:         {
  19:             return (from thang in dataContext.Thangs
  20:                     where thang.Active && thang.User.Active
  21:                     select thang).Distinct().ToList();
  22:         }
  23:  
  24:  
  25:     }
  26: }

That’s it in a nutshell.  If anybody sees major issue with this, please comment and let me know what you think.

 

Technorati Tags: ,,,

author: Eric Swann | posted @ Monday, March 16, 2009 10:56 PM | Feedback (1)

Mindscape LightSpeed 2.1


My favorite new ORM, MindScape's LightSpeed, has released their 2.1 version.  I love this little ORM.  It's fun and friendly to use like Linq 2 SQL, but has a ton of little productivity enhancements that make rapid developers happy.  Some of the things I like about this ORM:

  • Designer Support - Nice little designer much like Linq 2 SQL.  I can go without a designer, and I do quite often with NHibernate, but it's so nice when explaining things to other visual people to just pull up the designer and say "look...right here!".
  • Linq support.  I haven't tested the limits of their support, but it's worked for me so far.
  • You don't have to use the designer.  You can create your classes yourself or use the command line tool.
  • Unlike Linq 2 SQL, it makes an effort to propagate design changes between the model and the database.  I believe this support is always going to be difficult since changes can be radical when refactoring, but for little changes this is nice.
  • It's got nice features such as built in soft delete, and date created and updated time stamping, optimistic locking column.  It handles this stuff for you if you follow their conventions!
  • It's got a wide variety of approaches to ID creation: Guid, Guid.Comb, Keytable, Identity etc....and they are very easy to set and use.
  • Entity validation rules are built into the system, kind of like Castle ActiveRecord.
  • It connects to a wide range of databases, large and small.
  • It's got built in support for Lucene.Net (full text search) and Memcached (caching).
  • It's got single table inheritance and an interesting and easy many to many approach that I really like.
  • I've gotten great responses from the guys at MindScape, I suggested adding Guid.Comb to the product and it was available in the next nightly build.
  • Nightly builds.
  • Did I mention that you can use it for free for very small projects, and otherwise it's incredibly cheap?

There are a couple of things I don't like about LightSpeed, but I can live with these.  Entities derive from their base class.  Of course you can insert your own base class in between, but some purists really hate having to follow that approach.  I've accepted it as a limitation and I tend to take a compositional approach to functionality that needs to be shared between domain objects anyway, so although the snob in me wants to ding them on that, I find that the benefits far outweigh this concern.  In addition, 2.1 allows code creation to be driven from custom templates, so perhaps a clever individual can create alternate patterns.  There isn't currently support for stored procs, but sounds like this is coming soon.  Now with Linq, I tend to use stored procs much less often, but it could be important for backward compatibility and for highly tuned queries.

Version 2.1 includes some nice new features and fixes...but I'll reference rather than repeat.

Technorati Tags: ,,

author: Eric Swann | posted @ Saturday, October 18, 2008 9:52 AM | Feedback (0)