»
S
I
D
E
B
A
R
«
Too Easy: Instant Data Access Layer with SubSonic and SQLite
Oct 20th, 2009 by Josh Rivers

It’s frightening just how much pain I have in starting a new application project. Creating an SQL database, deploying a schema, building an ORM access layer, and putting all the pieces into source control to deploy, version, update that database, both on the development SQL Express, and the production SQL Server instances—it takes time. It takes work. The only thing worse than putting in all that effort is doing the cowboy bit with the tools and NOT doing it. Then you have various unsynchronized, untestable, messy databases and deployed versions of code. Oook!

SubSonic 3.0 introduced the SimpleRepository and Auto-Migrations. These Ruby-on-Rails-inspired features, when coupled with SQLite can make your DAL-creation tasks just disappear. Let’s give it a try!

  1. Download and install System.Data.SQLite. This distribution of SQLite is a single mixed-mode dll that contains a recent SQLite build as well as ADO.NET bindings for the database. Also in the package is a linq provider, and Visual Studio Server Explorer support. image
  2. Download SubSonic 3. There’s all sorts of great stuff in this download. Three different data access models. Modifiable templates. Examples. But you only need the single dll from the binaries folder. Toss the rest in a drawer and look at it later.
  3. Add some the references to your project.
    image 
  4. Create a RepositoryFactory class:
    public class SubsonicRepositoryFactory{    public static SimpleRepository GetRepository()    {        var provider = SubSonic.DataProviders.ProviderFactory.GetProvider("Data Source=|DataDirectory|my.db", "System.Data.SQLite");        var repository = new SimpleRepository(provider, SimpleRepositoryOptions.RunMigrations);        return repository;    }}

    This isn’t strictly necessary…but I hate putting my connection strings in all those angle brackets, and prefer to detect my production/test/development environments in code rather than in XML.

  5. You’re done! Make your own POCOs and use commands like SubsonicRepositoryFactory.GetRepository().Add(myObject);
  6. Go home. You’ve worked hard. Relax.

This is good stuff, and it certainly can take some of the pressure off of starting up a new project. With data access and persistence just ‘done’, you can get to logic and value quicker. And you always have the option of upgrading to a more ‘serious’ data layer if you need it later. I’m really curious if SubSonic/SQLite can provide a reasonably performant small-to-midsize backing store to a web service application server. If your applications and various UIs all access their data through the web services, who cares what the persistence storage is, and until that back end is doing enough work to require multiple servers, how much trouble can you get into by caching your objects in memory and persisting them to disk using SQLite?

It’s also worth noting that SimpleRepository doesn’t have any wiring for child collections yet. Rob has said on StackOverflow that he intends to do some work on that, but it’s not in there yet. However, it shouldn’t be too hard to drop some convenience methods on your POCOs (extension methods if you’re worried about polluting your classes) to emit Lists or IEnumerables of linked collections.

Don’t plug your Clear Wireless Modem into your Macintosh
Oct 20th, 2009 by Josh Rivers

Update: The most recent versions of the drivers may have solved these problems. Works for me now.

WARNING! If you plug your Clear Wireless modem into a Macintosh computer with Clear’s driver installed, the modem will be flashed with a firmware that makes it incompatible with Windows. This firmware flash is not reversible. You will need to return your modem to Clear to get a new Windows-compatible one. More details below.

I got connected with Clear Wireless in Portland 4 days ago. So far I’m very happy with the bandwidth. Connected to my laptop, I’m getting 3mbs down and 200kbps up speeds. For mobile, unlimited internet, this is fantastic. Even better, I’m getting 100-160ms packet latencies, which is about one-third of what I was getting on my cellular wireless modem.

photo(2)

I also purchased the Clear Spot WiFi/WWAN router. This little device is really cool, letting me share my WIMAX connection with other computers, my iPhone, or whatever. Even better, it lets me use the WIMAX modem without installing drivers. This is great because the drivers for WWAN modems are terrible….and Clear doesn’t have average quality drivers.

photo

Windows 7 Drivers

Ok. So there aren’t Windows 7 drivers. Yet. The folks at Clear have told me I can expect to see them on Thursday, when the official launch date happens. In the meantime, install the connection manager from your CD or Driver Thumb Drive(in the box), and do a little googling for instructions on installing the older version of the drivers. Scott Hanselman wrote up something on this. You can also download the 64-bit driver from here for a couple months.

