1

In Sitecore config patch files, both of the following will give the same result:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"
               xmlns:role="http://www.sitecore.net/xmlconfig/role/"
               xmlns:environment="http://www.sitecore.net/xmlconfig/environment/">
  <sitecore role:require="ContentManagement or Standalone">
    <scheduling>
      <agent type="MyAgent, MyAssembly" method="RunMyAgent" interval="00:60:00" />
      <agent type="MyAgent, MyAssembly" environment:require="dev">
        <patch:attribute name="interval">00:00:00</patch:attribute>
      </agent>
    </scheduling>
  </sitecore>
</configuration>

and a less conventional technique:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"
               xmlns:role="http://www.sitecore.net/xmlconfig/role/"
               xmlns:environment="http://www.sitecore.net/xmlconfig/environment/">
  <sitecore role:require="ContentManagement or Standalone">
    <scheduling>
      <agent type="MyAgent, MyAssembly" method="RunMyAgent" interval="00:60:00" />
      <agent type="MyAgent, MyAssembly" interval="00:00:00" environment:require="dev" />
    </scheduling>
  </sitecore>
</configuration>

In dev/standalone, these both result in:

<agent type="MyAgent, MyAssembly" method="RunMyAgent" interval="00:00:00" patch:source="MyConfigPatch.config"/>

I'm curious about that last syntax. Based on the Sitecore documentation, you might expect it to add two agents, but instead it accurately patches the interval, all the while keeping the method attribute intact:

When Sitecore applies the changes from a patch file, it tries to match each element in the patch file to an element in the existing configuration using the combination of the element name and all the attributes of the element. If there is a match, Sitecore updates the existing element. If there is no match, Sitecore inserts a new element.

What is the exact magic taking place here? How does Sitecore know which attribute to match (type), and which one to patch (interval)? (Note that this also works if I reverse the order of the attributes.) Is there some intelligence based on the context (agents) if one attribute is declared as unique?

For instance, why is the agent MyAgent being patched here?

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"
               xmlns:role="http://www.sitecore.net/xmlconfig/role/"
               xmlns:environment="http://www.sitecore.net/xmlconfig/environment/">
  <sitecore role:require="ContentManagement or Standalone">
    <scheduling>
      <agent interval="00:00:00" type="MyOtherAgent, MyOtherAssembly" method="RunMyOtherAgent" />
      <agent type="MyAgent, MyAssembly" method="RunMyAgent" interval="00:60:00" />

      <!-- Why does this patch "interval" on the second agent, and not "type" on the first? -->
      <agent interval="00:00:00" type="MyAgent, MyAssembly" environment:require="dev" />
    </scheduling>
  </sitecore>
</configuration>

It seems intuitive: obviously you're not going to want to match a configuration node based on interval, but how does Sitecore infer this? In XDT, you have to specify which attribute(s) to match, but Sitecore seems to have more intelligence.

2 Answers 2

2

Yes. It compares attributes.

To end up with 2 different sets, usually you add a hint or a name attribute to your configuration element. It serves no other purpose than instructing Sitecore, that this here is a unique element.

If you indeed wanted 1 agent, but 2 agents in dev, you would do:

  <agent name="My all round agent" type="MyAgent, MyAssembly" method="RunMyAgent" interval="00:60:00" />
  <agent name="My dev only agent" type="MyAgent, MyAssembly" interval="00:00:00" environment:require="dev" />

And Sitecore would recognise these as different.

1
  • Nice trick! But this doesn't fully answer my question. I've updated my question with a more specific example at the end, of the kind of Sitecore magic I'm trying to understand.
    – maz
    Sep 5, 2019 at 14:33
2

After some sniffing around, I finally understand what's happening. In Sitecore.Xml.Patch.ElementIdentification, there is a hard-coded list of "significant" attributes that Sitecore uses for identifying an XML element. Any other attribute will be ignored when comparing elements for a match.

For instance, hint, type and method are significant attributes, but interval is not. Therefore we have the following results:

<!-- Base elements -->
<agent type="MyOtherAgent, MyOtherAssembly" method="RunMyOtherAgent" hint="first" interval="00:01:00" />
<agent type="MyAgent, MyAssembly" method="RunMyAgent" hint="second" interval="00:02:00" />
<agent type="MyThirdAgent" method="RunThirdAgent" hint="second" interval="00:03:00" />  <!-- hint is the same -->

