Contextual Awareness – Making your container behave intelligently

Containers are dumb – what if they were smart?

One of the things I really wanted to do when I set out to create the ServiceLocator in Siege.Requisitions was to create a container that could map an interface to multiple types, and then at runtime, automatically figure out which one of those types I really wanted. To accomplish this, I put at it’s core a small rule engine to allow users to create rules that would help the container understand how to pick implementations. It works great — but there’s one problem. You have to specifically tell it what you want. That’s hardly much better than resolving the type directly; you have to program intent directly into your application, and you carry around references to IServiceLocator.

Very ugly.

I keep coming back to the original premise … I want the container to figure it out for me. I want to explain to the container what I want it to do, and under which conditions. Then I want it to figure it out for itself at runtime by understanding the state of the application, the user session, whatever I have explained to it. I want a smart container. But the problem is, most containers are dumb. You map an interface to a type — thats it. You want multiple types? Register them with names to distinguish them. Register different “profiles” to instruct the container to run in certain modes. Create child containers.

I just didn’t feel like those solutions worked well for me. I want a smart container that can figure it all out. I don’t want to spend time building families of containers and wiring it all together and telling them how to interact. I don’t want to have to make my application look up values by a name, or set my container into a certain “profile mode”. I want to focus on my application. I want to explain my scenario to the container and move on and not think about my container anymore.

A practical example

As I mentioned in my previous post, we encounter this scenario in our every day development at my company. Where I work, we integrate with a lot of partners. There are a lot of commonalities between these vendors and we have abstracted them out dutifully into interfaces with multiple implementations. Basic abstraction and polymorphism. We routinely have scenarios where user input/selections on a UI translates into a vendor call. Ultimately, this means resolving a specific implementation of an interface to send or request data from that vendor.

We have our registrations set up with rules like so:

 