Also, be sure not to unplug the Modem before using the ‘Safely Eject’ feature of Windows….I get a BSOD every time.

The Win7 drivers work. They aren’t shiny or easy to install, but they work. That’s much better than the…

Mac OS X Drivers

When Clear says they support Mac OS X, they are…well…reaching. Their driver is marked ‘Beta’ and it shows. After the install, the Connection Manager will spend 5 minutes or so just sitting there. No status message, and a red light on the modem. Is it working? Is it doing something? Who knows. After that, it will connect, and you’re set to go. Same good service.

Unless.

If you are running Snow Leopard (Mac OS X 10.6) you get nothing. No error message. No indication of failure. No connection. It just doesn’t work. Saying Windows 7 isn’t released yet is technically true, and I understand why those drivers aren’t out. OS X 10.6 has been out since August 28th. It seems foolish that an early-adopter service like Clear doesn’t support an early-adopter OS. That’s how it is, though. Clear people have told me unofficially that we should see a 10.6 driver ‘by the end of the month’. We’ll see.

You can’t go back again

The reason for the 5 minute wait when you install your Mac drivers is apparently that the drivers are installing a fresh firmware on your modem. As I stated at the top, this firmware isn’t compatible with the Windows drivers. Nor does Clear have a flash utility available that can downgrade it. You have to return your modem for a replacement. Clear has told me that this should be free, since it’s “Their Fault”. Be nice when you call, and they should hook you up.

WiFi to the rescue

Luckily, the Clear Spot can still connect to the modem with it’s new, corrupted, Mac OS (10.5 or earlier)-only firmware. I really like the driver-less mobile internet experience. No need for the hassle of incomplete, incompatible drivers or connection managers. Just Windows 7’s shiny new WiFi connection tool and fast internets.

The Clear Spot (Cradlepoint PHS-300) is a great router, works with non-Clear broadband modems, and is $50 cheaper when you get it from Clear. The battery seems to last a bit more than 3 hours with the network in use. You can get extra batteries and a car charger from Clear, but I think an external Tekkeon battery is probably a better idea. When you get it from 3Gstore, it comes with a USB cable that I’m hoping will let me charge my Clear Spot from my laptop. One less wall wart in my kit bag.

There is a slight glitch. Most of the pictures you’ll see of the Clear Spot will show it with the modem plugged right into it. It turns out this is a bad idea. WiFi and WiMax radios seem to interfere. Make sure you leave the router on Channel 1. I also didn’t get anything approaching full speeds until I used a USB extension cable to put some space between the two devices. Once I did that, speeds were right up there.

Good support experience

Clear’s service is definitely in early-adopter mode, but I had a really excellent experience both with the store staff, and with the folks at technical support. It took me 20 minutes on the phone to figure out my Mac/PC Firmware problem, but the rep was patient and eager to help, and that is an excellent experience these days.

I like my Clear service and I recommend it if you want some better mobile bandwidth.

Adding MvcContrib SubControllers to your ASP.NET MVC Project
Aug 3rd, 2009 by Josh Rivers

After a little bit of work with MVC, you get infected with the spirit of clean code
and begin to desire even more ways of eliminating repetition. You’ve got partials
and html helpers. Still you are hungry. SubControllers are the dish that will fill
you up.

Why SubControllers?

There’s a design decision here. The question is why are we designing with subcontrollers?
To understand the rationale, let’s look at the qualities of the various sub-view options.

  • Partial Views – partial views are probably the most useful method for re-using view
    output. They are very easy to set up, and are very versatile. However, they don’t
    contain logic. They are intended entirely as a slave to the Action Controller. They
    get their model from the controller and simply render it. This means that the controller
    needs to know and provide everything the partial view needs. Not good for something
    like a login status control, which is an separate concern from most controllers.
  • HTML Helpers – HTML helpers get used a lot, and they are very helpful for creating
    your own ‘controls’ to use in your pages. They do not, however, support using a view
    template. This means you’ve got to create them and test them with tests for emitted
    markup. This adds a lot of complexity if you are trying to do something more than
    create a html rendering function. Not a good place to insert complex view logic, like
    a shopping cart status widget, or anything with a table or list. The HTML helper also
    still gets it’s data from the master view/controller, so it fails at separating concerns.
  • Html.RenderAction() – RenderAction is the once and future solution to a number of
    problems. Or so I hear. I have trouble getting excited about using it right now. Currently,
    it is not baked into MVC, but rather is in the separate ‘MVC Futures’ assembly, where
    it is unsupported. Rumor is it is buggy and unsecure. In the future it may be changed
    or dropped or renamed or anything. ScottGu has promised in a blog comment that it
    is due for inclusion in MVC 2. However, I don’t program to unreleased Microsoft products.
    Maybe later, but for now…
  • MvcContrib SubController – Jeff Palermo implemented subcontrollers for MvcContrib
    to give us a working solution—now—for reusable view/controller code. Subcontrollers
    have their own model, ViewData, are nestable, and can use view templates for their
    output. If you have a job that goes beyond a partial view, or a html helper, SubControllers
    are a peach.

