|
Home |
Using and Configuring MementoSource'sFor small applications the StructureMap.config file might suffice for all configuration. In even a medium sized project the StructureMap.config file gets too long to be easily maintained. Fortunately, the configuration in StructureMap is extensible to allow configuration storage in multiple files or embedded resources. You can even create and use custom configuration sources. Each PluginFamily has a MementoSource object to locate the instance configuration. The MementoSource can be configured for a PluginFamily through either the <Source> node in the StructureMap or by using the optional [PluginFamily(Source=typeof(somesource))] [LINK]attribute property. Single External Xml FileStores InstanceMemento's in an external Xml file. The memento's are in the Node Normalized style. /// <summary> /// Implementation of XmlMementoSource that reads InstanceMemento's from an external file. /// Useful to break the StructureMap.config file into smaller pieces. /// </summary> [Pluggable("XmlFile", "")]
public class XmlFileMementoSource : XmlMementoSource {
private string _filePath; private string _xpath; /// <summary> /// Default constructor /// </summary> /// <param name="FilePath">Path to the xml file that contains the instance configuration</param> /// <param name="XPath">XPath expression to the parent node that contains the InstanceMemento nodes. /// If empty, it defaults to the top node</param> /// <param name="NodeName">The name of the nodes that are InstanceMemento nodes. Useful to store /// different types of instances in the same file</param> [DefaultConstructor] public XmlFileMementoSource(string FilePath, string XPath, string NodeName) : base(NodeName, "Type", "Key", XmlMementoStyle.NodeNormalized)
{
_filePath = FilePath; _xpath = XPath; } Sample Configuration<PluginFamily Type="StructureMap.Testing.Widget.IWidget" Assembly="StructureMap.Testing.Widget" DefaultKey="Red"> <Source Type="XmlFile" FilePath="FullTesting.XML" XPath="Widgets" NodeName="Widget"/> <Plugin Assembly="StructureMap.Testing.Widget" Type="StructureMap.Testing.Widget.NotPluggableWidget" ConcreteKey="NotPluggable"/> </PluginFamily> Single Xml Memento per FileFor deeper object graphs or large number of instances of the same PluginType it might be advantageous to put each configured instance into it's own file. Finds an InstanceMemento by looking in the folder path [directory]\[Instance Key].[extension] [Pluggable("DirectoryXml")]
public class DirectoryXmlMementoSource : MementoSource {
private readonly string _directory; private readonly string _extension; private XmlMementoCreator _mementoCreator;
/// <summary> /// Stores an Xml InstanceMemento per file in a directory /// </summary> /// <param name="directory">A ";" delimited list of directories to look for mementos. DirectoryXmlMementoSource /// will use the FIRST directory it finds</param> /// <param name="extension">The file extension of the InstanceMemento files without a dot. Typically "xml"</param> /// <param name="mementoStyle">NodeNormalized or AttributeNormalized</param> public DirectoryXmlMementoSource(string directory, string extension, XmlMementoStyle mementoStyle) : base() Sample Configuration<PluginFamily Type="StructureMap.Testing.Widget.IWidget" Assembly="StructureMap.Testing.Widget" DefaultKey="Red"> <Source Type="DirectoryXml" directory="Widgets;..\..\Widgets;D:\WidgetService\Widgets" extension="xml" mementoStyle="AttributeNormalized"/> </PluginFamily> Single Embedded Xml File [Pluggable("EmbeddedXmlFile")]
public class SingleEmbeddedXmlMementoSource : XmlMementoSource {
private readonly string _path; private Assembly _assembly;
/// <summary> /// Retrieves Xml InstanceMemento's from an xml file stored as an embedded resource in an assembly. /// </summary> /// <param name="nodeName">Designates the nodes that are memento nodes</param> /// <param name="style">NodeNormalized or AttributeNormalized</param> /// <param name="assemblyName">The name of the Assembly the file is embedded into</param> /// <param name="path">The path to the embedded resource within the file</param> [DefaultConstructor] public SingleEmbeddedXmlMementoSource(string nodeName, XmlMementoStyle style, string assemblyName, string path) : base(nodeName, "Type", "Key", style)
{
_path = path; _assembly = Assembly.Load(assemblyName); } Sample Configuration<PluginFamily Type="StructureMap.Testing.Widget.IWidget" Assembly="StructureMap.Testing.Widget" DefaultKey="Red"> <Source Type="EmbeddedXmlFile" nodeName="Widget" assemblyName="StructureMap.Testing" path="StructureMap.Testing.TestData.Widgets.xml" style="AttributeNormalized"/> </PluginFamily> Single Xml Memento per Embedded FileStores InstanceMemento Xml files in a directory of embedded resources. Finds an embedded resource by the pattern [folderPath].[Instance Key].[extension] [Pluggable("EmbeddedXmlFolder")]
public class EmbeddedFolderXmlMementoSource : MementoSource {
/// <summary> /// Implementation of MementoSource that stores and retrieves an XmlInstanceMemento per Embedded Resource file /// in a named namespace. EmbeddedFolderXmlMementoSource is meant to simplify complicated object graph configurations /// by isolating each instance to a separate /// editable file. /// </summary> /// <param name="style">NodeNormalized or AttributeNormalized</param> /// <param name="assemblyName">The name of the Assembly with the embedded resources</param> /// <param name="folderPath">The root namespace of all of the mementos.</param> /// <param name="extension">The file extension of the memento files - "xml"</param> [DefaultConstructor] public EmbeddedFolderXmlMementoSource(XmlMementoStyle style, string assemblyName, string folderPath, string extension) Sample Configuration<PluginFamily Type="StructureMap.Testing.Widget.IWidget" Assembly="StructureMap.Testing.Widget" DefaultKey="Red"> <Source Type="EmbeddedXmlFolder" style="AttributeNormalized" assemblyName="StructureMap.Testing" path="StructureMap.Testing.TestData.Widgets" extension="xml"/> </PluginFamily> Templated MementosIn a case [Pluggable("Templated")]
public class TemplatedMementoSource : MementoSource {
private readonly MementoSource _innerSource; private readonly MementoSource _templateSource; /// <summary> /// Default Constructor /// </summary> /// <param name="innerSource">MementoSource that contains the Memento Templates</param> /// <param name="templateSource">MementoSource that contains instances consisting of Template valuee</param> public TemplatedMementoSource(MementoSource innerSource, MementoSource templateSource)
{
_innerSource = innerSource; _templateSource = templateSource; } Consider this InstanceMemento below. It describes a validation rule on an invoicing data structure that invalidates a date that is in the past. The rule itself can be applied to any combination of date field at either the invoice header or invoice detail level and error severity (reject the invoice or just warn). Rather than write a separate InstanceMemento file for each possible permutation we extended StructureMap to allow for a "templated" memento source that would allow us to simply fill in the blanks in a larger InstanceMemento source. Note the "{FieldName}" values in the xml. In a similar manner to NAnt, the TemplateMementoSource will substitute in values into the xml configuration just prior to hydrating the instance. <?xml version="1.0" encoding="utf-8" ?> <Rule Type="Default" ruleName="{FieldName} is post dated."> <actions> <Child Type="Error" subjectField="{FieldName}" severity="{Severity}"> <message Type="Message" template=" {FieldName} [{FieldName}] is post dated"/> </Child> </actions> <query Type="DataSetQuery" scope="{Scope}"> <filters> <Child Type="Aged" fieldName="{FieldName}" count="0" durationUnit="Days" /> </filters> <exceptions /> </query> </Rule> The actual memento for a user's rules would look something like this for the post date constraint rule: <!-- The Template attribute refers to the instance template --> <Instance Template="PostDateConstraint" FieldName="InvoiceDate" Scope="Invoice" Severity="Error" /> At runtime StructureMap will substitute the values from the InstanceMemento above into the template to create this: <?xml version="1.0" encoding="utf-8" ?> <Rule Type="Default" ruleName="InvoiceDate is post dated."> <actions> <Child Type="Error" subjectField="InvoiceDate" severity="Error" > <message Type="Message" template=" InvoiceDate [InvoiceDate] is post dated"/> </Child> </actions> <query Type="DataSetQuery" scope="Invoice"> <filters> <Child Type="Aged" fieldName="InvoiceDate" count="0" durationUnit="Days" /> </filters> <exceptions /> </query> </Rule> StructureMap looks for attribute values that begin and end with {} to determine the tokens to replace. This behavior can be overriden by using a Substitutions attribute to specify a comma-delimited list of substitutions <Rule Type="Default" ruleName="Condition on {FieldName} {Operator} {ComparisonValue}" Substitutions="FieldName,Scope,Operator,ComparisonValue,Severity,AlternativeMessage"> Creating a new MementoSourceStructureMap can be extended to add new types of configuration storage. Simply create a subclass of MementoSource and override these methods: /// <summary> /// Abstract class that is the supertype of all storage and retrieval mechanisms of /// InstanceMemento instances /// </summary> [PluginFamily] public abstract class MementoSource {
private PluginFamily _family;
private InstanceMemento _defaultMemento;
private Hashtable _externalMementos = new Hashtable(); protected MementoSource() : base() {
} /// <summary> /// Retrieves an array of all InstanceMemento's stored by this MementoSource /// </summary> /// <returns></returns> protected abstract InstanceMemento[] fetchInternalMementos(); /// <summary> /// Template pattern method. Determines if the MementoSource contains a definition for the /// requested instanceKey. /// </summary> /// <param name="instanceKey"></param> /// <returns></returns> protected internal abstract bool containsKey(string instanceKey); /// <summary> /// Retrieves an InstanceMemento for the instanceKey /// </summary> /// <param name="instanceKey"></param> /// <returns></returns> protected abstract InstanceMemento retrieveMemento(string instanceKey); /// <summary> /// String description of the MementoSource. Used in the StructureMap-Client UI. /// </summary> public abstract string Description { get; } } |