serviceLocator.Register<Singleton>(Given<IExampleService>.When<SelectionType>(selection => selection == SelectionType.OptionA).Then<OptionAService>();
serviceLocator.Register<Singleton>(Given<IExampleService>.When<SelectionType>(selection => selection == SelectionType.OptionB).Then<OptionBService>();

 

For the sake of this example, SelectionType is an enum representing a vendor we integrate with. Implementations of IExampleService contain code that we use to integrate with those vendors. The registration API allows you to specify the lifestyle with the method call (in this case, singleton). And with those registrations in place, you’d see controller code like this:

 

public class SampleController : Controller
{
     private IServiceLocator serviceLocator;
    
     public SampleController(IServiceLocator locator)
     {
            this.serviceLocator = locator;
     }

     public ViewResult Foo(SelectionType selectionType)
     {
           IExampleService service = locator.GetInstance<IExampleService>(new ContextArgument(selectionType));
           //invoke methods on service
     } 

 

Like I said, ugly stuff. In this example, we get a reference to the service locator, pass in some context to help the container identify which rule applies so it knows which type to create. I don’t want to carry around that reference to IServiceLocator, and I don’t want my controllers having to understand how to help the container to select the rule that applies based on user input.

Like I said, I want a smart container. Turns out the solution was pretty easy to implement, and didn’t even require me to change how Siege works!

Introducing Awareness.Of<T>

Siege simplifies thing for consumers by exposing only one method to do registrations — I call it Register (novel, I know). Register takes in instances of IRegistration, or Action<IServiceLocator>. There are several different syntaxes available to help you do registrations … Given<T>, which helps you register individual types … Using.Convention<TConvention> which allows you bundle registrations together for reuse, and now … Awareness.Of<T>, which tells the container how to figure out the things it needs to evaluate its rules on its own.

This abstraction allows consumers to extend the Service Locator easily — by providing your own implementation of IRegistration, the container understands how to handle your new and unique registration type. To prove this premise, I add all new functionality to Siege.Requisitions.Extensions without extending the core framework. It is completely pluggable. But, that’s a tangent, and I’ll cover it more in some other post.

With Awareness, we have a smart container, which can identify the rules associated with a requested type and use its own awareness to determine validity of those rules.

How does it work?

Awareness.Of<T> works like this: When you resolve a type, the container looks up all rules associated with that type. In our example, it receives rules based on SelectionType. If the container is “Aware” of SelectionType, it will automatically use that awareness to find the current SelectionType value and then give it to the rule engine to evaluate. It continues this process until either a rule is satisfied or no rules match.

Awareness.Of<T> takes a delegate as an argument. This delegate is a Func<T> which tells the container how to get an instance of T to use in rule evaluation. For our MVC applications, we have used Awareness.Of<T> to make our container aware of the Model Binding mechanism in ASP.NET MVC, which it uses to pull data into an object like model binding for controller actions done (form data, query string data, etc), to make our container aware of any configuration files, to make it aware of the session, to make it aware of a wide variety of things. The container becomes more intelligent the more aware it becomes; Your other processes cease to depend on the container at all and cease to have to explicitly direct the container to make decisions.

It all happens naturally, and intuitively.

Making your container contextually aware

To make your container aware is very easy. To follow with our example, I will show you how we’ve made our container aware of the model binding system in ASP.NET MVC. This registration is from code in the Siege.Requisitions.Web.dll file. It’s very simple, just provide a registration like this:

 

serviceLocator.Register(Awareness.Of(ModelBinding.For<SelectionType>().UsingDefaultBinder());

 

This registration instructs the service locator to use the default model binder to pull create a SelectionType object. There are also methods on ModelBinding.For<T> which allow you to specify a custom binder (if, for example, you need to look an object up by ID before evaluating it with a rule). 

Our example, revisited with Awareness.

So here’s what the whole thing looks like with awareness. I will also use controller action injection, as I covered in my last post.

 

//from global.asax.cs, or wherever you initialize the container

serviceLocator.Register<Singleton>(Given<IExampleService>.When<SelectionType>(selection => selection == SelectionType.OptionA).Then<OptionAService>();
serviceLocator.Register<Singleton>(Given<IExampleService>.When<SelectionType>(selection => selection == SelectionType.OptionB).Then<OptionBService>();
serviceLocator.Register(Awareness.Of(ModelBinding.For<SelectionType>().UsingDefaultBinder()); 

 

public class SampleController : Controller
{
     public SampleController()
     {
     }

     public ViewResult Foo(IExampleService service)
     {
           //invoke methods on service
     } 
}

 

Looks much cleaner, don’t you think? All properly decoupled and automatically done for you. The best part is — it works with whatever container you’re already using. One of the other priorities I had when I started Siege was to be a value-added proposition for existing containers. If you use Windsor, StructureMap, Unity, whatever… Siege integrates seamlessly with them and makes them smarter. It adds additional abilities to what those containers can already do.

 

Happy Coding!

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

This entry was posted in IoC, Siege. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Confused

    I think I am missing something here. How is this different from what I currently do with StructureMap (aside from the fact that your example injected the service into the action):

    public class SampleController : Controller
    {
    public SampleController(ExampleService service)
    {
    _service = service;
    }
    }

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    Well, in my example there is an interface – IExampleService, and 2 implementations — OptionAService and OptionBService. The container picks one of these implementations automatically based on user input and injects it for you..

    Does StructureMap do that for you?

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    To clarify, in this example, if you user selects “Option A” in the drop down on the UI, the container sees that and knows to inject OptionAService into the controller action. If the user selects “Option B” in the drop down on the UI, the container sees that as well and knows to inject OptionBService into the controller action.

    Does that make sense/answer your question?

  • Confused

    > Does that make sense/answer your question?

    Yes. StructureMap does have a “profiles” feature, which I ‘think’ works in a similar fashion.

    Thanks for clearing that up!

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    Hi Confused,

    I have to disagree. The Profiles feature in StructureMap, as I understand it (someone please correct me if I’m wrong!) works by you manually setting the profile mode on the container.

    That means, as a result, you carry a reference to your container around, and have to explicitly say “Now run in this mode. Okay, good. Now run in this mode.” You are basically back to telling the container what to do … I don’t think that’s a smart container at all!

    In this approach, the container figures those things out _on it’s own_! You just teach it how to figure it out. That means no references to the container, and no explicitly telling it which “mode” to run in. It does it all on it’s own!

    Make sense?

  • http://blog.robinclowers.com Robin Clowers

    Actually you can do conditional registration, although you would need to register your own object that accesses the model binder on each request get what siege is providing:

    var container = new Container(x =>
    x.For().ConditionallyUse(c =>
    {
    c.If(context => context.GetInstance().For
    () == SelectionType.OptionA).ThenIt.Is.Type
    ();
    c.If(context => context.GetInstance().For
    () == SelectionType.OptionB).ThenIt.Is.Type
    ();
    }));

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    Hi Robin,

    I would say “not exactly”. The core distinction between how I’m doing this and how StructureMap is doing this is that I would say my stuff is more adaptive.

    Awareness is just one way the container is able to understand what you want to do. My goal is to decouple what can be considered context and where it comes from away from making the container the end-all, be-all for determining what context “is”.

    For Siege, context can be … anything. It can be information about the current user. It could be the time of day. It could be some action the user just initiated. It could be problems with an integration point. Literally, anything.

    The container is designed to be able to either collect these as you go, to be made aware (or, in the near future, to automatically “become” aware on its own).

    This could easily be ignorance on my part, but in the scenario you posted, it seems like these concepts must exist within the StructureMap container already for you to be able to use it? That is to say, you have to be able to resolve a “dependency”, which really isn’t a dependency, and likely not anything you even want inject/resolve anywhere. Is that correct?

    I did a quick spot check on the googles for StructureMap, ConditionallyUse and IContext. Nothing really stood out to me that defined it well enough for me to get a good grasp of how it works.

    Really, for me, it comes down to divorcing the concept of context and where it comes from away from types that can be injected. The container absolutely should be aware of context. But I don’t want to make users register the ModelBinding class unless they actually plan to make it a dependency of some type, or resolve it for their application need.

    I’d much rather give the container pieces of information as I go, or give the ability to find information on its own as it goes. Creating an object just so that I can register it, just so the container can look it up as a rule… seems kinda like a workaround, to me. In fact, I could do the same exact thing with Siege during registration. But that doesn’t really do what I was trying to do.

    I guess ultimately what I’m saying is … how would you express something like this?

    Let’s assume the following types:

    interface IAccountService
    class WellFundedAccountService
    class OverdrawnAccountService
    class SystemUnavailableAccountService

    I’m a customer of a bank. When I log in to their website, the system requests an IAccountService implementation. Based on whether or not the current customer’s account is overdrawn or not, I want to either get WellFundedAccountService or an OverdrawnAccountService. Additionally, if the last request to get a user’s account (same or different user session) failed, I want to show a SystemUnavailableAccountService (I want this rule to be true for 5 minutes, and we’ll ignore any concurrency issues).

    I know it’s a contrived scenario, and disconnected from the original example in this post, but I think it demonstrates the difference. These things are purely derived from arbitrary information. I could make the example completely more arbitrary than this. These are the sorts of things I want to be able to support.

    Again, it may be my ignorance in this matter, but it doesn’t seem like StructureMap natively supports this level of complete arbitrariness.

    For completeness sake, here’s how we would do it in Siege:

    locator
    .Register(Given.When(user=> user.Account.Balance >= 0).Then())
    .Register(Given.When(user => user.Account.Balance < 0).Then())
    .Register(Given.When(status => status == status.IsOffline).Then

    From here, there’s a couple of ways to do it. I’ll approach this with “non-awareness” to show another side of it.

    On log in, after current user has been determined:

    locator.AddContext(currentUser);

    Now all the information is available for the first two rules (well funded versus overdrawn). Additionally, we could have made the rules based on Account instead of user and passed that as context instead. But, I digress.

    On your account accessor page (or whatever mechanism used to view your account), assume you got a system failure of some sort for a different user.

    try
    {
    //access your account, assume at this point IAccountService is a WellFundedAccountService
    }
    catch(Exception)
    {
    locator.AddContext(new SystemStatus { IsOffline = true, MinutesUntilOnline = 5 });
    //assume internally that after the timer it resets to IsOffline = false
    }

    Again, I would say that this is all achievable with Awareness as well. But I want to show how context is accumulated throughout an application separate from the point of registration. The question I think is — if you were to use StructureMap’s approach, how many types would you have to create, and register, and get instance to and update as you go?

    I’d be curious to know. I don’t know StructureMap super-well. And if, great! I think this is a better use of containers than a 1:1 interface-to-type decoupling.

    Sorry for the blog-within-a-blog, I guess I’m just long-winded. :)

  • bordev

    hi mbratton,

    What I’m wondering is what is the real advantage of moving typical controller logic – choosing the right implementation based on some UI selection – to the container in the registry section and make it smart?

    This leakage of controller logic into the container clouds the distinct create/destroy concerns of the container and I wonder if this is a good thing regarding maintainability of your UI and controllers in this example?

    Losing the service locator tp make stuff less ugly is one thing but I think it might be better to leave the implementation selection in the controller. (Then inject a typed-factory using the windsor to lose the service locator.)

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    Hi bordev,

    I don’t really consider that to be implementation selection to be controller logic or business logic. I consider it to be #1 responsibility of the container. At the end of the day all that container setup says is this:

    When I select Vendor X on this page, I want to get an instance of VendorXService so that when I call a method (say, service.Submit()) it sends the info to the correct vendor.

    I don’t see anything inherently wrong with moving those sorts of decisions out of your controller and into your container. It’s application plumbing moreso than anything.

  • bordev

    mbratton> the fact that I would consider it as business logic is because you’re throwing in environmental applicaton variables in the container’s equasion which determine the plumbing during runtime.

    IMO application plumbing needs only be done at design time and scattered business logic avoided so I’m not really convinced.

    though it’s an interesting thin line where responsibilities of container starts/ends.

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    @bordev

    Well, upon reflection there are cases where it could be business-y logic. Maybe I want to pick implementations based on some business rule.

    But… in the end I still think of that sort of thing as noise. Anytime I have to deal with picking an implementation, it’s noise to me. Doesn’t matter if business rules are driving it.

    Siege has a construct where you can bundle things like this together. We bundle similar registrations together as cohesive units for a variety of reasons… portability of registrations across apps, for example. Another reason may be to encapsulate that sort of business logic together. The construct is called Using.Convention.

    I still think that decisions about which type to create belong in the container. I get the concern about the rules … and honestly I could probably split those out into encapsulated objects that could be given to the container versus constructed directly into it.

    I’m going to think about that one … I think maybe there is a case for both usages. Thanks for the food for thought!

  • http://blog.robinclowers.com Robin Clowers

    I see your point about the context being separate from other objects in the container, although I’m not sure how useful the distinction is.

    The implementation of your second example looks pretty much the same:

    var container = new Container(x =>

    x.For().ConditionallyUse(c =>

    {

    c.If(context => context.GetInstance().Account.Balance >= 0).ThenIt.Is.Type();

    c.If(context => context.GetInstance().Account.Balance < 0).ThenIt.Is.Type();

    c.If(context => context.GetInstance().IsOffline).ThenIt.Is.Type();

    }));

    Then you have to register the user in the container:

    container.Configure(c => c.For().Use(currentUser));

    Likewise in the catch method, you register the system status:

    container.Configure(c => c.For().Use(new SystemStatus { IsOffline = true, MinutesUntilOnline = 5 ));

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    Mmm…. I dunno. Context, to me, is a very different thing than registrations. I’m not sure the way you’re suggesting to do it would scale to cover the scenarios I’d want to cover … but I’ll take your word for it on this one :)

    Thank you for showing me more about StructureMap, though.

  • flipdoubt

    Since Siege passes the services in to the action, doesn’t this affect how you wire up your routes? Apparently not, because I can’t find anything about routes in your post, but I am confused by why you do not have to reach into the container to pull out services to put into your routes. Am I way off base?

  • http://www.lostechies.com/members/mbratton/default.aspx mbratton

    No… it’s just another type of model binding at the simplest level.