Setting Up Your Project

1) Add a reference to MvcContrib.

2) Create a class called StructureMapSubControllerBinder. This is only required if
you’re using StructureMap to do your IOC for you. You can use the base SubControllerBinder
from MvcContrib, or create your own version for your IOC tool.

public class StructureMapSubControllerBinder
: SubControllerBinder
{
public override object CreateSubController(Type
destinationType)
{
object instance = ObjectFactory.GetInstance(destinationType);
if (instance == null)
{
throw new InvalidOperationException(destinationType
+ " not registered with StructureMap");
}

return instance;
}
}

 

3) Make the SubControllerBinder your default binder.

ModelBinders.Binders.DefaultBinder = new StructureMapSubControllerBinder();
4) Add a reference to the MvcContrib namespace to the Pages section of Web.Config.
This will save you putting a lot of namespace tags in your views.
<add namespace="MvcContrib"/>

Creating a SubController

1) Create a ~/Controllers/SubControllers folder. This is completely optional. If you
have a bigger project you might want to make multiple subcontrollers folders for different
areas.

2) Create a SubController class. The action needs to have the ‘same’ name as the class.
It also subclasses from SubController.

using System.Web.Mvc;
using MvcContrib;

namespace NWIS.Business.Web.Controllers.SubControllers
{
public class DemoSubController
: SubController
{
public ViewResult Demo()
{
return View();
}
}
}

3) Create a View subfolder. Using the ‘Add View’ context menu from the controller
won’t work because the ‘sub’ in the class name. Just create the folder yourself. ~/Views/Demo.

4) Create a View. Right click on the ~/Views/Demo folder. Select Add->View. Name
the view ‘Demo’. Make it a partial view (.ascx). Go ahead an add some markup to the
view. Whatever you like.

Using the SubController in your View

1) Add an attribute to your Controller class.

[SubControllerActionToViewDataAttribute]

2) Add the subcontroller as a parameter to the action you want to use it in.

public ViewResult
Index(DemoSubController mySubCont)

3) Place the subcontroller output into your view.

<% ViewData.Get<Action>("mySubCont").Invoke();
%>
The name you use here is the name of the parameter to the controller action.
 
That should be it. You subcontroller can output into your view, and you can use
it in many, many controllers and actions. You can add dependencies directly to the
subcontroller in it’s constructor. You can also pass information from the action to
the subcontroller using properties.

Resources

The Best of Code Syndication
Aug 2nd, 2009 by Josh Rivers

I was recently asked about who I learn from, so I threw together this list of good
places to get new ideas. Most of these are the RSS Links, not the web sites. I use
Google Reader to make sure I keep up.

Top 3

.NET

General

Security

Funny

Podcasts

Using StructureMap to Build Your MVC Controllers
Jul 26th, 2009 by Josh Rivers

The simplest things seem to be the hardest to find the up-to-date documentation on.
Adding a ControllerFactory to MVC is very simple, and making it work with StructureMap
is pretty quick, but do you really want to write any of that code? Of course not.
Somebody else can do it.

The MVC Contrib project has built all of the code you need to make a number of the
popular IoC frameworks work in your project. Unfortunately IoC frameworks update a
lot, and the MvcContrib project was finding it pretty hard to keep up with the latest
versions when they compiled their builds. So that code is deprecated. Don’t use it.

Here’s how you add StructureMap to build controllers in your MVC project:

Create a StructureMapControllerFactory Class

using System;
using System.Web.Mvc;
using System.Web.Routing;
using StructureMap;

