Tuesday 6 September 2011

Implement Templating Solution

Hi,

I have now worked on a couple of projects where xRM has been asked to provide a design element to the system.

In this typical scenario I am asked to implement a system where a series of templates are used to "stamp" out a collection of entity records that will then be filled in.  In my current project I am building a question engine that groups questions into pre-defined collections, each collection can then be filled in by different clients of my client (if that makes sense).  So each collection is copied to become an instance and each question the template holds becomes a question instance.  Each instance contains fields that are filled in on a "per instance" basis and each template can contain any number of values to be copied across on instance creating.

Of course copying between the template and instance would be simple if we just let the end user go into each template and create a new instance through the standard relationship mapping.  But in these instances I need to record the correct template being used and the correct container that the instances are then assigned to.  In this case the container itself first points to templates and on checking a box and saving the instances are created by a plug-in.  After this first run there can be no more instances added to the container.

So the problem presented was how to implement a C# solution that allowed effective templating without constantly re-writing the code?

I decided to write a mapping class.  This would take the full template entity and the newly created instance entity and map between the two.


     internal class CopyTemplate
    {
        private string _MatchPattern = "_template_";
        internal CopyTemplate() { }

        internal string MatchPattern
        {
            get { return _MatchPattern; }
            set { _MatchPattern = value; }
        }

        internal Entity MapTemplate(Entity Template, Entity Instance)
        {
            Entity _instance = Instance;

            foreach(KeyValuePair<string,object> _attr in Template.Attributes)
            {
                if (_attr.Key.Contains(_MatchPattern))
                {
                    _instance[_attr.Key.Replace(_MatchPattern, "_")] = _attr.Value;
                }
            }

            return _instance;
        }
    }


This code would then be called within the plugin with something along these lines:


         private void CreateInstanceExample(Entity e, EntityReference Reg)
        {
            CopyTemplate _template = new CopyTemplate();
            Entity s = new Entity("abc_standard");
            s.Attributes["abc_name"] = e.Attributes["abc_name"].ToString().Replace("Template", "");
            s.Attributes["abc_registrationid"] = Reg;
            s.Attributes["abc_standardtemplateid"] = new EntityReference("abc_standardtemplate", e.Id);

            s = _template.MapTemplate(e, s);
            _service.Create(s)
        }


In this example I look for every field in the template entity that has the word _template_ in the field name and copy it to the field in the instance that lacks the _template_ pattern.  s being the new entity instance being created and e being the template passed into this procedure from a foreach block processing the results of a query to look up the templates elsewhere   As long as the fields are the same type and follow this naming convention then this will allow the end user's system admin to add new template fields and have them copied across without having to engage a developer to write code to explicitly match them.

e.g. abc_template_field1 would map to abc_field1

This code would also cope with templates and instances being created with different publishing authorities (and therefore different object prefixes, so abc_template_field1 would map to xyz_field1)