November Happy Hour will be moved to Thursday December 5th.

Stefan Forsberg
Mar 30, 2010
  10947
(0 votes)

Using StructureMap to work with IDataFactoryFacade

Since I started to write this post Joel Abrahamsson has written a great introduction to what IoC is which rendered the beginning part of my post more or less redundant. So if you’re not familiar with terms like DI (dependency injection) and IoC (Inversion of Controll) I suggest you go there and read his post. Also I’m new to this so I may misuse some concepts and be slightly confused about others.

For this post lets assume we have this simple but oh so useless class:

   1: public class WriteChildrenToStartPageCount
   2: {
   3:     public WriteChildrenToStartPageCount()
   4:     {
   5:     }
   6:  
   7:     public void Write()
   8:     {
   9:         var children = DataFactory.Instance.GetChildren(PageReference.StartPage).Count;
  10:         HttpContext.Current.Response.Write("Children: " + children);
  11:     }
  12: }

Now some cool Alt.Net guy comes along and tells us that working directly against DataFactory is not a very good idea for many reasons. They will probably give you quite the talk about using HttpContext and working with WebForms too, but that’s beside the point.

 

Using constructor injection

Our friend new says that our class shouldn’t be responsible for finding it’s dependency to the DataFactory but rather being told about it. So let’s rewrite our class to do just that.

   1: public class WriteChildrenToStartPageCountV2
   2: {
   3:     private readonly EPiAbstractions.IDataFactoryFacade dataFactoryFacade;
   4:  
   5:     public WriteChildrenToStartPageCountV2(EPiAbstractions.IDataFactoryFacade dataFactoryFacade)
   6:     {
   7:         this.dataFactoryFacade = dataFactoryFacade;
   8:     }
   9:  
  10:     public void Write()
  11:     {
  12:         var children = dataFactoryFacade.GetChildren(PageReference.StartPage).Count;
  13:         HttpContext.Current.Response.Write("Children: " + children);
  14:     }
  15: }

Our class now takes the class to use in its constructor (this is called constructor injection). The class can now be more easily be tested since we can either create our own class that implements the interface or simply mock it. Plus this loosely coupled code make you feel all warm on the inside.

Lets say that we want to call this class on our start page, like this:

   1: public partial class Default : TemplatePage
   2: {
   3:  
   4:     protected override void OnLoad(System.EventArgs e)
   5:     {
   6:         base.OnLoad(e);
   7:  
   8:         Service.WriteChildrenToStartPageCountV2 write = 
   9:             new EPiServer.Service.WriteChildrenToStartPageCountV2(EPiAbstractions.DataFactoryFacade.Instance);
  10:         write.Write();
  11:     }
  12: }

That’s not too much of a hassle, but since we’re creating a whole site we discover that we want to send in that class every time we come across the IDataFactoryFacade interface, it gets a bit tedious. And if we ever wanted to change the concrete class we would have to find every usage of that class and change it which, while easy using a search and replace, feels kind of smelly.

 

Using StructureMap

StructureMap is an IoC (Inversion of Control) tool that solves this problem. Please note I’m using a slightly old version still (2.5.2) so the syntax (or rather the method names) has changed a bit in the newer versions.

We’re going to use StrucutreMap to configure our app to always EPiAbstractions.DataFactoryFacade.Instance whenever an interfaces of type EPiAbstractions.IDataFactoryFacade is requested. We’ll put this code in the Application_Start method in global.asax (after adding a reference to the StructureMap dll).

   1: ObjectFactory.Initialize(x =>
   2: {
   3:     x.ForRequestedType<EPiAbstractions.IDataFactoryFacade>()
   4:         .TheDefault.IsThis(EPiAbstractions.DataFactoryFacade.Instance);
   5: }
   6: );

This code does exactly what it looks to do, it informs StructureMap to use EPiAbstractions.DataFactoryFacade.Instance whenever the type EPiAbstractions.IDataFactoryFacade is requested. So now we’re going to be asking StrucutreMap to give us the object we want and it will magically resolve all our dependencies for us.

   1: Service.WriteChildrenToStartPageCountV2 write = 
   2:     ObjectFactory.GetInstance<EPiServer.Service.WriteChildrenToStartPageCountV2>();
   3: write.Write();

Notice that we don’t have to inform the constructor of WriteChildrenToStartPageCountV2 which class to use for IDataFactoryFacade. What we’re doing here is using StructureMap and it’s ObjectFactory as a Service Locator to resolve the dependencies we might have.

Now if we ever wanted to change which concrete class to use we only have to change the code in one place.

 

Auto Wiring

While the above method works constantly calling ObjectFactory.GetInstance everytime we need IDataFactoryFacade is actually considered somewhat of an IoC anti-patterns. What you want is to have as few explicit calls to GetInstance as you can in your application.

Most (if not all) of the various IoC-containers have a concept of auto wiring, that is to figure out dependencies amongst classes. To exemplify this say that we have these classes

   1: public class SomeExecutor
   2: {
   3:     public SomeExecutor(IValidator validator)
   4:     {
   5:         validator.Validate();
   6:     }
   7: }
   8:  
   9: public class Validator : IValidator
  10: {
  11:     public IRepository Repository { get; set; }
  12:  
  13:     public Validator(IRepository repository) { }
  14:  
  15:     public void Validate()
  16:     {
  17:         Repository.Save();
  18:     }
  19: }
  20:  
  21: public class Repository : IRepository
  22: {
  23:     public void Save()
  24:     {
  25:     }
  26: }