namespace MvcContrib.StructureMap
{
public class StructureMapControllerFactory
: DefaultControllerFactory
{
public override IController
CreateController(RequestContext context, string controllerName)
{
Type controllerType = base.GetControllerType(controllerName);
return ObjectFactory.GetInstance(controllerType) as IController;
}
}
}

Add a Method to Global.asax

private void ConfigureIoC()
{
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
}

Add a call to that method in Application_Start

ConfigureIoC();

….and now we can go out for steak and exotic dancers. Your controllers will have their
dependencies injected all nice and purty.

StructureMap: IRepository and Test Data Injection
Jul 26th, 2009 by Josh Rivers

I Love StructureMap! It’s wonderful. What a way to compose your code together easily,
precisely, and consolidatedly (!!). When I put a sentence like that together, I wonder
if I even know what I’m talking about. The problem with StructureMap, IoC, and dependency
injection really seems to be that the jargon for the patterns is so manifestly
true
that once you learn what the hell you are doing, you are completely
unable to stop talking about it in shorthand. And that shorthand makes absolutely
no sense to someone who hasn’t absorbed the patterns. Keep plugging, people. Once
you do it, you’ll get it. Then you’ll be there and not be able to
explain to other people why you’re so right. It’s like being Tom Cruise and needing
to explain scientology.

Anyways.

I learned two things today. The first is how to hook all of my concrete generic repository
types together with their interfaces. I had been adding a line of configuration for
each repository. Now my test code looks like this:

ForRequestedType(typeof(IRepository<>)).TheDefaultIsConcreteType(typeof(ListRepository<>));

And my production code:

ForRequestedType(typeof(IRepository<>)).TheDefaultIsConcreteType(typeof(LlblRepository<>));

That’s easy!

The other thing I learned is that I can happily and easily inject data into my object
registry for testing purposes. I have an in-memory repository implementation built.
All it needs is data.

public class MemoryDataSource
{
private Dictionary<Type, IQueryable> data;

public MemoryDataSource(Dictionary<Type, IQueryable>
data)
{
this.data = data;
}

public IQueryable GetQueryable(Type type)
{
return this.data[type];
}
}
public class ListRepository<T>
: IRepository<T>
{
private MemoryDataSource source;

public ListRepository(MemoryDataSource source)
{
this.source = source;
}

public IQueryable<T> GetSource()
{
return ((IQueryable<T>)source.GetQueryable(typeof(T)));
}

public void SaveEntity(T
entity)
{
return;
}
}

Now all I need to do is build a Dictionary<> keyed on the entity object type
and fill it up with data. Once I’ve done that, I just pass it into the StructureMap
registry like this:

ObjectFactory.Inject<Dictionary<Type, IQueryable>>(dataSource);

Now I can have ObjectFactory construct my object under test and it’s got just the
data I need it to have.

Adding MVC to an existing ASP.NET Application
Jul 19th, 2009 by Josh Rivers

