Flattening a Jagged Array with LINQ

Today I had to flatten a jagged array.  In my case, it was a string[][] and I needed to make sure every single string contained in that jagged array was set to something (non-null and non-empty).  LINQ made the flattening very easy.  In fact, I ended up making a generic version that I could use to flatten any type of jagged array (assuming it's a T[][]):

private static IEnumerable<T> Flatten<T>(IEnumerable<T[]> data)
{
    return from r in data from c in r select c;
}

Then, checking to make sure the data was valid, was easy:

var flattened = Flatten(data);
bool isValid = !flattened.Any(s => String.IsNullOrEmpty(s));

You could even use method grouping and reduce the validation to:

bool isValid = !flattened.Any(String.IsNullOrEmpty);
Technorati Tags: ,,

No Intellisense in Visual Studio 2010 RC1

Well, that's odd.  Why were the default options for Intellisense turned off when I did a fresh install of Visual Studio 2010 RC1?

image

Technorati Tags: ,

Lookups vs. Dictionaries

Donn Felker has a great post that explains the different uses of LINQ's ToLookup and ToDictionary.  Check it out!

Technorati Tags: ,
Posted by Patrick Steele's .NET Blog
Filed under: ,

Super-simple Object Mapper

If you need a full-featured object mapper with minimal set up, I recommend you take a look at AutoMapper on Codeplex.  If you need a quick-and-dirty solution, maybe the following code could help you out.

First off, why am I not using AutoMapper?  At my current client, there are strict rules as to the use of open source software.  There's a process in place for requesting the use of a particular open source tool, but with the red-tape of the approval process (reviews, signatures, justification, etc…), it could literally take 3 – 6 months.  It's just not worth it for what I need right now.  So I rolled my own.

This mapper is super-simple, not very smart and may have a bug or two in it, but it works for what I need it to do and reduces a lot of hand coding.  USE AT YOUR OWN RISK!

It uses two simple rules to map data between two objects:

  1. If a property name and type on the source match the name and type of a destination property, the value is copied.
  2. If the user has defined a custom mapping action, use that to copy data (but rule #1 is always executed first).

Let's dig into the details.

First, I set up a generic class that takes in a couple of types – my source and destination types.  I added a clause on the destination type that it must be 'new-able' so that I could provide a utility function that would create a destination object, map it's values from a source and return it to you.

public class Mapper<TSource, TDest> where TDest : new()
{
 
}

Copying Properties

When copying data from a source object to a destination object, we get all public instance properties of the destination and see if they have a matching (same name and same type) property on the source.  If so, we set the value on our destination object:

protected virtual void CopyMatchingProperties(TSource source, TDest dest)
{
    foreach(var destProp in typeof(TDest).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanWrite))
    {
        var sourceProp =
            typeof (TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.Name == destProp.Name && p.PropertyType == destProp.PropertyType).
                FirstOrDefault();
        if( sourceProp != null)
        {
            destProp.SetValue(dest, sourceProp.GetValue(source, null), null);
        }
    }
}

Custom Transformations

I want the ability to define my own transformation for special cases.  This is simply a list of Action<TSource, TDest> delegates:

protected readonly IList<Action<TSource, TDest>> mappings = new List<Action<TSource, TDest>>();
 
public virtual void AddMapping(Action<TSource, TDest> mapping)
{
    mappings.Add(mapping);
}

Again, simple yet functional.

Perform Mappings

The last thing we need is a couple of methods to execute the actual mapping:

public virtual TDest MapObject(TSource source, TDest dest)
{
    CopyMatchingProperties(source, dest);
    foreach(var action in mappings)
    {
        action(source, dest);
    }
 
    return dest;
}
 
public virtual TDest CreateMappedObject(TSource source)
{
    TDest dest = new TDest();
    return MapObject(source, dest);
}

You'll see that "CreatedMappedObject" was the reason we needed to have the new-able clause on the TDest generic parameter.

Usage

Now let's put this into action!  Given a simple domain object and view model:

public class DomainObject
{
    public string Name { get; set; }
    public DateTime DOB { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
}
 
public class ViewModel
{
    public string Name { get; set; }
    public int Age { get; set; }
}

As you can see, our view model only needs the Name and Age.  Our mapping code looks like this:

var mapper = new Mapper<DomainObject, ViewModel>();
var viewModel = mapper.CreateMappedObject(domainObject);

if the view model is created somewhere else and pre-populated with other data, we would use the MapObject method instead of creating a new instance of ViewModel:

var mapper = new Mapper<DomainObject, ViewModel>();
var viewModel = InitializeViewModel();
viewModel = mapper.MapObject(domainObject, viewModel);

Now let's assume we want to add the user's birth year to the view:

public class ViewModel
{
    public string Name { get; set; }
    public int Age { get; set; }
    public int BirthYear { get; set; }
}

Yes, we could pass along the entire date of birth, but this way the view model is getting only what it needs and doesn't need to do any additional processing to get the year:

var mapper = new Mapper<DomainObject, ViewModel>();
mapper.AddMapping((source,dest) => dest.BirthYear = source.DOB.Year);
var viewModel = mapper.CreateMappedObject(domainObject);

To encourage re-use and centralize the setup of any custom transformations, I create a subclass of my Mapper class:

public class DomainModelToViewModelMapper : Mapper<DomainObject, ViewModel>
{
    public DomainModelToViewModelMapper()
    {
        this.AddMapping((s, d) => d.BirthYear = s.DOB.Year);
    }
}

Castle Windsor: Turn Off Automatic Property Injection

Suppose you have the following component:

public class SomeComponent
{
    public IEmailSender EmailSender { get; set; }
}

Further, you're using Windsor for IoC and have registered both this component and an IEmailSender in the container.  Whenever 'SomeComponent' is resolved, it's EmailSender will automatically be set by the container.  If you don't want this behavior, you can tell Windsor, on a per-property basis, to not auto-resolve the property with the "DoNotWire" attribute:

public class SomeComponent
{
    [DoNotWire]
    public IEmailSender EmailSender { get; set; }
}
Source: Turn off property injection/resolution
Technorati Tags: ,,,

New Language for 2010: Java?

No, I'm not going to pick Java as a "language to learn" in 2010.  However, I did a little Java development in 2009 (Blackberry, specifically) and was also the developer for the Blackberry version of the CodeMash mobile-application "MobiMash".

imageMy journey into Blackberry development started about March of 2009 when I got my first Blackberry, the Storm.  One of the first things I wanted to get was a Twitter client.  I had previously used PocketTwit on my WinMobile device and wanted something similar.  In particular, I wanted to see the entire tweet.  At the time, the most popular Twitter client for the Blackberry I found was TwitterBerry.  However, one thing that annoyed me was the inability to see the entire tweet.  You saw as much as was possible based on your device's width and then a "...".  You would have to click on the actual tweet to get a detail screen that showed the whole thing

Image Source: Matt Stratton

I tried a few other clients, but at that time, the Storm was still somewhat new and not many apps supported the large touch-screen.  So I thought, "Hey, I'm a programmer.  I could write my own twitter client!".

I spent some free time over the next month or so (maybe 6 or 8 hours a week) setting up RIM's Java Development Environment and learning about Java on the Blackberry devices.  It's definitely an interesting platform.  I had frustrations many times, but overall, it was a fun learning experience.

My ResultsColorFILL1

I never finished my twitter client.  TwitterBerry was eventually updated to view the entire tweet.  And since then, other twitter clients for the Blackberry have emerged – notably, UberTwitter.  This is what I'm currently using and I like it a lot.

After I abandoned my twitter client, I wanted another project to develop for my Storm.  Any time my kids were around their uncle, they bugged him to let them play Flood-It! on his iPhone.  I checked out the game and realized it was pretty simple concept with a basic UI and decided to tackle that.

After a few weeks, I released my version of Flood-It for the Blackberry Storm called "ColorFILL" (yeah, pretty imaginative, huh?).  I've gotten some requests to add features to it and I'd like to get back to it someday.  I still play it from time to time when I bored.

MobiMash

My most recent Blackberry project was MobiMash.  The MobiMash website can explain in more detail, but it was another very fun project and allowed me to lean more about some advanced layout options as well as XML parsing and data persistence on the Blackberry.  One thing that was tricky with MobiMash is that I had to make sure it worked with both the Storm and non-Storm (Pearl, Curve, etc…) Blackberry's.  I wanted a single codebase so I had to resort to preprocessing directives – how 1990's!

Java in 2010

So I'll probably continue to play around with Java development on the Blackberry.  The 5.0 Storm OS has a lot of nice UI improvements to help your apps "pop" (probably due to the competition from the iPhone).  I'd like to play around with those.  And I may even go back and polish up the MobiMash UI since it was thrown together pretty quickly.  Custom controls is definitely something to learn if you want your Blackberry apps to stand out.

Technorati Tags: ,,
Posted by Patrick Steele's .NET Blog
Filed under: , ,

Using the CodeDom With .NET 3.5 Features

Here it is 2010 and I just noticed while trying to use the CodeDom features of .NET that it defaults to using the .NET 2.0 compiler.  You have to specifically tell it to use .NET 3.5.  Thanks to LukeH and a Stack Overflow question, it was an easy solution:

var providerOptions = new Dictionary<string, string> {{"CompilerVersion", "v3.5"}};
CodeDomProvider provider = new VBCodeProvider(providerOptions);

Yeah, I had to do some integration with some VB.NET code…

Technorati Tags: ,
Posted by Patrick Steele's .NET Blog
Filed under: ,

Live Capture of Log4Net Logging

I recently had to whip up a small diagnostics application for a client.  We were having some connection problems with a component so we wanted to wrap a WinForms GUI around the component and display some debugging information to try and diagnose it.

The component was already configured to do some logging using log4net.  The GUI was going to do some additional logging of status information directly in the UI.  The end result was a textbox in the GUI and a log file generated with log4net.  Not bad, but the client asked if we could log everything (specifically, the log4net output) into the GUI – i.e. a single place to review all of the log messages.

It took a little bit of searching, but I found an easy way to programmatically add a root appender.  All I needed was an object that implemented IAppender.  Since this was quick-and-dirty and I didn't have a lot of time, my solution was as follows:

The main GUI form implemented IAppender:

public partial class MainForm : Form, IAppender

This interface has a single method – DoAppend.  Implementing this interface doesn't give you all of the bells and whistle's (like PatternLayout or filtering– derive from AppenderSkeleton if you're looking for that), but since this was done in the interest of speed, a quick String.Format was all I needed:

public void DoAppend(log4net.Core.LoggingEvent loggingEvent)
{
    ReportProgress(String.Format("log4net - {0}: {1}", loggingEvent.Level.Name, loggingEvent.MessageObject.ToString()));
}

The ReportProgress method simple appends messages to a textbox in the GUI.

Finally, in my application's "main", I initialize log4net and then add the main form to the collection of root appenders:

XmlConfigurator.Configure();
 
var mainForm = new MainForm();
((log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetLoggerRepository()).Root.AddAppender(mainForm);    
Application.Run(mainForm);

Success!  My log4net messages now appear live in the GUI as the components logs them.

Know Your Context

Another home appliance repair story that has parallels to software development.

A few days ago, I opened the dishwasher to remove the clean dishes.  However, I noticed the dishes weren't very clean.  They still had some food particles left on them.  We'd never had this problem with the dishwasher.  Then my wife pointed something out: "Oh look.  The soap dispenser didn't open".

That explained the dirty dishes.  Now I had to figure out why it didn't open.  There's a release button on the soap dispenser so I pushed that in and the dispenser popped open.  I slid the door shut and opened it again.  I did this a few times to make sure nothing was sticking.  Everything looked okay so I ran the dishwasher again.

Later that evening, I checked and the dishwasher and the dispenser had still not opened.  So now I had a new item on my to-do list.  I hopped on the computer and went to Repair Clinic.com.  It's a great site that helps you diagnose problems with your appliances.  In fact, they had a section specific to my problem.  I was leaning towards a possible problem with the linkage mechanism, but it was late and I didn't want to start taking the dishwasher apart.  I'll tackle it some night later in the week.

At dinner the next day my wife made a small comment: "I fixed the dishwasher today".  "Really?", I asked.  Not that I'm saying she isn't technically capable of taking apart the dishwasher, but appliance repair has just been something she's perfectly comfortable leaving to me.  "How did you fix it?", I asked.  She smiled and said "It was on the wrong cycle.  It was set for rinse and hold."

Now I look back and think of all the time I spent researching a problem in which I had not taken the time to know the entire context in which the problem was occurring.  A simple check to make sure the dishwasher was set for the proper cycle would have identified the issue right away.

If you're in a QA field, make sure your bug reports accurately describe the context in which a problem is occurring.  If you're in development, make sure you know the context under which a bug has been reported to make sure you're approaching the problem from the proper angle!

Technorati Tags: ,
Posted by Patrick Steele's .NET Blog
Filed under: ,

Looking Forward to 2010

Yes, the first month of 2010 is almost gone and I'm just now getting around blogging about the past year and the year ahead.  I guess time management should be on my to-do list for this year?

The CodeMash Website

One of the coolest projects I've worked on in 2009 was the CodeMash websiteBrian Prince and Jim Holmes asked SRT if they wanted to help design a new website for CodeMash.  Brian will be the first to admit that he's an evangelist first, a developer second and a web developer third.  They gave us pretty much free reign to come up with a new idea as well as the freedom to implement the solution however we wanted.

We worked with a Inner Circle Media to help plan the new look and feel of the site.  They deserve kudos for the new look.  On the back-end, I used ASP.NET MVC 1.0 along with SQL Server, Linq2SQL and Castle Windsor for my IoC container.  We also integrated with the existing Sharepoint installation for sponsor maintenance, news and session submissions.  User registration was all done in SQL.

This was a great learning project.  Registering for a conference is usually a simple process (from the registrants standpoint).  On the back-end, when you're dealing with varying registration costs (based on the current date), discount codes, PayPal, and other things, it can get pretty complicated.  A large suite of unit tests helped us catch a lot of stuff in the beginning, but a few bugs slippped through.  Luckily, nothing major!

I want to also thank fellow SRT developers Marina Fedner and Ben Barefield.  Marina helped me out on the user registration portion and Ben was responsible for the REST feed that we all used for our mobile CodeMash applications.

Stepping Down from GANG

After being involved with the Great Lakes Area .NET Users Group (GANG) for many, many years (webmaster, VP and this last year as President), I did not run for re-election.  There were some other projects I was taking on and last year's vice president David Giard was willing to take the reigns of the group.  Dave did an amazing job last year as VP and is continuing to do great things with GANG in 2010.  I'll still be around to help out from time to time, but Dave is the man in charge now!

VSM's C# Corner

After helming Visual Studio Magazine's C# Corner for a number of years, Bill Wagner decided he wanted to devote his time to other things.  He offered my name as a possible successor!  I talked it over with him and VSM Editor in Chief Michael Desmond.  Everything fell into place and I'm now honored to be following in Bill's footsteps as a VSM author.  My first column has been published (Interface-Based Programming in C#) and I've got some positive feedback so far.  My next article is in-process and I have to have the first draft done by February 1st or I'll be on someone's naughty list (and it won't be Santa's!).

Microsoft C# MVP

I was pleasantly surprised on January 1st to receive an email from Microsoft telling me I've received an MVP award for my C# and community work in 2009.  Thanks to Microsoft and other community members I work closely with!

2010 Plans

  • One of the big conferences for 2010, CodeMash, has already come and gone.  It was a great conference and you CAN NOT beat the price.  The amount of content and learning available is unheard of for the price you pay.  I'm already looking forward to CodeMash 2.0.1.1.
  • In February, I'll be attending the MVP Summit in Redmond.  A great chance to get in touch with new technologies, talk with Microsoft reps and mix it up with other MVP's.
  • Michael Eaton is already planning this year's Ann Arbor Give Camp.  I've offered my assistance again this year and will post more on this even as it gets closer.
  • Speaking: I'd like to do more speaking this year.  While I usually get compliments on my presentations, I'm very hard on myself.  I may be a good speaker, but I want to be a great speaker.  That will come with practice.  I've got some idea's for presentations on topics I'm passionate about (specifically, Inversion of Control and Mocking).

I'm really looking forward to 2010!

Technorati Tags: ,,,

And SRT Grows by One!

Great news!  MVVM-Guru Brian Genisio is joining SRT in February!  I first met Brian in-person at CodeMash last year.  In fact, he was the one that convinced me to get on Twitter.  Since then, I've been reading his blog and trying to catch his presentations at local user groups.  He'll be giving his great MVVM talk at CodeMash in January.

Congratulations Brian.  I'm looking forward to working with you!

Posted by Patrick Steele's .NET Blog
Filed under:

CodeMash REST Endpoints Updated

You spoke, we listened.  We've got updates to the CodeMash REST endpoints!

We've added two new pieces of information to the Sessions feed: Track and Room.  Track exposes what track a session is part of (for filtering, etc…) and Room indicates which room the session will be held in.  Note that the room information and start time have not been finalized and are subsequently empty.

Also, the speaker submission form was designed during a heated battle for control of a coconut tree so we forgot to ask speakers for their twitter handle and blog URL.  We're collecting that info now and as it trickles in, we'll get our database updated and you'll see the information in the REST feeds.

Technorati Tags: ,,,
Posted by Patrick Steele's .NET Blog
Filed under: , , ,

Use Dependency Injection To Simplify Application Settings

We've all seen and written code that accesses data from our app.config or web.config file.  We'll throw some simple settings in there:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key="enableLogging" value="true" />
        <add key="startDate" value="12/1/2010" />
        <add key="baseFee" value="157.50" />
    </appSettings>
</configuration>

And then we'll use the ConfigurationManager to pull the data out when we need it:

public class Foo
{
    public void DoSomething()
    {
        bool enableLogging = Convert.ToBoolean(ConfigurationManager.AppSettings["enableLogging"]);
        DateTime startDate = Convert.ToDateTime(ConfigurationManager.AppSettings["startDate"]);
        decimal baseFee = Convert.ToDecimal(ConfigurationManager.AppSettings["startingFee"]);
    }
}

Now that Inversion of Control and Dependency Injection are part of my everyday development, I don't do it this way anymore.  It's messy and doesn't allow me to easily plug in different values during testing.

These days, I create a simple interface for my application settings:

public interface IApplicationSettings
{
    bool EnableLogging { get; }
    DateTime StartDate { get; }
    decimal BaseFee { get; }
}

And create an implementation of this interface that pulls data from app.config:

public class AppConfigSettings : IApplicationSettings
{
    public AppConfigSettings()
    {
        this.EnableLogging = Convert.ToBoolean(ConfigurationManager.AppSettings["enableLogging"]);
        this.StartDate = Convert.ToDateTime(ConfigurationManager.AppSettings["startDate"]);
        this.BaseFee = Convert.ToDecimal(ConfigurationManager.AppSettings["startingFee"]);
    }
 
    #region IApplicationSettings Members
 
    public bool EnableLogging { get; private set; }
    public DateTime StartDate  { get; private set; }
    public decimal BaseFee { get; private set; }
 
    #endregion
}

I register my types with my IoC container.  During production, dependency injection takes over and automatically gives me my AppConfigSettings instance.  For testing, I generate a mock IApplicationSettings.  And using these settings just got a whole lot cleaner:

public class Foo
{
    public Foo(IApplicationSettings applicationSettings)
    {
        
    }
}

CodeMash 2.0.1.0 REST Interfaces

This year's CodeMash website has two URI's that expose CodeMash Information:

The main news feed is already exposed via RSS (http://www.codemash.org/rss) but we may add a REST interface for that as well.

Session Information

Here's an example XML response for a single session (note that the <Abstract> content has been removed for brevity):

<Sessions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Session>
    <URI>/rest/sessions/The-case-for-Griffon-developing-desktop-applications-for-fun-and-profit</URI>
    <Title>The case for Griffon: developing desktop applications for fun and profit</Title>
    <Abstract>...</Abstract>
    <Start>0001-01-01T00:00:00</Start>
    <Difficulty>Beginner</Difficulty>
    <SpeakerName>Andres Almiray</SpeakerName>
    <SpeakerURI>/rest/speakers/Andres-Almiray</SpeakerURI>
  </Session>
</Sessions>

The <URI> node contains a URI that will return a single session.  The <Start> node is not set yet since the sessions have not been scheduled.  Once scheduling is complete, it will be available from this feed.

Speaker Information

Here's an example XML response for a single speaker (again, the <Biography> content has been removed for brevity):

<Speakers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Speaker>
    <Name>Chris Adamson</Name>
    <Biography>...</Biography>
    <Sessions>
      <SessionURI>/rest/sessions/Oh-Crap-I-Forgot-or-Never-Learned-C</SessionURI>
      <SessionURI>/rest/sessions/How-Do-You-Do-That-on-iPhone</SessionURI>
    </Sessions>
    <TwitterHandle />
    <BlogURL />
  </Speaker>
</Speakers>

Most speakers only have one session, but some have multiple.  You'll notice that the TwitterHandle and BlogURL for most of the speakers is empty.  That's because we forgot to ask for that information during the speaker submission process (oops!).  We're in the process of collecting the information and as we get it, we'll update our database and the information will be exposed to you.

Supported Formats

Right now, we support XML (default) and JSON formats.  If you don't add any extension to the REST URI's (or use ".xml") you'll get XML-formatted data (like above).  Use ".json" to return a JSON result.  We could support HTML – perhaps a simple <table> that dumps the information?  Let me know if you think that would be useful.

Technorati Tags: ,,

Is There An App For That?

Do you plan on using these API's to make some cool CodeMash apps?  Keep us updated on your progress by leaving a comment here, blogging about it or via Twitter using the hashtag #CodeMash.  We're looking forward to seeing what you build!

Posted by Patrick Steele's .NET Blog
Filed under: , ,

Using Windsor to inject dependencies into ASP.NET MVC ActionFilters

I'm using Windsor as my IoC container for an ASP.NET MVC application.  To get dependency injection in my controllers, I'm using a slightly modified WindsorControllerFactory from Andre Loker's post earlier this year.  It works great and allows me to easily test my controllers.

I've got some custom ActionFilters that would benefit from dependency injection.  Unfortunately, ActionFilters are attributes on controllers and methods and their instantiation is controlled by the framework.  There is no extension point to allow custom creation of the ActionFilters.  So I can't do constructor dependency injection.  However, I can do the next best thing – property dependency injection!

Custom Method Invoker

The ASP.NET MVC framework has an extensibility point when it comes to actually invoking actions on controllers.  The default ControllerActionInvoker does everything we need.  We need to modify it's behavior just a little bit to allow us to inject property dependencies.

There's a method on ControllerActionInvoker that is used whenever an action with filters is going to be executed.  It's appropriately named "InvokeActionMethodWithFilters".  This method is passed a collection of ActionFilters that have already been created by the framework (see, this is why we can't use constructor injection).  Thanks to Simone Chiaretta for blogging about his custom Ninject-based ActionInvoker, I was able to convert his to a Windsor-based invoker with relative ease:

public class WindsorActionInvoker : ControllerActionInvoker
{
    readonly IWindsorContainer container;
 
    public WindsorActionInvoker(IWindsorContainer container)
    {
        this.container = container;
    }
 
    protected override ActionExecutedContext InvokeActionMethodWithFilters(
            ControllerContext controllerContext,
            IList<IActionFilter> filters,
            ActionDescriptor actionDescriptor,
            IDictionary<string, object> parameters)
    {
        foreach (IActionFilter actionFilter in filters)
        {
            container.Kernel.InjectProperties(actionFilter);
        }
        return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
    }
}

As you can see, it's pretty straightforward.  All we do is loop through all of the ActionFilters and inject any dependant properties.  Those of you familiar with Windsor will realize that the IKernel doesn't have an "InjectProperties" method.  I grabbed that from Jeremy Skinner's post about using AutoFac to inject properties into ActionFilters.  He created an extension method that uses reflection to resolve property dependencies:

public static class WindsorExtension
{
    public static void InjectProperties(this IKernel kernel, object target)
    {
        var type = target.GetType();
        foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (property.CanWrite && kernel.HasComponent(property.PropertyType))
            {
                var value = kernel.Resolve(property.PropertyType);
                try
                {
                    property.SetValue(target, value, null);
                }
                catch (Exception ex)
                {
                    var message = string.Format("Error setting property {0} on type {1}, See inner exception for more information.", property.Name, type.FullName);
                    throw new ComponentActivatorException(message, ex);
                }
            }
        }
    }
}

Now that we have the plumbing, let's hook it up!

Changes to WindsorControllerFactory

My current implementation of WindsorControllerFactor.GetControllerInstance looks like this:

protected override IController GetControllerInstance(Type controllerType)
{
    if( controllerType == null)
    {
        return base.GetControllerInstance(controllerType);
    }
    var controller = container.Resolve(controllerType) as Controller;
 
    return controller;
}

I just need to add a few lines of code to add my custom WindsorActionInvoker (which is registered inside Windsor!):

protected override IController GetControllerInstance(Type controllerType)
{
    if( controllerType == null)
    {
        return base.GetControllerInstance(controllerType);
    }
    var controller = container.Resolve(controllerType) as Controller;
 
    // new code
    if (controller != null)
    {
        controller.ActionInvoker = container.Resolve<IActionInvoker>();
    }
 
    return controller;
}

Using Property Injection with ActionFilters

So now lets look at how this can be used to add common data to every page.  The oft-used example of populating a list of sponsors just happened to be the exact scenario I was facing when I researched this solution.  With the WindsorActionInvoker in place, I can now create an ActionFilter to load my sponsor information into ViewData:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class LoadSponsorsAttribute : ActionFilterAttribute
{
    public ISponsorRepository SponsorRepository { get; set; }
 
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.Controller.ViewData["sponsors"] = SponsorRepository.GetSponsors();
        base.OnActionExecuting(filterContext);
    }
}

Now any method (or an entire controller class) can have this attribute applied and the sponsor information will be loaded before the action is called.  Here's an example:

[LoadSponsors]
public class ErrorController : ControllerBase
{
    public ActionResult Index()
    {
        return View();
    }
}

The list of sponsors is available in your view pages and user controls by simply looking at the ViewData dictionary:

var sponsors = (IEnumerable<Sponsor>) ViewData["sponsors"];

Final Polish

A nice solution, but the use of magic string constants ("sponsors") along with where the data are stored (ViewData) didn't sit well.  To fix that, I added a few extension methods to abstract out the access to the list of sponsors:

public static class Extensions
{
    private static class ViewDataKeys
    {
        public const string Sponsors = "sponsors";
    }
 
    public static void SetSponsors(this ControllerBase controller, IEnumerable<Sponsor> sponsors)
    {
        controller.ViewData[ViewDataKeys.Sponsors] = sponsors;
    }
 
    public static IEnumerable<Sponsor> GetSponsors(this ViewUserControl viewControl)
    {
        return viewControl.ViewData[ViewDataKeys.Sponsors] as IEnumerable<Sponsor>;
    }
}

The SetSponsors extension method allows me to change my LoadSponsorsAttribute to be simply:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    filterContext.Controller.SetSponsors(SponsorRepository.GetSponsors());
    base.OnActionExecuting(filterContext);
}

The GetSponsors extension method on ViewUserControl allows the Sponsors.ascx that I used to display the list to simply call the GetSponsors() method – no access to ViewData or casting required:

var sponsors = this.GetSponsors();

Thanks to Andre Loker, Simone Chiaretta and Jeremy Skinner for making great posts that allowed me to piece all of this together!

More Posts Next page »