So SomeExecutor has a dependency to IValidator and the concrete class we’re using for that (setup using StrucutreMap config as above) has a dependency to the IRepository. What happens when we do a GetInstance<SomeExecutor> is that StructureMap will looking at the fattest constructor (as in most parameters) and notice that we want a class that implements IValidator. It checks it’s config and find that we want to use the class Validator. This class fattest constructor in its turn wants a IRepository and so on and so on. So even though we only ask for the class at the bottom of the dependency chain StrucutreMap resolves all the dependencies for us and that’s what auto wiring is all about.

If we had been using MVC instead of WebForms this would have been simple to use because you can control the creation of all controls using your own controller factory. WebForms unfortunately has no similar thing for controlling the creation of a System.Web.UI.Page so we have to use a little trick called Setter injections here. Bare with me.

 

One approach to using StructureMap in WebForms

First we create a simple front end interface that has a property of type IDataFactoryFacade. The class that implements the interface takes this dependency via constructor injection like this

   1: public interface IFrontEndService
   2: {
   3:     EPiAbstractions.IDataFactoryFacade DataFactoryFacade { get; set; }
   4: }
   5:  
   6: public class FrontEndService : IFrontEndService
   7: {
   8:     public EPiAbstractions.IDataFactoryFacade DataFactoryFacade { get; set; }
   9:  
  10:     public FrontEndService(EPiAbstractions.IDataFactoryFacade DataFactoryFacade)
  11:     {
  12:         this.DataFactoryFacade = DataFactoryFacade;
  13:     }
  14: }

 

We then create a class that inherits from EPiServers TemplatePage.

   1: public class TemplatePage : EPiServer.TemplatePage
   2: {
   3:     public Abstractions.IFrontEndService FrontEndService { get; set; }
   4:  
   5:     protected override void OnLoad(EventArgs e)
   6:     {
   7:         base.OnLoad(e);
   8:  
   9:         ObjectFactory.BuildUp(this);
  10:     }
  11: }

 

This class has a property of type IFrontEndService and on line 9 some magic happens. To understand what’s going on let’s take a look on how StructureMap is configured.

   1: ObjectFactory.Initialize(x =>
   2: {
   3:     x.ForRequestedType<Abstractions.IFrontEndService>()
   4:         .TheDefaultIsConcreteType<Abstractions.FrontEndService>();
   5:  
   6:     x.ForRequestedType<EPiAbstractions.IDataFactoryFacade>()
   7:         .TheDefault.IsThis(EPiAbstractions.DataFactoryFacade.Instance);
   8:  
   9:     x.SetAllProperties(y => y.OfType<Abstractions.IFrontEndService>());
  10: }
  11: );

Line 3 – 7 is the same way of telling StructureMap which classes to use like we’ve seen above. Line 9 informs it that whenever it comes across a property of type Abstractions.IFrontEndService it should set it’s value. It will look in the config and see that it should set the property to the concrete class FrontEndService which in its turn has a dependency to IDataFactoryFacade. The ObjectFactory.BuildUp(this); (line 9 in the code for TemplatePage) is the line that will activate the setting of the properties setup using SetAllProperties.

Now let’s change our code on the start page to inherit from this TemplatePage instead of EPiServers. We now have access to a FrontEndService property which in turn has a property for our DataFactoryFacade.

   1: public partial class Default : UI.TemplatePage
   2: {
   3:  
   4:     protected override void OnLoad(System.EventArgs e)
   5:     {
   6:         base.OnLoad(e);
   7:  
   8:         Service.WriteChildrenToStartPageCountV2 write
   9:             = new EPiServer.Service.WriteChildrenToStartPageCountV2(FrontEndService.DataFactoryFacade);
  10:             
  11:         write.Write();
  12:     }
  13: }

 

Conclusion

As you notice here I’m still passing in the DataFactoryFacade property to the class WriteChildrenToStartPageCountV2. This is because this class isn’t automatically wired up because we haven’t told StructureMap how and where to do that. But I’m quite happy to pass the property from the FrontEndService to the class since that property in it self is only declared and setup in one place.

It would be quite possible to extract an IWriteChildrenToStartPageCount interface from our concrete class and use that instead of our front end service approach. But since I have a feeling that I’m going to be using the DataFactoryFacade all over the application it can be nice to have it setup on the page that all EPiServer pages inherit from. It’s also quite likely that we’ll be adding other dependencies that’s nice to have available, some ILogger for instance.

The next step is to remove the dependency to HttpContext (since this is null outside of a web-context) in the Write method of the WriteChildrenToStartPageCountV2 but this is left as an exercise for the reader. ^^

Mar 30, 2010

Comments

Sep 21, 2010 10:33 AM

We are currently looking at introducing an IDataFactory interface and adding new constructor and static method overloads to classes that use DataFactory.Instance to take an IDataFactory instead.

Paul Smith
/ Paul Smith

Please login to comment.
Latest blogs
Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data...

Damian Smutek | Nov 21, 2024 | Syndicated blog

Optimizely SaaS CMS DAM Picker (Interim)

Simplify your Optimizely SaaS CMS workflow with the Interim DAM Picker Chrome extension. Seamlessly integrate your DAM system, streamlining asset...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Optimizely CMS Roadmap

Explore Optimizely CMS's latest roadmap, packed with developer-focused updates. From SaaS speed to Visual Builder enhancements, developer tooling...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Set Default Culture in Optimizely CMS 12

Take control over culture-specific operations like date and time formatting.

Tomas Hensrud Gulla | Nov 15, 2024 | Syndicated blog

I'm running Optimizely CMS on .NET 9!

It works 🎉

Tomas Hensrud Gulla | Nov 12, 2024 | Syndicated blog

Recraft's image generation with AI-Assistant for Optimizely

Recraft V3 model is outperforming all other models in the image generation space and we are happy to share: Recraft's new model is now available fo...

Luc Gosso (MVP) | Nov 8, 2024 | Syndicated blog