Home » Best Practice
Category Archives: Best Practice
Leverage the Sitecore Configuration Factory: Inject Value Types as Dependencies
In a previous post, I had mentioned primitive types cannot be injected into classes via Sitecore’s Configuration Factory, and further learned through exploring and experimentation that this applies to all value types in C#.
However, after some digging in Sitecore.Configuration.Factory — this lives in Sitecore.Kernel.dll — I discovered the CreateFromFactoryMethod() method which calls a static method — this method is defined in an XML attribute named “factoryMethod” — on a class defined in the “type” attribute on the XML configuration element being processed, and felt I could take advantage of this hidden gem for injecting value types into my configuration-defined classes.
Despite the fact the method defined in the “factoryMethod” attribute must be static, I decided to define an interface for classes that do value type conversions from strings, and created the following interface (don’t worry, I haven’t completely lost my mind — yes, I did say it requires a static method — but do bear with me until we get further down in this post 😉 ):
namespace Sitecore.Sandbox.Configuration { public interface IValueTypesConverter { bool ConvertToBoolean(string argument); char ConvertToCharacter(string argument); decimal ConvertToDecimal(string argument); double ConvertToDouble(string argument); float ConvertToFloat(string argument); int ConvertToInteger(string argument); } }
I then created the following class to implement the interface above:
using Sitecore.Diagnostics; namespace Sitecore.Sandbox.Configuration { public class ValueTypesConverter : IValueTypesConverter { public bool ConvertToBoolean(string argument) { bool value; Assert.ArgumentCondition(bool.TryParse(argument, out value), "argument", "argument is not a boolean!"); return value; } public char ConvertToCharacter(string argument) { char value; Assert.ArgumentCondition(char.TryParse(argument, out value), "argument", "argument is not a character!"); return value; } public decimal ConvertToDecimal(string argument) { decimal value; Assert.ArgumentCondition(decimal.TryParse(argument, out value), "argument", "argument is not a decimal!"); return value; } public double ConvertToDouble(string argument) { double value; Assert.ArgumentCondition(double.TryParse(argument, out value), "argument", "argument is not a double!"); return value; } public float ConvertToFloat(string argument) { float value; Assert.ArgumentCondition(float.TryParse(argument, out value), "argument", "argument is not a float!"); return value; } public int ConvertToInteger(string argument) { int value; Assert.ArgumentCondition(int.TryParse(argument, out value), "argument", "argument is not an integer!"); return value; } } }
Each method in the class above calls its specific value type’s TryParse() method to convert the passed string to the value type. We ensure the argument was in the correct format: an assertion is done to check if the argument was of the type the method was expecting.
I then created the following adapter which wraps an instance of a class that implements the interface defined above:
using Sitecore.Configuration; namespace Sitecore.Sandbox.Configuration { public class TypesConverter { static TypesConverter() { ValueTypesConverter = Factory.CreateObject("valueTypesConverter", true) as IValueTypesConverter; } public static bool ConvertToBoolean(string argument) { return ValueTypesConverter.ConvertToBoolean(argument); } public static char ConvertToCharacter(string argument) { return ValueTypesConverter.ConvertToCharacter(argument); } public static decimal ConvertToDecimal(string argument) { return ValueTypesConverter.ConvertToDecimal(argument); } public static double ConvertToDouble(string argument) { return ValueTypesConverter.ConvertToDouble(argument); } public static float ConvertToFloat(string argument) { return ValueTypesConverter.ConvertToFloat(argument); } public static int ConvertToInteger(string argument) { return ValueTypesConverter.ConvertToInteger(argument); } private static IValueTypesConverter ValueTypesConverter { get; set; } } }
The class above creates an instance of the ValueTypesConverter class via the Sitecore.Configuration.Factory.CreateObject() method. I felt defining the class that implements the IValueTypesConverter interface in Sitecore configuration would allow for customization if one would ever need to do so.
Further, all methods in the class above just delegate calls to the ValueTypesConverter instance.
For testing, I created the following class to serve as a <renderField> pipeline processor:
using System.Text; using Sitecore.Diagnostics; using Sitecore.Pipelines.RenderField; namespace Sitecore.Sandbox.Pipelines.RenderField { public class ValueTypesTest { public void Process(RenderFieldArgs args) { Assert.ArgumentNotNull(args, "args"); StringBuilder htmlBuilder = new StringBuilder(); htmlBuilder.AppendFormat("SomeBoolean ==> {0} <br />", SomeBoolean); htmlBuilder.AppendFormat("SomeCharacter ==> {0} <br />", SomeCharacter); htmlBuilder.AppendFormat("SomeDecimal ==> {0} <br />", SomeDecimal); htmlBuilder.AppendFormat("SomeDouble ==> {0} <br />", SomeDouble); htmlBuilder.AppendFormat("SomeFloat ==> {0} <br />", SomeFloat); htmlBuilder.AppendFormat("SomeInteger ==> {0}", SomeInteger); args.Result.FirstPart = htmlBuilder.ToString(); } private bool SomeBoolean { get; set; } private char SomeCharacter { get; set; } private decimal SomeDecimal { get; set; } private double SomeDouble { get; set; } private float SomeFloat { get; set; } private int SomeInteger { get; set; } } }
The class above defines properties that will be populated magically by Sitecore’s Configuration Factory, and builds a string of HTML to be rendered on the front-end.
I wired everything together in the following Sitecore configuration file:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <renderField> <processor type="Sitecore.Sandbox.Pipelines.RenderField.ValueTypesTest, Sitecore.Sandbox"> <SomeBoolean type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToBoolean" arg0="true" /> <SomeCharacter type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToCharacter" arg0="b" /> <SomeDecimal type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToDecimal" arg0="0.1" /> <SomeDouble type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToDouble" arg0="1234.5678" /> <SomeFloat type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToFloat" arg0=".98765" /> <SomeInteger type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToInteger" arg0="2" /> </processor> </renderField> </pipelines> <valueTypesConverter type="Sitecore.Sandbox.Configuration.ValueTypesConverter, Sitecore.Sandbox" /> </sitecore> </configuration>
When I loaded my home page, I saw that all value types defined in configuration were accounted for:
If you have any thoughts on this, or ideas around implementing this in a different way, please share in a comment.
Leverage the Sitecore Configuration Factory: Inject Dependencies Through Class Constructors
In my previous post, I showed how you can leverage Sitecore’s Configuration Factory to inject dependencies into properties of class instances — this is known as Setter injection in the Dependency injection world — and thought I would share another way you can inject dependencies into instances of classes defined in configuration: through class constructors (this is known as Constructor injection).
Suppose we have the following interface for objects that perform some kind of operation on parameters passed to their DoSomeOtherStuff() method:
namespace Sitecore.Sandbox.Pipelines.SomePipeline { public interface ISomeOtherThing { void DoSomeOtherStuff(SomeProcessorArgs args, string someString); } }
The following dummy class implements the interface above — sadly, it does not do anything:
namespace Sitecore.Sandbox.Pipelines.SomePipeline { public class SomeOtherThing : ISomeOtherThing { public void DoSomeOtherStuff(SomeProcessorArgs args, string someString) { // TODO: add code to do some other stuff } } }
In my previous post, I defined the following interface for objects that “do stuff”, and will reuse it here:
namespace Sitecore.Sandbox.Pipelines.SomePipeline { public interface ISomeThing { void DoStuff(SomeProcessorArgs args); } }
I have modified the SomeThing class from my previous post to consume an instance of a class that implements the ISomeOtherThing interface above along with a string instance:
using Sitecore.Diagnostics; namespace Sitecore.Sandbox.Pipelines.SomePipeline { public class SomeThing : ISomeThing { public SomeThing(ISomeOtherThing someOtherThing, string someString) { Assert.ArgumentNotNull(someOtherThing, "someOtherThing"); Assert.ArgumentNotNullOrEmpty(someString, "someString"); SomeOtherThing = someOtherThing; SomeString = someString; } public void DoStuff(SomeProcessorArgs args) { SomeOtherThing.DoSomeOtherStuff(args, SomeString); } private ISomeOtherThing SomeOtherThing { get; set; } private string SomeString { get; set; } } }
The Sitecore Configuration Factory will magically create instances of the types passed to the constructor of the SomeThing class defined above, and you can then assign these instances to members defined in your class.
As far as I know — I did a lot of digging in Sitecore.Kernel.dll for answers, and some code experimentation — the Sitecore Configuration Factory will only magically inject instances of class types and strings: it will not inject .NET primitive types (if I am incorrect on this, please share in a comment).
I have reused the SomeProcessor class from my previous post — I did not change any code in it:
using Sitecore.Diagnostics; namespace Sitecore.Sandbox.Pipelines.SomePipeline { public class SomeProcessor { public void Process(SomeProcessorArgs args) { DoSomethingWithArgs(args); } private void DoSomethingWithArgs(SomeProcessorArgs args) { Assert.ArgumentNotNull(SomeThing, "SomeThing"); SomeThing.DoStuff(args); } private ISomeThing SomeThing { get; set; } // is populated magically via Setter injection! } }
We can then piece everything together using a Sitecore patch configuration file:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <somePipeline> <processor type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeProcessor, Sitecore.Sandbox"> <SomeThing type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeThing, Sitecore.Sandbox"> <param hint="1" type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeOtherThing, Sitecore.Sandbox" /> <param hint="2">just some string</param> </SomeThing> </processor> </somePipeline> </pipelines> </sitecore> </configuration>
You must define <param> elements in order to pass arguments to constructors. The “hint” attribute determines the order of the parameters passed to the class constructor, though I believe using this attribute is optional (if I am wrong on this assumption, please share in a comment below).
If you have any thoughts on this, please drop a comment.
Leverage the Sitecore Configuration Factory: Populate Class Properties with Instances of Types Defined in Configuration
I thought I would jot down some information that frequently comes up when I am asked to recommend plans of attack on projects. The first recommendation I always give for any Sitecore project is: define as much as you possibly can in Sitecore configuration. Doing so introduces seams in your code: code that does not have to change when its underlying behavior changes — think interfaces. 😉
When defining types in Sitecore configuration, you are leveraging Sitecore’s built-in Dependency Injection framework: Sitecore’s Configuration Factory will magically inject instances of classes — yes, you would define these in configuration files — into properties of classes that are used for your pipeline processors, event handlers, and other Sitecore configuration-defined objects.
For example, suppose we have the following interface for classes that change state on an instance of a phony subclass of Sitecore.Pipelines.PipelineArgs:
namespace Sitecore.Sandbox.Pipelines.SomePipeline { public interface ISomeThing { void DoStuff(SomeProcessorArgs args); } }
Let’s create a fake class that implements the ISomeThing interface above:
namespace Sitecore.Sandbox.Pipelines.SomePipeline { public class SomeThing : ISomeThing { public void DoStuff(SomeProcessorArgs args) { // it would be nice if we had code in here } } }
We can then define a class property with the ISomeThing interface type in a class that serves as a pipeline processor:
using Sitecore.Diagnostics; namespace Sitecore.Sandbox.Pipelines.SomePipeline { public class SomeProcessor { public void Process(SomeProcessorArgs args) { DoSomethingWithArgs(args); } private void DoSomethingWithArgs(SomeProcessorArgs args) { Assert.IsNotNull(SomeThing, "SomeThing must be set in your configuration!"); SomeThing.DoStuff(args); } private ISomeThing SomeThing { get; set; } // this is populated magically! } }
The class above would serve as a processor for the dummy <somePipeline> defined in the following configuration file:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <somePipeline> <processor type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeProcessor, Sitecore.Sandbox"> <SomeThing type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeThing, Sitecore.Sandbox" /> </processor> </somePipeline> </pipelines> </sitecore> </configuration>
In the configuration file above, we defined a <SomeThing /> element within the processor element of the <somePipeline> pipeline, and this directly maps to the SomeThing property in the SomeProcessor class shown above. Keep in mind that names matter here, so the name of the configuration element must match the name of the property.
Until next time, have a Sitecoretastic day!
Labels and Literals and Field Controls, Oh My!
I wish I could tell you I’ve discovered the panacea to extirpate common issues inherent in many Sitecore development projects and this article would enumerate a recipe for curtailing these, or how I’ve discovered a hidden gem within Sitecore.Kernel.dll that your future projects cannot live without.
Sadly, this article will do none of these things — it will not knock your socks off, and may even leave you saying to yourself “come on Mike, tell me something I don’t already know”.
Yet, I feel adamantly compelled to pen this article given an egregious practice I’ve seen employed by numerous Sitecore developers at multiple Sitecore development shops over the span of five years.
What is this foul practice? It’s the act of building HTML in your C# code using content from Sitecore fields and displaying this HTML via ASP.NET Literal or Label Web Controls.
No doubt, you have seen some code like the following in a Sitecore solution, or have even written code like this yourself (please note these code examples haven’t been tested, aren’t robust, and are only given as pedagogical illustrations):
An ASP.NET User Control containing a Literal Web Control:
Code-behind for the User Control:
In the above code-behind, the author composed some link HTML using content within a Sitecore General Link field named ‘My Link’.
Compare that with the following:
An ASP.NET User Control containing Sitecore Link Field Control:
Code-behind for the User Control:
Notice how much less code was needed to wire-up the Link Field Control. The Link Field Control will render link HTML if the current Item has a link set on the ‘My Link’ General Link field — including the target attribute, if a target is set on the General Link field.
You might be saying “Mike, this is great, but we don’t want this working in the Page Editor — we want this field to be completely locked down since it’s in a global Item”. Well, you could use the following to disable your Field Control in the Page Editor, and have the ability to change it later without having to do a full code build (assuming you’re using the Web Application model in ASP.NET):
If you are uncertain about which Field Control to use, you could always poke around in Sitecore.Web.UI.WebControls using .NET Reflector, or could opt to use a FieldRenderer Web Control — all Sitecore Field Controls use an instance of a FieldRenderer to output their HTML:
Using a FieldRenderer would also aid in allowing you to change a field’s type later without breaking its presentation in the UI — as long as the field type can be rendered by the FieldRenderer.
That’s it for now. Happy coding!
-Mike