ASP.NET MVC Has added a very useful new programming model to developing websites in
.NET. There is hype and debate all throughout the interwebs on how great MVC is. And
it all can be yours, with nothing but a ‘File->New’ in Visual Studio….unless you
already have an ASP.NET application. In which case, you need to do a bit more work.

  1. Add project references to:

    System.Web.Abstractions

    System.Web.Mvc

    System.Web.Routing

    image
  2. Create /Controllers, /Views, and /Views/Shared folders in your project

    image
  3. Update web.config

    <?xml version="1.0" encoding="utf-8" ?>
    
    <configuration>
    
    <system.web>
    
    <pages>
    
    <namespaces>
    
    <add namespace="System.Web.Mvc"/>
    
    <add namespace="System.Web.Mvc.Ajax"/>
    
    <add namespace="System.Web.Mvc.Html" />
    
    <add namespace="System.Web.Routing"/>
    
    <add namespace="System.Linq"/>
    
    <add namespace="System.Collections.Generic"/>
    
    </namespaces>
    
    </pages>
    
    <compilation>
    
    <assemblies>
    
    <add assembly="System.Core,
    Version=3.5.0.0,
    Culture=neutral,
    PublicKeyToken=B77A5C561934E089"/>
    
    <add assembly="System.Web.Mvc,
    Version=1.0.0.0,
    Culture=neutral,
    PublicKeyToken=31bf3856ad364e35" />
    
    <add assembly="System.Web.Abstractions,
    Version=3.5.0.0,
    Culture=neutral,
    PublicKeyToken=31BF3856AD364E35"/>
    
    <add assembly="System.Web.Routing,
    Version=3.5.0.0,
    Culture=neutral,
    PublicKeyToken=31BF3856AD364E35"/>
    
    </assemblies>
    
    </compilation>
    
    <httpModules>
    
    <add name="UrlRoutingModule"
    
    type="System.Web.Routing.UrlRoutingModule,
    System.Web.Routing,
    Version=3.5.0.0,
    Culture=neutral,
    PublicKeyToken=31BF3856AD364E35" />
    
    </httpModules>
    
    </system.web>
    
    <system.webServer>
    
    <validation validateIntegratedModeConfiguration="false"/>
    
    <modules runAllManagedModulesForAllRequests="true">
    
    <remove name="ScriptModule" />
    
    <remove name="UrlRoutingModule" />
    
    <add name="ScriptModule" preCondition="managedHandler"
    
    type="System.Web.Handlers.ScriptModule,
    
    System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
    
    PublicKeyToken=31BF3856AD364E35"/>
    
    <add name="UrlRoutingModule"
    
    type="System.Web.Routing.UrlRoutingModule,
    System.Web.Routing,
    
    Version=3.5.0.0, Culture=neutral,
    
    PublicKeyToken=31BF3856AD364E35" />
    
    </modules>
    
    <handlers>
    
    <remove name="WebServiceHandlerFactory-Integrated"/>
    
    <remove name="ScriptHandlerFactory" />
    
    <remove name="ScriptHandlerFactoryAppServices" />
    
    <remove name="ScriptResource" />
    
    <remove name="MvcHttpHandler" />
    
    <remove name="UrlRoutingHandler" />
    
    <add name="ScriptHandlerFactory" verb="*" path="*.asmx"
    
    preCondition="integratedMode"
    
    type="System.Web.Script.Services.ScriptHandlerFactory,
    
    System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
    
    PublicKeyToken=31BF3856AD364E35"/>
    
    <add name="ScriptHandlerFactoryAppServices" verb="*"
    
    path="*_AppService.axd" preCondition="integratedMode"
    
    type="System.Web.Script.Services.ScriptHandlerFactory,
    
    System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
    
    PublicKeyToken=31BF3856AD364E35"/>
    
    <add name="ScriptResource" preCondition="integratedMode"
    
    verb="GET,HEAD" path="ScriptResource.axd"
    
    type="System.Web.Handlers.ScriptResourceHandler,
    
    System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
    
    PublicKeyToken=31BF3856AD364E35" />
    
    <add name="MvcHttpHandler" preCondition="integratedMode"
    
    verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler,
    
    System.Web.Mvc, Version=1.0.0.0, Culture=neutral,
    
    PublicKeyToken=31BF3856AD364E35"/>
    
    <add name="UrlRoutingHandler"
    
    preCondition="integratedMode" verb="*" path="UrlRouting.axd"
    
    type="System.Web.HttpForbiddenHandler,
    System.Web,
    
    Version=2.0.0.0, Culture=neutral,
    
    PublicKeyToken=b03f5f7f11d50a3a" />
    
    </handlers>
    
    </system.webServer>
    
    </configuration>
  4. Add the following to Global.asax.cs (create a Global.asax if you don’t already have
    one)

    public static void RegisterRoutes(RouteCollection
    routes)
    {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
    
    routes.MapRoute(
    "Default", //
    Route name
    
    "{controller}/{action}/{id}", //
    URL with parameters
    
    new { controller = "Home",
    action = "Index", id = "" } //
    Parameter defaults
    
    );
    
    }
    
    protected void Application_Start()
    {
    RegisterRoutes(RouteTable.Routes);
    }
    

    The above is the “standard” routing for a MVC site. It will work, but it might cause
    you trouble if you want the root of your website to still go to a ‘default.aspx’.
    Try this line instead:

    routes.MapRoute(
    "Default",
    
    "MVC/{controller}/{action}/{id}",
    
    new { controller = "Home",
    action = "Index", id = "" } 
  5. Edit your .csproj file by hand

    <ProjectTypeGuids>{603c0e0b-db56-11dc-be95-000d561079b0};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    
    

    This will tell Visual Studio to act like this is an MVC project. You will get
    the context menu items for MVC in your Controllers and Views directories.

    Note: I’ve had trouble at this point with some Visual Studio installs.
    If you’re having trouble with “The project type is not supported by this installation.”
    messages here, it may be time for a clean install in a fresh VM.
  6. Set Up IIS. If you’re using IIS7, the System.Webserver settings in your Web.Config
    file should have taken care of this step. If you’re going to need to map the wildcard
    URL to aspnet_isapi.dll in order to get all of the MVC routing magic to work.

    image
  7. You might want to copy of the /Views/web.config file from an existing MVC project
    to prevent your views from being viewed directly, rather than through their controllers.
  8. You might also wish to create a /Scripts folder and copy over the contents of
    the /Scripts folder from native MVC project. It has the jquery and MS AJAX javascripts
    that you may be looking for if you’re following a MVC book or tutorial.

Boy howdy. That was a good chunk of work. But now you have the infinite pleasure of
developing in MVC, and your old web site should continue to work under you. Delicious
incremental development goodness.

Resources

Incorporating Growl For Windows into my Application
Jun 23rd, 2009 by Josh Rivers

I’m a big believer in re-use. Code I don’t aihave to write is code I don’t have to
debug or maintain. When I discovered that I’d really like some notification
toast
in my Windows Forms enterprise application, I immediately started looking
for libraries.

The definitive toast solution for Mac OS is Growl.
Growl is a separate application/control panel that runs on your system and manages
notifications from all subscribing services on your system. No more overlapping toast.
It also provides a lot of filtering, control, and plugin-based extensibility. Did
you want your ‘new mail’ notifications to show up on your cell phone as SMS rather
than on your desktop? Just do it. Want the toilet to flush when the build fails? Get
an Arduino and script it up!

Needless to say I was really chuffed to find the Growl
for Windows
project. Their application is still in beta, but the developers are
very responsive, and they are checking code fixes in within hours of getting bug reports
when they can. GfW can provide you notifications for your GMail, Outlook, Visual Studio
Builds, and the current song playing in Pandora.

Installing Growl With My Application

My application installs with NSIS. Adding
automatic installation of the Growl client to my installer was very quick. I grabbed
the .msi file from the Growl download, and put it within my build tree. Then I added
the following snippet to my NSIS script:

Section "Growl
Install" SEC01
File "Growl\Windows Deployment.msi"

ExecWait 'MSIEXEC.EXE /I "$INSTDIR\Windows Deployment.msi"
/QB- ALLUSERS=1'

SectionEnd

That’s it for the install. However, I wanted it to uninstall cleanly as well, so I
added the following line to the uninstall section:

ExecWait 'MSIEXEC.EXE
/x "$INSTDIR\Windows Deployment.msi" /qn'

Two caveats:

  1. You may not want to uninstall Growl automatically for the user, in case they had installed
    it on their own and want to uninstall your app, but keep Growl. This isn’t an issue
    in my business deployment, but it may be one for a public application.
  2. There may be some issues with UAC installs, and I haven’t tested this yet. I think
    I can just run my NSIS exe ‘As Administrator’ and have it work, but there may be issues
    with msiexec and UAC.

Code to Send Notifications

First things first, lets establish some interfaces to program to, that way we can
swap out implementations for testing and if our needs change.

public interface INotificationMessage
{
string MessageType { get; }
string MessageDescription { get; }
string MessageId { get; }
string MessageText { get; set; }
Image MessageIcon { get; }
Action AcceptMessageCallback { get; set; }
Action DeclineMessageCallback { get; set; }
}
public interface INotificationSender
{
void SendMessage(INotificationMessage message);
}

Next, we’ll implement a specific Notification Message type. Each Notification Message
type can be instantiated of a specific type so that it’s very easy to add new types
as well as very easy to use them. Each type can have an icon of it’s own.

public class ApplicationErrorNotificationMessage
: INotificationMessage
{
public string MessageType
{ get; private set; }
public string MessageDescription
{ get; private set; }
public string MessageId
{ get; private set; }
public string MessageText
{ get; set; }
public Image MessageIcon { get; private set;
}
public Action AcceptMessageCallback { get; set;
}
public Action DeclineMessageCallback { get; set;
}

public ApplicationErrorNotificationMessage()
{
this.MessageType = "APPLICATION_ERROR";
this.MessageDescription = "Application
Error";
this.MessageId = Guid.NewGuid().ToString();
this.MessageIcon = Application.Properties.Resources.AppIcon;
}
}

It appears that growl caches the message icons, so you might need to restart the Growl
process if you go changing your Notification icons.

Finally we have our implementation of INotificationSender which registers with Growl,
ensures that it is installed, and ensures that it is running.

public class GrowlNotificationSender
: INotificationSender
{
private GrowlConnector connector;
private Image applicationIcon;
private string applicationName;
private INotificationMessage[] messageTypes;
private Dictionary<string,
INotificationMessage> sentMessages;

public GrowlNotificationSender(INotificationMessage[]
messageTypes,
Image applicationIcon,
string applicationName)
{
this.connector = new GrowlConnector();
this.messageTypes = messageTypes;
this.applicationIcon = applicationIcon;
this.applicationName = applicationName;
this.sentMessages = new Dictionary<string,
INotificationMessage>();

EnsureGrowlIsRunning();
RegisterWithGrowl();
}

private void RegisterWithGrowl()
{
Application thisApplication = new Application(applicationName);
thisApplication.Icon = applicationIcon;
List<NotificationType> notificationTypes = new List<NotificationType>();
foreach (var messageType in messageTypes)
{
NotificationType notificationType =
new NotificationType(messageType.MessageType,
messageType.MessageDescription);
notificationType.Icon = messageType.MessageIcon;
notificationTypes.Add(notificationType);
}
this.connector.Register(thisApplication, notificationTypes.ToArray());
this.connector.NotificationCallback += new GrowlConnector.CallbackEventHandler(connector_NotificationCallback);
this.connector.EncryptionAlgorithm = Cryptography.SymmetricAlgorithmType.PlainText;
}

public void EnsureGrowlIsRunning()
{
Process[] processlist = Process.GetProcesses();
foreach (Process theprocess in processlist)
{
if (theprocess.ProcessName.Equals("Growl"))
{ return; }
}

Detector detector = new Growl.CoreLibrary.Detector();
if (detector.IsAvailable)
{
System.Diagnostics.Process.Start(detector.InstallationFolder + @"\Growl.exe");
Thread.Sleep(1000);
return;
}
else

{
throw new FileNotFoundException("Growl
not installed?");
}
}

void connector_NotificationCallback(Response response,
CallbackData callbackData)
{
if (sentMessages[callbackData.Data] != null)
{
if (callbackData.Result == CallbackResult.CLICK)
{
if (sentMessages[callbackData.Data].AcceptMessageCallback
!= null)
{
sentMessages[callbackData.Data].AcceptMessageCallback.Invoke();
}
}
else

{
if (sentMessages[callbackData.Data].DeclineMessageCallback
!= null)
{
sentMessages[callbackData.Data].DeclineMessageCallback.Invoke();
}
}
sentMessages.Remove(callbackData.Data);
}
}

public void SendMessage(INotificationMessage
message)
{
this.sentMessages.Add(message.MessageId, message);

CallbackContext callbackContext = new CallbackContext();
callbackContext.Data = message.MessageId;
callbackContext.Type = message.MessageType;

Notification notification = new Notification(this.applicationName,
message.MessageType,
message.MessageId,
message.MessageDescription,
message.MessageText);

EnsureGrowlIsRunning();
this.connector.Notify(notification, callbackContext);
}
}

This implementation keeps track of sent messages, and executes callbacks on them depending
on the user’s response. If you don’t want to use one of the callbacks, just leave
it undefined.

I haven’t found a way to get Growl to send the CLOSE message rather than the TIMEOUT,
but that’s ok, since I treat them the same.

Using The Code

You’d probably want to wire this up with your IOC container, but here’s a manual usage.

INotificationSender sender = new GrowlNotificationSender(
new[] { new ApplicationErrorNotificationMessage()
},
Moneta.Properties.Resources.Crystal_128_package_network,
"My Application"

);
Thread.Sleep(1000);
ApplicationErrorNotificationMessage message = new ApplicationErrorNotificationMessage
{
MessageText = "Hi There",
AcceptMessageCallback = () => MessageBox.Show("You
clicked"),
DeclineMessageCallback = () => MessageBox.Show("Pay
Attention!")
};
sender.SendMessage(message);

It’s worth noting that the first registration of your app may take a moment for Growl
to process, and that’s why I have the Sleep() in there. If your app doesn’t intend
to immediately send a message after it’s first registration, that’s unnecessary.

Resources

Windows Forms Eventing: Thread Synchronization
Jun 23rd, 2009 by Josh Rivers

So last time, we created this great event aggregator for our Windows Forms applications.
Instead of having the code that sends messages directly connected to the code that
receives messages, everybody just knows about the event aggregator. This works the
same way you don’t need turn-by-turn directions to Fox News Headquarters to mail them
a box of dirty diapers.

The catch is that the code so far only works if all the senders and receivers are
on the same thread. If they are on different threads, who knows what may happen? (Not
me. I failed out of Home-Ec. I don’t even know how you turn cotton flowers into threads,
let alone how to make them fit together.)

There is a simple fix, however. The .NET 2.0 SynchronizationContext is used by Windows
Forms and can provide an easy-to-use central choke point to manage all of our thread-to-thread
communications.

Remember Your Singleton

When I was cutting and pasting together this code from the Intarnets, one big problem
I had was forgetting to initialize my singleton correctly. It’s really important
that you initialize your singleton EventAggregator from SynchronizationContext.Current
in the Windows Forms Thread. Setting the thing up right in StructureMap or
your IOC Container of choice works just great. Just make sure it gets done. (My mistake
also involved letting StructureMap use “new SynchronizationContext()” in initializing
my singleton rather than “SynchronizationContext.Current”.) Get it right the first
time and you won’t have to do multithread debugging.

EventAggregator Class

public class EventAggregator
: IEventAggregator
{
private readonly SynchronizationContext
_context;
private readonly List<object>
_listeners = new List<object>();
private readonly object _locker
= new object();

public EventAggregator(SynchronizationContext
context)
{
_context = context;
}

#region IEventAggregator Members
public void SendMessage<T>(T
message)
{
sendAction(() => all().CallOnEach<IListener<T>>(x => { x.Handle(message);
}));
}

public void AddListener(object listener)
{
withinLock(() =>
{
if (_listeners.Contains(listener)) return;
_listeners.Add(listener);
});
}

public void RemoveListener(object listener)
{
withinLock(() => _listeners.Remove(listener));
}
#endregion

private object[]
all()
{
lock (_locker)
{
return _listeners.ToArray();
}
}

private void withinLock(Action
action)
{
lock (_locker)
{
action();
}
}

protected virtual void sendAction(Action
action)
{
_context.Send(state => { action(); }, null);
}
}

Notice the locking. Notice the creation of a copy of the _listeners list into an array
for Thread Safety. Most importantly, notice the use of _context.Send(). A few minor
changes…but now the whole class is thread safe, and synchronous between threads. Hooray!

Next Time

I’ve still got lots to cover on this subject. How do you use the eventing in your
application architecture. Using Weak References to protect your event system from
memory leaks. Sending events to a specific target or set of targets. I’m going to
be working on some other projects for a bit, so it may be a while before I write those
posts, but hopefully what we’ve covered so far is useful on its own until then.

Resources

Design Principles
Jun 23rd, 2009 by Josh Rivers

Digging out the Draft Bucket: This isn’t really a completed post, but rather than
leave it in my Draft folder forever, I’m just going to post it. That way it’s infamy
will live forever.

There’s a massive summary of design principles in the ASP.NET
MVC Forums project introduction
. The actual code in the project is opaque to me.
I’d need to learn the framework before I’d understand how he’s working on it.

Rocky on TDD, with an
explanation on how comprehensive testing means writing more test code than real code.
"No one actually does this." "I once watched a TDD (and MVC/MVP) presentation.
The speaker wrote several pages of code to build and test a presenter that did a bunch
of work. Nice stuff, until you realized that all that could have been done in 1-3
lines of code using data binding. I asked him why he did this rather than using data
binding. The answer: you can’t test data binding. I’m afraid my jaw dropped. See,
I have a wife and kids. I like to get home and spend time with them. If I can write
3 lines of code, or write 3 pages of code that I need to test and debug, I’m going
to pick the 3 lines of code every time"

Authorization system in
CSLA
.

Polymorphic MV*.

For when I need some new article materials: A
Big Hanselman Article on Code Learning Online
.

Cohesion
and Coupling

Articles on SOLID:

  1. Single
    Responsibility Principle
  2. Open/Closed
    Principle
  3. Liskov
    Substitution Principle
»  Substance: WordPress   »  Style: Ahren Ahimsa