<!-- This patch would insert a new element, as non matches both type and hint -->
<agent type="MyAgent, MyAssembly" hint="first" interval="00:99:99" environment:require="dev" />

<!-- This patch would update the interval of the first element, as both significant attributes match -->
<agent type="MyOtherAgent, MyOtherAssembly" hint="first" interval="00:99:99" environment:require="dev" />

<!-- This patch would also update the first element, as it's a match based on the only significant attribute -->
<agent hint="first" interval="00:99:99" environment:require="dev" />

<!-- This patch would insert a new element, because there are multiple matches based on significant attribute "hint" -->
<agent hint="second" interval="00:99:99" environment:require="dev" />

Additionally, the list of known attributes can be augmented through the sitecore/patch/significantAttributes configuration node. This can only be done directly in Sitecore.config:

<patch>
  <significantAttributes>
    <add name="significantAttribute" />
  </significantAttributes>
</patch>

So, going back to a tweaked version of my question's final example:

<configuration xmlns:set="http://www.sitecore.net/xmlconfig/set/"
               xmlns:role="http://www.sitecore.net/xmlconfig/role/"
               xmlns:environment="http://www.sitecore.net/xmlconfig/environment/">
  <sitecore role:require="ContentManagement or Standalone">
    <scheduling>
      <!-- Base element -->
      <agent type="MyAgent, MyAssembly" method="RunMyAgent" interval="00:12:34"
             significantAttribute="uniqueMatch" regularAttribute="regular" />

      <!-- This will update the interval of the agent, keeping all other attributes. -->
      <agent type="MyAgent, MyAssembly" interval="00:99:99" environment:require="test1" />

      <!-- This also works. -->
      <agent method="RunMyAgent" interval="00:99:99" environment:require="test2" />

      <!-- This also works, since we declared significantAttribute as a... significant attribute. -->
      <agent significantAttribute="uniqueMatch" interval="00:99:99" environment:require="test3" />

      <!-- This has zero significant attributes specified. To my surprise, it (dangerously) updates the first agent found,
           which has no relation to the one defined. It adds "regularAttribute" and changes the interval. -->
      <agent regularAttribute="regular" interval="00:99:99" environment:require="test4" />

      <!-- Since there is no unique match based on significant attributes, this adds a new element. -->
      <agent type="MyAgent, MyAssembly" significantAttribute="somethingDifferent"
             interval="00:99:99" environment:require="test5" />

      <!-- This matches the type properly, and updates "regularAttribute" and "interval". -->
      <agent type="MyAgent, MyAssembly" regularAttribute="somethingDifferent"
             interval="00:99:99" environment:require="test6" />

      <!-- You can't change a significant attribute like "method" this way. This will insert a new element
           since there is no match based on specified significant attributes. -->
      <agent type="NewAgent, NewAssembly" significantAttribute="uniqueMatch"
             method="NewMethod" interval="00:99:99" environment:require="test7" />

      <!-- This will properly update the method and interval
           First line is what's being matched (significant attributes). Second line is what's being updated.
           Note that, as in other examples, only as many significant attributes
           as required for a unique match are needed. -->
      <agent type="MyAgent, MyAssembly" method="RunMyAgent"
             interval="00:99:99" set:method="NewMethod"
             environment:require="test8" />
    </scheduling>
  </sitecore>
</configuration>

Based on my findings, I would recommend:

  • In order to properly match elements, refer to the list of existing significant attributes.
  • If an element will potentially have siblings, make sure to target it uniquely based on those significant attributes. As mentioned by Mark Cassidy, use one like hint or name if necessary.
  • Don't use custom significant attributes. It forces a direct change to Sitecore.config and could cause confusion with other developers.
  • Avoid using the "magic" approach to patching attributes. Use patch and set.

The relevant Sitecore documentation is here and includes the list of known attributes:

ancestor, assembly, builderType, category, channelId, comment, contains, creationType, desc, description, displayName, extensions, facetKey, fieldId, fieldName, fieldType, fieldTypeName, file, find, folder, groupName, hint, hostname, id, implementationType, innerText, interface, key, messageDataType, messageItemBaseType, method, name, namespace, networkName, originalKey, path, postingConfiguration, prefix, providerName, querystring, ref, region, serviceType, sitecoreKey, statusCode, tagName, templateId, trafficType, trigger, type, typeConverter, typeName, uid, urlReferrerHost, value, verb, viewName, with, xmlControl, xmlns

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.