If you are just joining, you might want to start from the beginning: http://www.damonpayne.com/2008/09/14/RunTimeIsDesignTimeForAGT0.aspx
Service Wire-up, Presenters, and Events
As I wrapped up the last article I found that I needed to determine a mechanism for providing IService implementations to Components that need them, and that it would be nice to have this happen in some automatic repeatable fashion. As for providing IService implementations, there are four ways (four that come to mind right now anyway) to do this, one of which I have already shown:
1. Explicitly: ISuck is = ServiceManager.Resolve<ISuck>();
2. Implicit magic: public ISuck MySuck{get;set;} //set by some magic
3. Via constructor: public MyServicedComponent(ISuck defaultSuck){…} //possibly also set by magic
4. Via a Serviced Component interface: perhaps something like this, coupled with a registration mechanism:
public interface IServicedComponent
{
IService[] GetDependencies();
void SetDependency(Type serviceType, IService impl);
}
In terms of discoverability of what is going on, #4 wins. Back in my original design goals I stated that I wanted things to feel like Serviced Components. An interface like this, which we could build on, would establish an unambiguous set of expectations and behaviors. When it gets down to the other design consideration of “How do I want to code?”, though, I’m not very in love with #4. #1 is safe but puts any future lifecycle management we may want to do out of reach most likely. To support #2 or #3, I am going to need control over when most important objects are created. It seems that going with #3 would make the various Reflection code that I have and expect I’ll have later a bit more messy. As much as it pains me to do so, I’m going to go with #2 and try to make sure I take care of some design goals:
· Make the source of injected objects as discoverable as seems reasonable, so that there is as little distrustful magic as possible.
· In terms of the cleanest OO practices one doesn’t want to allow objects to exist in a state where they are invalid. Providing a default constructor when this would render an object unusable is not desirable.
Since I’ve been rereading some Fundamental Material lately, the answer has already jumped out at me.
Builder Pattern
“The Builder Pattern separates the construction of a complex object from its representation so that the same construction process can create different representations.”
Using a builder pattern we can separate the construction of a serviced component from the creation of its dependent parts. This also provides a clean “entry point” into the creation of top level objects and gives client code a window into when and how dependency injection is happening without exposing any unnecessary plumbing. For truly old-school OO folks: I realize I am reaching a bit here as what I’m about to show differs in structure but not intent from the canonical Builder examples.
Because generics are the answer to nearly everything, and because part of the point of this series of articles is to show code evolving, I type the following code into VS2008 and stare at it so that thoughts can congeal.
namespace DamonPayne.AGT.IoC
{
public class ComponentBuilder
{
public static T Create<T>() where T :new ()
{
T instance = new T();
return instance;
}
I thought about this for a while. Suppose my first use of this code was as follows:
MessageConsoleView view = ComponentBuilder.Create<MessageConsoleView>();
This will accomplish several goals for me. If MessageConsoleView needs an IService implementation I can detect that need and provide it here. If MessageConsoleView needs a “Presenter” of some kind I could detect and provide that too. In the first article I stated that I wanted as little code behind/presenter code as possible. As it turns out, I had been corrupted by WPF. WPF contains a nice notion of a Command infrastructure whereby visual components can be bound to fire commands such as from a button’s click event. This often results in hardly any code behind or presenter needed. Silverlight doesn’t have this, nor a Navigation service, nor control Validation plumbing, so there’s probably some more work to do later.
I could take this notion one layer “forward”, and come up with a way to construct the IView implementations too, such that the entire composite UI was constructed in magical fashion. For now I’ll stick with imperative creation of the IViews. The following code now works:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
IRegionManager mgr = ServiceManager.Resolve<IRegionManager>();
MessageConsoleView view = ComponentBuilder.Create<MessageConsoleView>();
mgr.AddView(view, "MessageConsole");
}
The next step is to determine what the contract looks is for a serviced component to say “I need an IService implementation”. Since we have ruled out the constructor method, we’re left with properties or fields. Now to rely solely on reflection or include attributes too? For now, we’ll just let the magic roll and state that: Any Public properties of type IService or some Presenter type will just be set for you within ControlBuilder. MessageConsoleView needs an ILogService.
public ILogService LogService { get; set; }
We’ll assume we can use the LogService property as soon as the Constructor returns, like so:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
LogService.Debug("Message console started");
}
Next it’s up to the ComponentBuilder to make the magic happen. As soon as we generically create an instance of whatever type was given to ComponentBuilder we call an method to find out if it needs any IService types:
internal static void ResolveServiceDependencies(object component)
{
PropertyInfo[] publicProps = component.GetType().GetProperties();//TODO: cache per Type to make this faster
foreach (PropertyInfo pi in publicProps)
{
Type propType = pi.PropertyType;
if (propType.IsInterface && propType.InterfaceExtends(typeof(IService)))
{
object svc = ServiceManager.Resolve(propType);
pi.SetValue(component, svc, null);
ResolveServiceDependencies(svc);//TODO: Smartest way to do this?
}
}
}
“InterfaceExtends” is a new extension method for Type “Type” from the new TypeExtensions static class in the IoC project. Determining if one Interface extends another interface is rather annoying so it’s encapsulated there. Other than that this method is straightforward: look for any public properties on component that are of a Type that extends IService. Resolve that type using the ServiceManager and recursively check to see if that service depends on any other services. I also had to refactor ServiceManager to have a Resolve(Type) method alongside the generic Resolve<Type>() method. This takes care of IService injection for now, on to Presenters.
I could call Presenters a special kind of Service and be done now. I won’t do this for several reasons. First, I’ve never looked back on code and wished I could swap out a different Presenter implementation. While running automated testing, why would I want to swap out a Presenter? If I’m not testing the logic/process, and I’m mocking out any heavy components like data access, what am I really testing then? Second, I tend to like doing Model-View-Presenter a certain way which doesn’t fit into some of the design choices I made for the IService world and I don’t want to redo my IService world for this reason at least. I like to have a Presenter talk to a certain specific View interface, while the View knows the concrete type of the Presenter. View ßà Presenter communication is then a combination of method calls and data binding.
I could have an IPresenter interface and use that to type public properties and have the same logic as I have above. Since every Presenter will be highly specialized I would end up just having IPresenter as a “marker” interface and since this is .NET there are better ways to do this. I could force a Presenter to extend a base class, but in this case I don’t like that either. It’s a common practice to have an application specific Presenter base class to use for code sharing. I’ll mark a Presenter with a public Property with a certain attribute type on it. I can always change my mind later. After creating an empty IMessageView and MessagePresenter, MessageConsoleView now has another Property:
[Presenter()]
public MessagePresenter Presenter { get; set; }
And my presenter constructor looks like:
public class MessagePresenter
{
public MessagePresenter(IMessageView view)
{
View = view;
}
Now I can go back to ComponentBuilder and add another method:
internal static void BuildPresenters(object component)
{
PropertyInfo[] publicProps = component.GetType().GetProperties();//TODO: cache per Type to make this faster
foreach (PropertyInfo pi in publicProps)
{
object[] attrs = pi.GetCustomAttributes(typeof(PresenterAttribute), true);
if (null != attrs && attrs.Length > 0)
{
//We rely on the convention that the Presenter has a public constructor with a view as argument,
//where said view is implemented by "component"
ConstructorInfo ci = pi.PropertyType.GetConstructor( new Type[] { component.GetType() } );
object presenter = ci.Invoke(new object[] { component });
pi.SetValue(component, presenter, null);
ResolveServiceDependencies(presenter);
}
}
}
At this point, by the time my IView loaded event fires, I’ve already got valid state through the friendly ComponentBuilder. The “parts” of my Builder pattern are the IServices and Presenters. If I were a tiny bit less lazy I could hide the algorithms for IService and Presenter resolution behind an interface or Strategy and allow different algorithms to be plugged in for resolution.
Component Communication using Events
“Every problem in computer science can be solved by one more level of indirection.”
In Composite Application Guidance for WPF (http://msdn.microsoft.com/en-us/library/cc707857.aspx) they introduce the idea of an Event Aggregator. An Event Aggregator is sort of a message hub. In normal .Net event driven code, you have a component that has a hard reference to another component that can raise an event. The event is signified as a special delegate type and VS2008 is nice about generating code stubs for you. The problem here is that in complex systems you might be interested in certain occurrences where it is impossible or undesirable to obtain references to all of the components that could affect the kind of occurrence you are interested in. An Event Aggregator is a huge help in meeting the design goal of having components communicate with each other.
The PRISM way of doing this is to define a special event for absolutely every action that could occur and hooking these into an event aggregator. This might mean (going back to the purple couch example from the previous article) a CouchAddedEvent, a CouchChangedEvent, and a CouchDeletedEvent all with a Couch payload. Following this will surely be a ChairAddedEvent, ChairChangedEvent, ChairDeletedEvent, and so forth. In order to avoid this event explosion, we can define more generic events with template data payloads. The first place I’ll use this is for the ILogService. I’m going to refactor the ILogService slightly to show two points:
public interface ILogService: IService
{
void Log(DamonPayne.AGT.IoC.Types.LogMessage m);
}
The new Types namespace will contain things like this. Using the EventAggregator I want to be able to get an event of a certain type, with a certain kind of data payload and either subscribe to it or raise the event with a valid payload. I don’t see any way to make these work exactly the same as the .NET events you might be used to, so let’s take a look. First we create an AggregateEvent class to help with the generics programming.
namespace DamonPayne.AGT.IoC.Events
{
public class AggregateEvent<T>
{
public AggregateEvent()
{
_subscribers = new Dictionary<Action<T>, Func<T, bool>>();
}
internal void InvokeAll(T arg)
{
foreach (Action<T> callback in _subscribers.Keys)
{
if (null == _subscribers[callback] || _subscribers[callback](arg))
{
callback(arg);
}
}
}
private Dictionary<Action<T>, Func<T, bool>> _subscribers;
public void Subscribe(Action<T> callback)
{
Subscribe(callback, null);
}
public void Subscribe(Action<T> callback, Func<T, bool> filter)
{
_subscribers.Add(callback, filter);
}
public void Raise(T payload)
{
InvokeAll(payload);
}
}
}
Generics are still the answer to everything as far as I can tell. It took some time to get the template parameters correct, but now the EventAggregator thus far is fairly simple.
namespace DamonPayne.AGT.IoC.Events
{
/// <summary>
/// Allow creation and subscription to decoupled events
/// </summary>
public class EventAggregator
{
static EventAggregator()
{
_events = new Dictionary<Type, object>();
}
private static Dictionary<Type, object> _events;
/// <summary>
/// Get an event representation. The event Type signifies the sort of situation that we are interested in raising or being notified of,
/// and the payload type represents the precise type of data payload we are interested in.
/// </summary>
/// <typeparam name="TEventType"></typeparam>
/// <typeparam name="TPayload"></typeparam>
/// <returns></returns>
public static TEventType Get<TEventType, TPayload>() where TEventType: AggregateEvent<TPayload>
{
Type t = typeof(TEventType);
if (!_events.ContainsKey(t))
{
ConstructorInfo noArgs = t.GetConstructor(new Type[] { });
TEventType evnt = (TEventType)noArgs.Invoke(null);
_events.Add(t, evnt);
}
return (TEventType)_events[t];
}
The first example of using the event aggregator is in the ILogService implementation, MemoryLogger. I realize I had Startup and Shutdown methods on IService but wasn’t using them, so a call to Startup was added to ServiceManager.ObtainDefaultInstance. The MemoryLogger wants to be able to let the universe know when log messages arrive, in a decoupled fashion. The event “Type” is MessageArrivedEvent.
public class MessageArrivedEvent<T> : AggregateEvent<T>
{
public T Message { get; set; }
}
Next we get an event that has the type of payload we are interested in.
private MessageArrivedEvent<LogMessage> _logEvent;
public void Startup()
{
_logEvent = EventAggregator.Get<MessageArrivedEvent<LogMessage>,LogMessage>();
}
public void Log(DamonPayne.AGT.IoC.Types.LogMessage m)
{
_messages.Add(m);
_logEvent.Raise(m);
}
This is the code necessary to consume this event in MessageConsoleView.
public MessageConsoleView()
{
InitializeComponent();
_currentMessages = new List<LogMessage>();
DataContext = _currentMessages;
EventAggregator.Get<MessageArrivedEvent<LogMessage>, LogMessage>()
.Subscribe(MessagePosted);
}
private void MessagePosted(LogMessage m)
{
_currentMessages.Add(m);
}
We would need to keep the event around in order to unsubscribe later, but we haven’t gotten that far yet. There is one other bit of functionality that I wanted to show here that isn’t used yet but is best shown along with the rest of the EventAggregator code. We also have the notion of event filters, implemented using … wait for it… Generics. We have the ability to subscribe to an event conditionally. This is another slick idea stolen from PRISM. Take a look at this temporary test method:
public static void Test()
{
MessageArrivedEvent<LogMessage> myEvent =
EventAggregator.Get<MessageArrivedEvent<LogMessage>,LogMessage>();
Action<LogMessage> logCallback = msg =>
{
Console.WriteLine("I am the first subscriber");
};
Action<LogMessage> lc2 = msg =>
{
Console.WriteLine("I am the second subscriber!");
};
Action<LogMessage> filterCB = msg =>
{
Console.WriteLine("I only get called if the message sucks");
};
myEvent.Subscribe(logCallback);
myEvent.Subscribe(lc2);
myEvent.Subscribe(filterCB, lm => lm.Message.IndexOf("suck") != -1);
LogMessage goodMsg = new LogMessage
{
Level = LogLevels.Debug,
Message = "Yee haw!"
};
LogMessage suckMsg = new LogMessage
{
Level = LogLevels.Debug,
Message = "this sucks!"
};
myEvent.Raise(goodMsg);
myEvent.Raise(suckMsg);
}
We subscribe to the MessageArrivedEvent<LogMessage> with three different Closures. For the third closure, we use the overload that accepts a Func<T,bool>. The lambda expression only returns true if the message contains “suck” in it somewhere. Running this code shows that when we raise the event with goodMsg only logCallback and lc2 fire. Raising the event with suckMsg passes the filter and all three callbacks fire. This is incredibly useful to further fine tune which events a component is really interested in.
Summary and Next Steps
Service containers, service locators, model-view-presenter, and event aggregation actually have very little to do with the overall problem I set out to solve. I could have made a service container specific to the design-time environment similar to what Winforms 2.0 uses and gotten started on the core problem sooner. Still, this stuff may come in handy. I have now the beginnings of a framework that I can use and refine as I discover what my needs truly are.
Source code: DamonPayne.AGT[2].zip (550.62 KB)