tag:blogger.com,1999:blog-208830102024-02-21T08:16:14.017+01:00Into the core - Independent Sitecore developer viewpointsMark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.comBlogger57125tag:blogger.com,1999:blog-20883010.post-31404124828384023372016-07-25T07:18:00.001+02:002016-07-25T07:18:34.157+02:00Gentlemen! (m/f) – Restart your View Engines!<h4>Take real advantage of the MVC pattern and step, with me, outside of the box for a minute.</h4> <p>So here’s the thing. I’ve been doing Sitecore implementations for over 10 years now. And for all of those 10 years, part of my job has been – to some extent – taking front end assets (HTML, JS and whatnot) delivered to “the project” by either an external agency or internal team and work it into the Sitecore solution.</p> <p>You all know the deal; you take these HTML files, you break them into their component parts by chasing down <DIV> elements. Lately, you may have noticed that you often get the HTML delivered in part like this already – so broken down “library” snippets of HTML. Be that as it may, you still end up having to “Sitecoreify” this into your solution.</p> <p>You replace the static text and images with Sitecore Controls (if you’re still stuck with Webforms) or maybe Sitecore Razor helpers (@Html().Sitecore.Field(“Headline”)). Or maybe you’re using Glass to wiff things up a bit, or perhaps you’re in the same camp as I am, believing the Razor view should depend on nothing but the model – so you end up with code like FieldRender.Render() into your model properties.</p> <p>Whatever your approach, I think we’ve all been down one or more of these roads.</p> <p><img alt="What If I Told You - what if I told you All of that work is obsolete" src="https://cdn.meme.am/instances/500x/69630013.jpg"></p> <p>Yep. Morpheus isn’t wrong. I’ll elaborate.</p> <h1></h1> <h2>What if I told you – you could achieve all of this:</h2> <ul> <li>Never mess around with another View file again. You’re a Sitecore rockstar, why would you be spending your time on trying to inject “col-md-40” class attributes into frontend elements when you could be spending your time on better eXperience Editor support?</li> <li>Not to mention; having to inject class attributes based on back-end functionality and data just feels <em>oh so n-tier-wrong</em> to begin with.</li> <li>Remove anywhere from 20% to 50% of your Sitecore project implementation time.</li> <li>Remove anywhere from 20% to 50% of your Sitecore project implementation time. I repeated this on purpose. Try measuring your own time, spent on fiddling with views.</li> <li>Achieve complete separation of frontend and backend parts of your project. The “No man’s land” that currently exists somewhere in the .cshtml space can be eliminated.</li> <li>Your frontend developers would never need to leave their OSX Macbooks and boot up Windows.</li> <li>Get people working on what they do best; instead of having to require people on your team with skillsets that look like this:</li> <ul> <li>Must know c#, javascript, handlebars, Sitecore, SQL, SQL Replication, MongoDB, SOLR, Lucene, ASP.NET MVC, CSS, Responsive Design, Photoshop, NodeJS, jQuery, jQuery UI, Bootstrap, Knockout and how to fish.</li></ul> <li>When instead these concerns should be (and almost without exception always are, in the real world) separated:</li> <ul> <li>C#, Sitecore, ASP.NET MVC (Sitecore Developers)</li> <li>Javascript, Handlebars, CSS, Responsive Design, NodeJS, jQuery, jQuery UI, Bootstrap, Knockout (Frontend Developers)</li> <li>SQL, SQL Replication, MongoDB, SOLR, Lucene (Application Engineers)</li> <li>Who fishes, anyway?</li></ul> <li>Get “backend” developers (Sitecore) working on what they do best. Model Sitecore, set up IA, implement features – present it all in the form of Models. (<strong>M</strong>).</li> <li>Get “frontend” developers working on what they do best. Handlebars, frontify, node, SASS and so on – anything and everything that runs on the client – and feed their Views (<strong>V</strong>) with a mock of the Model (<strong>M</strong>).</li> <li>Connect the two worlds with Controllers to render the final results server-side and send it off to whatever client happens to be connected (<strong>C</strong>).</li></ul> <p>Have I got your attention yet?</p> <h2>This is how we do-o-o it</h2> <p>Admit it, I now managed to get that song spinning in your mind. Don’t worry, it will go away in a couple of hours ;-)</p> <p>So yea. The technology exists. I’ve been hinting at it for a while, some documentation and explanation effort is underway. I hope to find time to aid in some of this documentation and explanation effort as well – but for right here and now, my aim for this post is just to serve as an eye opener, thought provoker, and see if I can get a bit more community engagement going.</p> <p>What I’m on about is <a href="https://github.com/namics/NitroNetSitecore" target="_blank">NitroNet for Sitecore</a>.</p> <h2>NitroNet for Sitecore in less than 5 minutes.</h2> <p>Don’t worry if most (or all) of the below Open Source projects I mention now are completely unknown to you. If you are like me, you probably know as little about the whole frontend developer stack as you can get away with – and it is exactly this stack that NitroNet for Sitecore aims to bridge the gap to. Stay with me.</p> <p><a href="https://github.com/namics/NitroNetSitecore" target="_blank">NitroNet for Sitecore</a> is an extension to <a href="https://github.com/namics/NitroNet" target="_blank">NitroNet</a>. It adds Sitecore concepts to the stack, in form of placeholders, eXperience Editor support and so on.</p> <h3>What is NitroNet?</h3> <p>NitroNet is an ASP.NET MVC View Engine that replaces Razor. It parses Handlebars.js templates (server side), based on OS code from <a href="https://github.com/namics/TerrificNet" target="_blank">TerrificNet</a> and Chris Sainty’s <a href="https://github.com/csainty/Veil/tree/master/Src/Veil.Handlebars" target="_blank">Veil Engine</a>. </p> <p>So in simpletalk: <a href="https://github.com/namics/NitroNet" target="_blank">NitroNet</a> replaces Razor to do Handlebars server side instead of Razor Views.</p> <p>(but you can still use Razor Views, should you need to)</p> <p><a href="https://github.com/namics/NitroNet" target="_blank">NitroNet</a> is the server side component for <a href="https://github.com/namics/generator-nitro/" target="_blank">Nitro</a>.</p> <h3>What is Nitro?</h3> <p>Frontend developer framework. I’m actually not being vague intentionally here, I simply don’t know all that many details LOL :D But that’s the beauty here; I don’t need to know all that much about frontend – and I like it like that.</p> <p>So here’s a few highlights. And remember; this stack is all your frontend developers will need to worry themselves about. They don’t need Windows, they don’t need a local Sitecore running.</p> <ul> <li>100% file based</li> <li>Runs on Mac, Win and Linux (and who knows, maybe more)</li> <li>Encourages <a href="http://bradfrost.com/blog/post/atomic-web-design/" target="_blank">Atomic Design</a> and <a href="https://en.bem.info/method/definitions/" target="_blank">BEM</a> concepts</li> <li>Comes with its own lightweight web server</li></ul> <p>So essentially, frontend developers will be “doing their thing” on their beloved Macbooks, developing components using Atomic Design/BEM via Handlebars, running and testing it locally and when they’re done they will even tell you (the Sitecore developer) what the model they require, looks like. All you need to do, is deliver it.</p> <p>Take a look here, for more information. Better yet, point your resident frontend developer team in this direction and hear their thoughts: <a title="https://github.com/namics/generator-nitro/blob/master/app/templates/project/docs/nitro.md" href="https://github.com/namics/generator-nitro/blob/master/app/templates/project/docs/nitro.md">https://github.com/namics/generator-nitro/blob/master/app/templates/project/docs/nitro.md</a></p> <h2>So ummm….</h2> <p>Yea I know. Scope of this goes a little beyond “should we use this or that Dynamic Placeholder solution in our project?”.</p> <p>I tell you this though; this is what real architecture looks like. In my view. Separation of frontend and backend concerns is key for a list of reasons so long, I wouldn’t even know where to begin. I challenge you to consider these things:</p> <ul> <li>Having your frontend and backend developers being able to work in complete parallel – all they need to discuss early on, is the Model.</li> <li>Having your frontend developers work on their tools and platform of choice, not needing to get them running Sitecore locally or even boot up Windows.</li> <li>This goes for post go-live changes too. If the model doesn’t change, no Sitecore resource need even be involved in a fix.</li> <li>100% file based JSON models also means, you could hook into the <a href="https://github.com/JakobChristensen/Sitecore.Pathfinder" target="_blank">Sitecore PathFinder</a> project and possibly get your Sitecore templates, Rendering items and so on auto-generated. I’ll gladly get involved if a community effort is kicked up around this.</li> <li>Completely removing the need for Sitecore developers to fiddle around with view files, hiding HTML blocks, injecting attributes, wasting their time. Sitecore devs don’t really come cheap, nor in particular abundance.</li></ul> <p>So yea. The scope of this certainly promises more than adequate ROI. I encourage you to verify my claim on this :-)</p> <h2>Quick list of links and references:</h2> <ul> <li>Nitro: <a title="https://github.com/namics/generator-nitro" href="https://github.com/namics/generator-nitro">https://github.com/namics/generator-nitro</a></li> <ul> <li>Nitro project template: <a title="https://github.com/namics/generator-nitro/blob/master/app/templates/project/docs/nitro.md" href="https://github.com/namics/generator-nitro/blob/master/app/templates/project/docs/nitro.md">https://github.com/namics/generator-nitro/blob/master/app/templates/project/docs/nitro.md</a></li></ul> <li>NitroNet: <a title="https://github.com/namics/NitroNet" href="https://github.com/namics/NitroNet">https://github.com/namics/NitroNet</a></li> <li>NitroNet for Sitecore: <a title="https://github.com/namics/NitroNetSitecore" href="https://github.com/namics/NitroNetSitecore">https://github.com/namics/NitroNetSitecore</a> & <a title="http://www.nitronet.io/" href="http://www.nitronet.io/">http://www.nitronet.io/</a></li> <li>TerrificNet: <a title="https://github.com/namics/TerrificNet" href="https://github.com/namics/TerrificNet">https://github.com/namics/TerrificNet</a></li> <li>Veil.Handlebars: <a title="https://github.com/csainty/Veil/tree/master/Src/Veil.Handlebars" href="https://github.com/csainty/Veil/tree/master/Src/Veil.Handlebars">https://github.com/csainty/Veil/tree/master/Src/Veil.Handlebars</a></li> <ul> <li>Note: If for some reason your frontend team doesn’t like Handlebars, this can be replaced here</li></ul> <li>Introduction Video by Oliver Eisenhut: <a title="https://www.youtube.com/watch?v=_nnH6cszfQI" href="https://www.youtube.com/watch?v=_nnH6cszfQI">https://www.youtube.com/watch?v=_nnH6cszfQI</a></li> <li>#NitroNet: <a title="https://twitter.com/hashtag/NitroNet?src=hash" href="https://twitter.com/hashtag/NitroNet?src=hash">https://twitter.com/hashtag/NitroNet?src=hash</a></li> <li>Fabian Geiger: <a title="https://twitter.com/naibaf_net" href="https://twitter.com/naibaf_net">https://twitter.com/naibaf_net</a> (get Tweeting you lazy bum ;-))</li> <li>Daniel Scherrer: <a title="https://twitter.com/daniiiol" href="https://twitter.com/daniiiol">https://twitter.com/daniiiol</a></li></ul> <p>Start your engines!</p>Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com0tag:blogger.com,1999:blog-20883010.post-50804749531735033932016-06-02T01:35:00.000+02:002016-06-02T09:42:26.752+02:00Decennial Series #3 – Sitecore.Context is an anti-pattern<h3>And I’ll even attempt to tell you why</h3>
<p>If you’re in any way or form following trends for “normal” development (should such a thing exist), you will no doubt have come across one or both of these terms:</p>
<ul>
<li>Inversion of Control
<li>Dependency Injection</li></ul>
<p>You might even know what they mean. And why these terms are so popular these days.</p>
<p>Or you might not. But you’re implementing code following these principles anyway. Because your boss, team lead, customer, client or girlfriend demands that you do.</p>
<p>Either way, I’ll just do a very quick explanation of what these mean. If you know enough about these terms to tell me I’m oversimplifying this, this introduction wasn’t meant for you anyway. Skip to the good stuff further down.</p>
<h1>IOC and DI in less than 5 minutes</h1>
<h2>Inversion of Control</h2>
<p>Inversion of Control (IoC from here on) is the principle, that nothing in your system should make decisions on exactly which other systems to “talk” to. So your CustomerManager class shouldn’t really get to decide, it wants to talk to a SqlCustomerProvider. Your BasketController shouldn’t really get to decide, everything should be stored in HttpContext.Current.Session.</p>
<p>And why is that?</p>
<p>Because things change. They change over time, as customer requirements change. Someone might later want those BasketItems stored in a back-end Sql table so the Basket isn’t lost between user visits to your site. Someone might not want to pay good money for a Sql Server license to store only 200 customers.</p>
<p>Things change, is my point. And we’re trying to write software to best accommodate that.</p>
<p>But how can I write my BasketController if I can’t store my BasketItems anywhere?</p>
<p>But you can. And in writing it, you do obviously get to decide WHAT you require in order to store and retrieve your BasketItems. You just don’t write the actual implementation. You produce a “blueprint” of the requirements you have.</p>
<p>In C#, we call this an “Interface”.</p>
<h3>So in short (simple) summary. Inversion of Control (IoC).</h3>
<p>You might have started out like this:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #008800">public</span> <span style="font-weight: bold; color: #008800">class</span> <span style="font-weight: bold; color: #bb0066">BasketController</span>
{
<span style="font-weight: bold; color: #008800">private</span> <span style="font-weight: bold; color: #008800">readonly</span> BasketStorage _bs;
<span style="font-weight: bold; color: #008800">public</span> <span style="font-weight: bold; color: #0066bb">BasketController</span>()
{
_bs = <span style="font-weight: bold; color: #008800">new</span> BasketStorage();
}
<span style="font-weight: bold; color: #008800">public</span> List<BasketItem> GetBasket(<span style="font-weight: bold; color: #333399">int</span> customerId)
{
<span style="font-weight: bold; color: #008800">return</span> _bs.GetBasket(customerId).ToList();
}
<span style="font-weight: bold; color: #008800">public</span> <span style="font-weight: bold; color: #008800">void</span> <span style="font-weight: bold; color: #0066bb">StoreBasket</span>(<span style="font-weight: bold; color: #333399">int</span> customerId, IEnumerable<BasketItem> items)
{
_bs.StoreBasket(customerId, items);
}
}
</pre></div>
<p>So what’s the big deal? Well it’s about control. You might argue; “so what if the client changes her mind, and wants this to come from Sql instead of Session? I’ll just make a new BasketStorage class, job done”.</p>
<p>And you’re right of course.</p>
<p>But what about me? the consumer of your API? or me, the maintenance developer who – 3 years from now – actually want to do both? Store BasketItems in SessionState – and then persist them to Sql when Session expires? Why can’t I be your friend?</p>
<p>I’m the consumer of this API, and it’s not unreasonable that I have some form of control over these matters.</p>
<p>So the way we both get happy, is find common ground. With just a little tweaking (and tools like Resharper does like 95% of this work for you anyway; go ask your boss for a license), we can both win. You still get to easily replace one BasketStorage for another, and I get a choice in the matter too.</p>
<p>So we (you, actually) make up the blueprint. The Interface.</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #008800">public</span> <span style="font-weight: bold; color: #008800">interface</span> IBasketStorage
{
<span style="font-weight: bold; color: #008800">void</span> <span style="font-weight: bold; color: #0066bb">StoreBasket</span>(<span style="font-weight: bold; color: #333399">int</span> customerId, IEnumerable<BasketItem> items);
IEnumerable<BasketItem> GetBasket(<span style="font-weight: bold; color: #333399">int</span> customerId);
}
</pre></div>
<p>And you apply this to your existing BasketStorage class.</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #008800">public</span> <span style="font-weight: bold; color: #008800">class</span> <span style="font-weight: bold; color: #bb0066">BasketStorage</span> : IBasketStorage
{
<span style="font-weight: bold; color: #008800">public</span> <span style="font-weight: bold; color: #008800">void</span> <span style="font-weight: bold; color: #0066bb">StoreBasket</span>(<span style="font-weight: bold; color: #333399">int</span> customerId, IEnumerable<BasketItem> items)
{
<span style="font-weight: bold; color: #008800">throw</span> <span style="font-weight: bold; color: #008800">new</span> <span style="font-weight: bold; color: #0066bb">NotImplementedException</span>();
}
<span style="font-weight: bold; color: #008800">public</span> IEnumerable<BasketItem> GetBasket(<span style="font-weight: bold; color: #333399">int</span> customerId)
{
<span style="font-weight: bold; color: #008800">throw</span> <span style="font-weight: bold; color: #008800">new</span> <span style="font-weight: bold; color: #0066bb">NotImplementedException</span>();
}
}
</pre></div>
<p>(and you finish up the methods that I am too lazy to flesh out in this blogpost, naturally)</p>
<p>So far, this is a matter of maybe 20 keystrokes and a few Resharper tricks. And you’re almost done. One last thing.</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #008800">private</span> <span style="font-weight: bold; color: #008800">readonly</span> IBasketStorage _bs;
<span style="font-weight: bold; color: #008800">public</span> <span style="font-weight: bold; color: #0066bb">BasketController</span>(IBasketStorage bs)
{
_bs = bs;
}
</pre></div>
<p>And that’s it. You’ve now achieved IoC with Dependency Injection (DI). Well done you.</p>
<p>“But instead of having just 1 place in my code where I created my BasketStorage() class, I am now forced to spread it out everywhere in my codebase!?!? SURELY that cannot be better?”</p>
<p>And you’re right indeed. Enter: The Service Locator. Notice how I made the person next to you just spill coffee?</p>
<p>How are we for time? got a couple of minutes before the 5 minutes are up, yea?</p>
<h3>Service Locator, also in very short and simple terms.</h3>
<p>If you’ve heard the terms IoC and DI, you have absolutely also heard someone – at one point or another – mention “<a href="http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/" target="_blank">Service Locator is an Anti-Pattern</a>”.</p>
<p>And it can be. Indeed. But let me also tell you… somewhere in your system, SOMEONE is going to have to instantiate some classes and services at some point. And they are going to need to locate these classes and services. Stay with me on this. We’ll get through this, you and I.</p>
<p>The most direct way to go about, getting these dependencies injected – is to use the Service Locator Pattern. It looks like this:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #333399">var</span> basketStorage = DependencyResolver.Current.GetService<IBasketStorage>();
<span style="font-weight: bold; color: #333399">var</span> basketController = <span style="font-weight: bold; color: #008800">new</span> BasketController(basketStorage);
</pre></div>
<p>Or the slightly better version:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #333399">var</span> basketController = DependencyResolver.Current.GetService<BasketController>();
</pre></div>
<p>Notice how, in this last example, we don’t even worry about IBasketStorage any longer. The DependencyResolver is meant to work this out (almost) on it’s own. </p>
<p>“But how does it know?”</p>
<p>Yea, I skipped that didn’t I? I don’t really want to make this post about, how DI frameworks are configured. In Castle Windsor, it looks like this:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%">container.Register(Component.For<IBasketStorage>().ImplementedBy<BasketStorage>());
</pre></div>
<p>So now, whenever someone requests (registers a dependency for, usually in the class constructor) IBasketStorage, the DependencyResolver knows to instantiate BasketStorage() and send that off to the requesting class.</p>
<p>Just for reference, this form of DI we call “constructor injection” for hopefully obvious reasons.</p>
<p>I think our 5 minutes are up. So let’s address the elephant in the room; “Why is this an Anti-Pattern then?”.</p>
<h3></h3>
<h3>Service Locator and the bad rep</h3>
<p>It’s not so bad. Really. Not when used like I just showed here. But that’s not to say, you can’t get in a lot of trouble using SLOC. You absolutely can. Let’s return to the example from before. Imagine I had done it like this:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #008800">private</span> <span style="font-weight: bold; color: #008800">readonly</span> IBasketStorage _bs;
<span style="font-weight: bold; color: #008800">public</span> <span style="font-weight: bold; color: #0066bb">BasketController</span>()
{
_bs = DependencyResolver.Current.GetService<IBasketStorage>();
}
</pre></div>
<p>Same thing, right? Except now, my BasketController() is nice and easy to instantiate again. </p>
<p>Wrong. </p>
<p>First of all, you’ve now come full circle and achieved exactly nothing. You’ve pushed an interface down on top of BasketStorage, good on you. But to what end?</p>
<p>Dependencies are no longer injected, so DI is out. As for control. Well it’s still somewhat inverted at least – but it’s well hidden from my view. Me, the API consumer/maintenance developer/ravaging psychopath who knows where you live.</p>
<p>So when I, 3 years from now, go… “oh, I’m gonna use this fancy BasketController. Public constructor, no dependencies. I’ll just add 1.000.000 to customerId, and we can use it to store the Wishlish for the customer – I’m a genious. Long weekend, here I come. We’re in an HttpModule with no SessionState, but why should that matter?”. And I go:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #333399">var</span> bc = <span style="font-weight: bold; color: #008800">new</span> BasketController();
bc.StoreBasket(<span style="font-weight: bold; color: #6600ee">1000000</span>+customerId, wishlist);
</pre></div>
<p>Yay me. Except. Noooooo. YSOD.</p>
<p>What happened? Well I didn’t know, invoking BasketController called out using SLOC and procured IBasketStorage. I probably didn’t even know that IBasketStorage required SessionState – but as I said, I didn’t even know about the requirement for it in the first place.</p>
<p>And that, is the problem with the Service Locator.</p>
<p>It hides dependencies. It’s (sort of) IoC but without DI. It’s Prince without The NPG. It’s Bonnie without Clyde. </p>
<p>It’s just not really very good. That’s what I’m saying.</p>
<h1>The Good Stuff</h1>
<p>I told you this was coming yea?</p>
<p>So as I might have let shine through a little; I do come across zealots from time to time who will turn into an absolute jumblepot of failed arguments and internet quotes whenever I even dare mention the unspeakable Service Locator. “<em>Everybody KNOWS</em> it’s an Anti-Pattern.” (you know who you are).</p>
<p>The irony is, in our day to day work with Sitecore, you (not me) use the Service Locator Pattern ALL THE TIME. Hundreds and hundreds of times every day. Often these very same zealots use it, some of them aren’t even aware.</p>
<p>Take a look at this:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #333399">var</span> database = DependencyResolver.Current.GetService<Database>();
<span style="font-weight: bold; color: #333399">var</span> database = Sitecore.Context.Database;
</pre></div>
<p>If you think there is anything other than just a semantic difference between those two lines of code, you’ve missed a point somewhere. Start over from the top of this article.</p>
<p>Sitecore.Context is a Service Locator</p>
<p>Actually, I’ll work the font a little and repeat.</p>
<p align="center"><strong><font size="5"><u>Sitecore.Context is a Service Locator</u></font></strong></p>
<p>And it has ALL the problems of a Service Locator, too. All of them.</p>
<p>Ever tried calling some of your code – say some Url Generation code – only to find it blows up when called from a Sitecore Publishing Processor? Sitecore.Context.Site is NOT what you expect is it? </p>
<p>Or called some Sitecore code from a Unit Test? I tell you; BOOKS have been written to explain to you why this will blow up. Hint: It’s mostly to do with Sitecore.Context.Database.</p>
<p>Sitecore – in pretty much every corner and crack you look into, has services and functions that rely on other Sitecore services and functions. It is all hidden from view (although some, very far from all, is exposed in the configs), and unless you have lots and lots of Sitecore experience, predicting what calling Sitecore.Context.Database is actually going to need is near impossible.</p>
<p>There is little to no runtime DI in play anywhere; and the config files only allow you to really alter some of the dependencies on a very top level.</p>
<p>And you know what? this is fine. Well no it’s not – but it is what it is. If this ever changes, I am almost willing to bet a Dollar, Sitecore will roll out a NEW API to live side-by-side with the existing one. But let’s leave this for now and get to the punchline.</p>
<p>Every time you use Sitecore.Context (or RenderingContext.Current or pretty much any global static in the Sitecore API) – you are using the Service Locator Pattern. And it gives you all the problems that got it labelled as an Anti-Pattern to begin with.</p>
<p>I’m not saying you can avoid it, really. While there are ways to work around most of it; in some cases you just have to cease fire and accept the way things are.</p>
<p>But I’m saying you can:</p>
<ul>
<li>Be aware of it
<ul>
<li>Awareness of a problem is often the first step in minimizing the impact, the problem will have on your project</li></ul>
<li>Minimize the use of it
<ul>
<li>Declare your dependencies. Even if you can’t fix all of it; demand “SiteContext context, Database database” in your constructor if you’re going to use them. At least bring back DI.</li></ul></li></ul>
<p>Over time, this becomes second nature to you. You no longer get mysterious yellow screens because <a href="https://soen.ghost.io/sitecore-sitecontext-and-contextdatabase-oh-my/" target="_blank">your context is not what you expect</a>. You no longer increase the workload of your poor maintenance programmer by 100-fold, trying to work out what external dependencies your code has (generating a Product Url requires a Site Context, a Context Database, a Context Language, a Context Item and your SSN – seems fair, right? – real story btw, except for the SSN).</p>
<p>Now let’s get back to work. </p>Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com0tag:blogger.com,1999:blog-20883010.post-61417773665945054742016-05-18T03:51:00.000+02:002016-05-18T03:53:24.769+02:00Setup Castle Windsor for Sitecore 8.1 in 5 easy steps<h4>Getting started with Dependency Injection is easier than you think</h4> <p>So I was reading Kevin Obee (<a href="https://twitter.com/KevinObee" target="_blank">@KevinObee</a>) <a href="https://kevinobee.wordpress.com/2013/11/12/dependency-injection-with-sitecore/" target="_blank">write about Dependency Injection</a>, and how many of you out there wasn’t using it yet. While the post that inspired me addresses Webforms implementations, I also find this lacking in many MVC solutions I come across.</p> <p>Further; most of the examples you find on the web now – setting up DI for Sitecore – is obsolete as of Sitecore 8.1. These days, it’s much much easier than it used to be.</p> <p>I’m going to demonstrate using Castle Windsor as my DI container. Personal preference, I’m sure there are similar ways of doing it with AutoFAC, SimpleInjector and whatever else roams about.</p> <p>So let’s get to it.</p> <h3>Step 1 – Install packages</h3> <p>Start with a blank solution set up for MVC. Or use whatever you have on hand right now. Now get your NuGet going. We’re after the <strong>CommonServiceLocator.WindsorAdapter</strong>.</p> <h4>Via GUI</h4> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZcnYeW-6kc43cPnRUbHsrVbAotMRrtt_hhRjSbPvpui7wld2KOfBHmG9YEczYnMIx0SkfCxNbjkIu4AdoYQEnm1DKYC61bK2ffMvHtHjiDpDd_F0UbPSIDDCNWmoQeNFsnycl/s1600-h/step-1_thumb1%25255B2%25255D.gif"><img title="step-1_thumb1" style="margin: 0px; display: inline" alt="step-1_thumb1" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyD1YTd72nGd4_2_f_CexIvBx7zwnEMB7wLzZYTs577Ht0JpO7NLll8gcphAL9heFI0X7nxS4DmPPZHm-co0vpJozFwDxgBkq5X6lm8EJRXT52zJKm_CRdTRcNKuiIH6v9lur6/?imgmax=800" width="600" height="400"></a></p> <h4>or if you prefer, via the Nuget Package Manager Console.</h4> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQrwI-C1-j0SUL2kI4g5ZwGNc6i2ttbhMGJZ1cIQVAWiSUU0DML88XvlcmzWNVd4qh-ebIaK4jGvCWlE4a-XxxeXeSLtgtW0VWtwg61cS_biE9aIEHaNXklUsRoSlWJP2hBAbO/s1600-h/step-2_thumb2%25255B2%25255D.gif"><img title="step-2_thumb2" style="margin: 0px; display: inline" alt="step-2_thumb2" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgn-eLwwsRv0zsuMawJew5-bv8v8Od2ytTfblpLsaYOZuF5KsiAEnK-tEvTkD80KyKvN4Qss_R9snNbdkW50s20kZEm4cnVJ_b3ytyBecJqsMwxyxLOU9WgxpWZTRnCTcWLBNd8/?imgmax=800" width="600" height="400"></a></p> <p> </p> <h3>Step 2 – Update packages</h3> <p>Do a few updates. Personal preference here, but registration options have improved a lot since the minimum version of Castle Windsor required for this to work.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgr7vevl7k6HIzeRzo-b3G7pweLvGmYgO8mwE_q0fdlIuBnHV3LTFaB4iNjD7uWdyXzwd540zPIfkZC7COVic9PsWIqAwlC2eQcOJa2QNakk8xZUFPUgM3R2DMlHik8GdXYFNZn/s1600-h/step-3_thumb1%25255B2%25255D.gif"><img title="step-3_thumb1" style="margin: 0px; display: inline" alt="step-3_thumb1" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieQAfTEjvGexTx92ngKpxvuyaOzHFo6I9XMMtOzz4bFQbw0x__JGW3S9eo9T2Ph9PALXHBdsI3Z22UOMhfesdMLxUPy1gZ5_Y6xajs7xa26R6qeJaANSoWmCuHRTeLXlvsWFx_/?imgmax=800" width="600" height="400"></a></p> <p> </p> <h3>Step 3 – Write some code</h3> <p>But not a lot. Truly.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvwQo3dqQQg-aCTkjXtfLw2mO6zdSC81Qq_S9fIR9jEzJjv6KVfYDaapuPkHUpXYxYE7hPyvZ1yj27Xe3pM65HBZ_5LBpS-lYEjiPwzDhjY643Bz7eoX1EaL4DbkEvbIKdsW_P/s1600-h/step-4_thumb2%25255B2%25255D.gif"><img title="step-4_thumb2" style="margin: 0px; display: inline" alt="step-4_thumb2" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSOyDFC2emefUiyL7NE-lKDMYirVlp1O0WW99HsO7OQaZH5tuurJnuJAQEUmaa3TGG7uEOW9rCGwBon_rh7TuIU-Ok6DLbUXaMkOCdJ4oafBp_UuJCOM2dAdj3Tv_KGvBSG7Vh/?imgmax=800" width="600" height="400"></a></p> <p>IntelliSense… mostly a blessing, but sometimes a bit of a nag.</p> <h3>Step 4 – Line it up</h3> <p>Now we just need to wire this up. Sitecore Best Practice would be, to get this cooked up in the <initialize> pipeline. So let’s do that.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXCkQtLEQOkAPzcY9rGhdqK9xkxjdCKdNhg3VSu1cHjYRUh-UKSR0H62rCjVYbbVuPNfcuutlW3RKCUXpbyd9GB982-6ALE7UcE-uW8bAMreQjlK-arR8WvYfDNCOfp_5GJC3I/s1600-h/step-5_thumb1%25255B2%25255D.gif"><img title="step-5_thumb1" style="display: inline" alt="step-5_thumb1" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG-BfPo0seGr6XIJYHc9O69jclcAN9Zqp9-wcYPktZ6RIqQYMWdcohq4Bz0Uol9UhYWt7YhgUjHEes1VGsB1bbsn2gok7PeJv-t36t1Olj8ZdAUfmnc7OBrBZR7R4MJ-m-rHC5/?imgmax=800" width="600" height="400"></a></p> <h3>Step 5 – Join us on Slack</h3> <p>Just kidding. You’re done. That’s it. I won’t go into detail on how to actually use Dependency Injection, there are plenty of good tutorials on the web for that.</p> <p>Meanwhile I suggest you come on the <a href="https://sitecorechat.slack.com" target="_blank">Sitecore Slack Channel</a>. You’ve got plenty of spare time on your hands now, I just showed you how to do a 1 day task in 5 minutes flat ;-)</p> <p>If you’re feeling doubly lazy, <a href="https://gist.github.com/cassidydotdk/7cc9a3591e90a9914dd562f2634081e0" target="_blank">I’ve created a Gist</a> with the two files on display.</p>Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com3tag:blogger.com,1999:blog-20883010.post-27364657994200576022016-05-08T14:18:00.000+02:002016-05-08T14:24:39.042+02:00Effective use of Sitecore LinkDatabase<h1></h1> <p>Keep track of your Reference Field references</p> <p>I’ve often mentioned the Sitecore LinkDatabase, which I’ve found to be a hugely useful resource over the years but it’s getting very little exposure. And given I need to test out the Open Source version of my <a href="http://openlivewriter.org/" target="_blank">favourite blogging tool</a>, I figured I might as well hit 2 birds with 1 stone and write a post about it.</p> <h2>What is Sitecore LinkDatabase?</h2> <p>Sitecore LinkDatabase (to save my sanity, hereafter referred to as SLDB) is a simple table that keeps track of Reference Field references. And what are Reference Fields? Basically anything that stores one or more IDs as the raw field value. So Droplink, Treelist, Image, Internal Link and so on.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWGJQ-VSt_83Xe8nhcEjD6MMI-ra00OHBlPQNQkIK-UgVqQcxZ_2NIZhLDT6IMWmOD3lpp1bHtucM7aO55i4vMvEl8LDGICqrYLj8-_woWWzh3_QzJso7ZAbRTvePn-yOTX9_k/s1600-h/SNAGHTML13175c1f%25255B4%25255D.png"><img title="SNAGHTML13175c1f" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="SNAGHTML13175c1f" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ2SSS8DUuG-C8tF-GO0Wz-imd_d1zDYvR572l7YWDNARhdZqcp_YBdYBPacnqsA7cioJLRH0tzx784m-e1IXqPRfiLDGyResNZX4AUpx2eVaDHGwK49nMgWpTSR2OAPRLZL2V/?imgmax=800" width="322" height="224"></a></p> <p>And raw values looking like this:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM5exXmRlj5Jye9NMXaCxcmfvg7-GJuw8-lquVqISN8W1Du6BLp71kLz4FegkmkFJzuvx5RkZsY1sm-6LttqG79sSvb4T2FV1MqeBF19zTajupakHhz2nOHd6OC5mzJ2SNUE8Q/s1600-h/image%25255B3%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMgqcCl64K93-in0j3VF9jvzABYxe-U6v5FKtiTDzEBi7kag8mR8ws-hcITvL0Oc90t6VIIRw-n6MevY_59LnDRH5sXzrfn7XWulh5f4Nb_anS5bai9aaqHzsL6uRdKqFmmlJT/?imgmax=800" width="539" height="60"></a></p> <p>And this is where the SLDB comes into play. What it does, is keep track of those references. It basically stores something to the effect of; “Item {id} on Field {id} keeps a reference to Target Items {id}{id}{id}”. The reasons this functionality exists, is to support the Sitecore Content Editor so that when you are about to delete an item that might be used elsewhere, it pops a warning.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8Z6SlKUIiZPZSGEL4Ha9bP6XRvJf5v7VWkmOwCnteQQgDGuccz23AzNMkRM_sBZI2jRiaGiAyo7egyRPjJ20_j5Gp9M8XhxEDMaC8tD4yQwzTFVyzcV9MMsM3GLzrfG9bCx-c/s1600-h/image%25255B6%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU90uouzLsCd3qdu2dfhLxU8jTxGyYdG6wPlOvnCGYvp6lS9yaA3Gu4Bz0inp10X22oiuhCtQDUu7bHV9UBLPSjgGoH4BpWogUJh6TpQqLNidXEtYNI4tUUTUEm7dvnyoEqi44/?imgmax=800" width="244" height="161"></a></p> <p>In this case I am not given other choice (as I’m trying to delete a Template in use), but in other cases Sitecore will offer you to remove the references to the Item you’re about to delete, or relink the references to someplace else. As a side note; this, too, is immensely useful functionality if you’re refactoring your content – but that’s a story for a different day.</p> <h2></h2> <h2>The nuts and bolts</h2> <p>Currently, the SLDB looks like this:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgG3K0rHltqAhGnFvSXY5THrGNMnXHk51n0MYNgdsldI-bHvrBYqIvCFPvKWZydhJXgif6ftG0gTC3K5sggnDOKESD5hGi-9RPz3krfGmvO7lvLof_abezWcTo6Gu-Tq4bQjbU_/s1600-h/image%25255B10%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHvNAOcWYOoYGf7mnQwah38G7UfrJ9oshzTcz9AWe_ZqOwwdTwg-NzFtd9NZEgEQbwy0GO64uwLaPLUO4Jk9KHtFnJSJrvvXE8kdWakBAQ89LjkXvThNfOEdKEI-CF8DLeFE4j/?imgmax=800" width="382" height="321"></a></p> <p>It sits in your “core” database. Be mindful of this if you’re going to be putting it to use – “core” may or may not be available in your Content Delivery environment depending on your setup. Easily fixed, however. Configuration for it sits here:</p><!-- HTML generated using hilite.me --> <div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"> <span style="color: #888888"><!-- LINK DATABASE --></span>
<span style="color: #007700"><LinkDatabase</span> <span style="color: #0000cc">type=</span><span style="background-color: #fff0f0">"Sitecore.Data.$(database).$(database)LinkDatabase, Sitecore.Kernel"</span><span style="color: #007700">></span>
<span style="color: #007700"><param</span> <span style="color: #0000cc">connectionStringName=</span><span style="background-color: #fff0f0">"core"</span> <span style="color: #007700">/></span>
<span style="color: #007700"></LinkDatabase></span>
</pre></div>
<p>Migrate the “Links” table from “core” to “web” or wherever you please, and you’re good to go.</p>
<p>The day to day is handled by this event handler, you’ll find it on most of the item events.</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="color: #007700"><handler</span> <span style="color: #0000cc">type=</span><span style="background-color: #fff0f0">"Sitecore.Links.ItemEventHandler, Sitecore.Kernel"</span> <span style="color: #0000cc">method=</span><span style="background-color: #fff0f0">"OnItemCopied"</span> <span style="color: #007700">/></span>
</pre></div>
<p>And there’s also a setting. Leave it at it’s default if you’re going to be using the SLDB at runtime (if you toggle this “off”, SLDB won’t be kept up to date for “web”).</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="color: #007700"><setting</span> <span style="color: #0000cc">name=</span><span style="background-color: #fff0f0">"LinkDatabase.UpdateDuringPublish"</span> <span style="color: #0000cc">value=</span><span style="background-color: #fff0f0">"true"</span> <span style="color: #007700">/></span>
</pre></div>
<p>Caveat: If you’re using the excellent <a href="https://github.com/kamsar/Unicorn" target="_blank">Unicorn</a>; be sure to allow it to update SLDB during sync operations. It is currently off by default. You need <a href="http://kamsar.net/index.php/2016/03/Unicorn-3-1-5-Released/" target="_blank">version 3.1.5</a> or later.</p>
<h2>The nitty-gritty</h2>
<p>Right, so enough. WHY should you care? Well let me show you.</p>
<p>So I’ve set up a vanilla solution, and defined me some templates. It’s not particularly advanced, but it represents a pretty common IA challenge in our solutions today.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX3ay1EwWzV2XnH7eZzPCJd05qbvTAN2TSBH3uR0WixsrfNQ-FfycqYexHvfwLFBuoxVekl3c2lxoDrkEL3Z-4Pdq8nSy-28mMkOWWXRPSpqxEnKg_bxXWHHQ0n9gp7N9w6M5B/s1600-h/SNAGHTML134587f2%25255B4%25255D.png"><img title="SNAGHTML134587f2" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="SNAGHTML134587f2" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhteStkvBk3RO__Uxu156dxcPhzGsxwn4N0kz4Jr9qziTFnUkF_HfMDkYgiwnPlcVcjjGgGmg1tkGPMlVzAW6QcQxwJVR9J_78TyG4nK-TUjTDcyIYgSkox9hFyig1Mm5-AjHYs/?imgmax=800" width="572" height="406"></a></p>
<p>So we have a “News” template. It can exist in several categories, and has 1 Author.</p>
<p>IA challenge #1: We can’t organise “News” in category “folders”, as it is not a 1:1 relationship<br>IA challenge #2: We need news organised in a deep “folder” structure for SEO and performance</p>
<p>Using a bit of c# code so hacky I would never share it, I end up with 1000 “News” articles, with a random number of categories and so on.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiISns6tf5LPNlVWZXsOMFsBKL88eQzMma9GEZ0rh9WKPlj4Gigoz1k5tg7-4E8Ik6AsBYIVeodw2zU1bsiBOrPaM9OM9gU8BReMxiW24bXXwmHIF4SlA__Mir4tgBkvolYs6ly/s1600-h/image%25255B15%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb05flOR3Cs5rFySDGiKSDBBCzqdf-IOJIVSyoAOiolDtjaz_24kl-GiD60LWbMqDFHbtZYySW7lEMI6QbHSNea5gcOPzGcUYnKv5P5N2ATK4CLyRbBMoHcIMXo-sF5jFKgGKg/?imgmax=800" width="575" height="471"></a></p>
<p>I’ve kept the organisation simple, as this will be enough for what I’m trying to demonstrate.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmDVQEJACXjMqGodFkFWg2-1LjTeBcYsFPVt3b1oJb4Al-9-eFYCK-VS1fCcT-mWnBQqOMFdLHmXN02TTOpPATVgLZTBNMS0UP130k8dux3ZlPHqCRV-oUNpAWCaE0qxhdqc5e/s1600-h/image%25255B18%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivFxAS9IK5JmBnV83Z4M4sZtkWEzOe9S7jvBcN2phl0I5wsnBUWnWu4PBrvFnlDCg1LclUhB4N5A9uD6fgzP1S3eAmcck8jhbHcMiLQWfRaz9rDpDXXBkBvf5xxG45xAl1Mx4D/?imgmax=800" width="208" height="243"></a></p>
<p>So basically a “month” folder. In a real scenario, likely you would go 1 level deeper and include “year”. It won’t matter for this example however.</p>
<p>Now; our common problems with this are:</p>
<p>Problem #1: How do I list all “News” in a specific Category?<br>Problem #2: How do I list all “News” by a specific Author?</p>
<p>I’m going to pretend we live in a world where indexing this data isn’t an option, and we’re left with just the basic tools available to us. </p>
<p>Solution #1: Use Sitecore Query<br>Solution #2: Use Sitecore Fast Query<br>Solution #2: Use SLDB</p>
<p>Let’s see what that looks like. I cook up some rudimentary (and ugly) code:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #333399">var</span> db = Factory.GetDatabase(<span style="background-color: #fff0f0">"web"</span>);
List<Item> authors = db.GetItem(<span style="background-color: #fff0f0">"/sitecore/content/Solution Data/Authors"</span>).GetChildren().ToArray().ToList();
List<Item> categories = db.GetItem(<span style="background-color: #fff0f0">"/sitecore/content/Solution Data/Categories"</span>).GetChildren().ToArray().ToList();
ID newsTemplateId = <span style="font-weight: bold; color: #008800">new</span> ID(<span style="background-color: #fff0f0">"{5CB0FC6D-DCAA-4A51-8745-D9933F77679A}"</span>);
<span style="font-weight: bold; color: #333399">var</span> newsRoot = db.GetItem(<span style="background-color: #fff0f0">"/sitecore/content/Home/news"</span>);
<span style="color: #888888">// Make sure Sitecore is warmed up</span>
newsRoot.Axes.GetDescendants();
Response.Write(<span style="background-color: #fff0f0">"<h2>Authors</h2>"</span>);
<span style="font-weight: bold; color: #333399">var</span> sw = <span style="font-weight: bold; color: #008800">new</span> Stopwatch();
<span style="font-weight: bold; color: #008800">foreach</span> (<span style="font-weight: bold; color: #333399">var</span> author <span style="font-weight: bold; color: #008800">in</span> authors)
{
Response.Write(<span style="color: #ff0000; background-color: #ffaaaa">$</span><span style="background-color: #fff0f0">"<strong>{author.Name}</strong><br />"</span>);
sw.Start();
<span style="font-weight: bold; color: #333399">var</span> referrers = GetAuthorReferrers(newsRoot, author.ID);
sw.Stop();
Response.Write(referrers.Length + <span style="background-color: #fff0f0">" articles; Time in Ms: "</span> + sw.ElapsedMilliseconds + <span style="background-color: #fff0f0">"<hr />"</span>);
sw.Reset();
}
</pre></div>
<p>And the most important bit, how I query it.</p>
<h4>Regular Sitecore Query</h4><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%">Item[] <span style="font-weight: bold; color: #0066bb">GetAuthorReferrers</span>(Item root, ID authorId)
{
<span style="font-weight: bold; color: #333399">string</span> query = root.Paths.FullPath + <span style="background-color: #fff0f0">"//*[contains(@Author, '"</span> + authorId + <span style="background-color: #fff0f0">"')]"</span>;
<span style="font-weight: bold; color: #008800">return</span> root.Database.SelectItems(query);
}
</pre></div>
<p>I run this a couple of times (I’m not exactly in a stable environment to conduct any real scientific test of this – keep this in mind).</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9MZiFeNX2xM_hLK9Ir3ZBSnmuUhJBUpDHT6K_lZCNKPd7WKFclESo4CxoYwpMvNUdEDcdqTbNaZqcboS5lNZURKIBMT94DNAMTBCfGTArXkl4YbNF9r91LEDrWfW4h-6Ppfrg/s1600-h/image%25255B21%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHUPUmxqy-uCI_fY5bwgLcwSgFriGU4peymXnwlsthwUqBX8oTU5y7oRU8DNtY5ZUNOEYjVUGxOM_NfNG8COwtR5vD-HKMAKgXrTyCzjfXTdCmAq5j_kPIfycTfYxBYHcTW3n_/?imgmax=800" width="194" height="244"></a></p>
<p>I then rewrite it for Sitecore Fast Query.</p>
<h4>Sitecore Fast Query</h4>
<p>BE AWARE! Sitecore Fast Query sacrifices functionality to gain performance. Be especially aware of its limitations if your solution is multilingual.</p>
<p>Code now looks like this:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%">Item[] <span style="font-weight: bold; color: #0066bb">GetAuthorReferrers</span>(Item root, ID authorId)
{
<span style="font-weight: bold; color: #333399">string</span> query = <span style="background-color: #fff0f0">"fast:"</span> + root.Paths.FullPath + <span style="background-color: #fff0f0">"//*[@Author = '%"</span> + authorId + <span style="background-color: #fff0f0">"%']"</span>;
<span style="font-weight: bold; color: #008800">return</span> root.Database.SelectItems(query);
}
</pre></div>
<p>And an output that comes out like this. (Believe me, I ran this dozens of times).</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlJ0YkrCrjUZJ0-SdsceYgZhTOcBy-JULmL9ZG5ub1peJsPnQn2lMqJ9lCjUVuoOTIgPZjJh9jo_9gEwkJmQKlMCK1Ul9Y1MgNIkJgH-xPkHSrrrvPnXGLv0ThSKaXpeq1-24I/s1600-h/image%25255B24%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQgajJLT_R527pxe1GhUJQew9f2mP__lILeCv7Lh2QSypZUNU6-J6_zQ-mbZJhzLaJ9R69VBidXY3ej4lAnVrOmIEHJVuPxoCuxpBuCTnFlsOKDsHFilrhck55j0BlQLpxDPZ3/?imgmax=800" width="200" height="244"></a></p>
<p>Are we having fun yet? I can offer up a bit of speculation on what’s going on here, but I’m honestly not completely sure. Mostly because I would never use neither solution to query my data, so my experience is somewhat limited. My best guess would be; to benefit from fast: query, you need more data. I will try this out with a larger dataset a bit further down.</p>
<p>So anyway. Roll on SLDB.</p>
<h4>Sitecore LinkDatabase</h4>
<p>Drawback of this approach is, that it’s a bit more code heavy. Not by much though.</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%">Item[] <span style="font-weight: bold; color: #0066bb">GetAuthorReferrers</span>(Item root, Item author)
{
ID authorFieldId = <span style="font-weight: bold; color: #008800">new</span> ID(<span style="background-color: #fff0f0">"{438E45E5-9F85-4705-976E-FC76E563F5EF}"</span>);
<span style="font-weight: bold; color: #333399">var</span> items = <span style="font-weight: bold; color: #008800">new</span> List<Item>();
ItemLink[] itemLinks = Sitecore.Globals.LinkDatabase.GetItemReferrers(author, <span style="font-weight: bold; color: #008800">false</span>);
<span style="font-weight: bold; color: #008800">foreach</span> (<span style="font-weight: bold; color: #333399">var</span> il <span style="font-weight: bold; color: #008800">in</span> itemLinks)
{
<span style="font-weight: bold; color: #008800">if</span> (il.SourceDatabaseName.Equals(root.Database.Name) && il.SourceFieldID == authorFieldId)
{
items.Add(il.GetSourceItem());
}
}
<span style="font-weight: bold; color: #008800">return</span> items.ToArray();
}
</pre></div>
<p>And here’s how we end up.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4raUTobVnbcwpD23gf3MbxkNaiVd35JUbF5UogNhJ9ZgwvBrCoz9n0tJkYOVWSFg9VIx1VwBrGMFp5fp2og3tdnyAQT_p2g61efegXfsVfXJZ7M9RO1Mq5A-0GWnGVCQKzYgR/s1600-h/image%25255B27%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhg2M5zOAlCINpjQ80DYJGldxmq4Sl-NtHkeOXmMu8pE-0UkHDQoWKCFPJyER7yNwBKcKDWZO09ahzR2f6eCwCh3sNXpnNYA97RLzsLuBNOAw1wMT9M8P0ZF0LPGCaN-g3VVk5F/?imgmax=800" width="183" height="244"></a></p>
<p>Am I managing to convince you yet? so far we’re doing ok. We’re outperforming Sitecore Query by about 10 to 1. And Sitecore Fast Query by 50 to 1. A pretty decent start I’d say.</p>
<h2>The sleight of hand</h2>
<p>So I’m left with two problems. I’ve not brought “Category” into play. And I’ve not got a simple output, that fully compares these 3 side by side. I’ve created a <a href="https://gist.github.com/cassidydotdk/f2cb2afb6dbf83f045188cd252f648bc" target="_blank">Gist with the full source code</a> – it is to long to include here, even by my standards.</p>
<p>Enter, a code rewrite. This time I’m looping through all Categories, then all Authors – finding all News articles that match the Category AND the Author. It completes the setup, and it actually demonstrates one of the slightly more complex situations when using SLDB. Using querying this becomes just another “and” expression.</p>
<p>SLDB code looks like this:</p><!-- HTML generated using hilite.me -->
<div style="overflow: auto; border-top: gray 0.1em solid; border-right: gray 0.1em solid; width: auto; background: #ffffff; border-bottom: gray 0.1em solid; padding-bottom: 0.2em; padding-top: 0.2em; padding-left: 0.6em; border-left: gray 0.8em solid; padding-right: 0.6em"><pre style="margin: 0px; line-height: 125%"><span style="font-weight: bold; color: #008800">private</span> Item[] <span style="font-weight: bold; color: #0066bb">GetNewsArticlesSitecoreLinkDatabase</span>(Item newsRoot, Item authorItem, Item categoryItem)
{
<span style="color: #888888">// Note. SLDB version of this actually needs to make 2 lookups. First to find all news articles referencing our author, then all that reference the category.</span>
<span style="color: #888888">// Note #2: Still, my hand is not shaking ;-)</span>
<span style="font-weight: bold; color: #333399">var</span> authorFieldId = <span style="font-weight: bold; color: #008800">new</span> ID(<span style="background-color: #fff0f0">"{438E45E5-9F85-4705-976E-FC76E563F5EF}"</span>);
<span style="font-weight: bold; color: #333399">var</span> categoriesFieldId = <span style="font-weight: bold; color: #008800">new</span> ID(<span style="background-color: #fff0f0">"{31D2A6CC-6984-4CA0-BB73-9D39C3B8D0AA}"</span>);
<span style="font-weight: bold; color: #333399">var</span> authorLinks = Globals.LinkDatabase.GetItemReferrers(authorItem, <span style="font-weight: bold; color: #008800">false</span>).Where(al => al.SourceFieldID == authorFieldId).ToList();
<span style="font-weight: bold; color: #333399">var</span> categoryLinks = Globals.LinkDatabase.GetItemReferrers(categoryItem, <span style="font-weight: bold; color: #008800">false</span>).Where(cl => cl.SourceFieldID == categoriesFieldId).ToList();
<span style="font-weight: bold; color: #333399">var</span> newsArticles = <span style="font-weight: bold; color: #008800">new</span> List<Item>();
<span style="font-weight: bold; color: #008800">foreach</span> (<span style="font-weight: bold; color: #333399">var</span> authorIl <span style="font-weight: bold; color: #008800">in</span> authorLinks)
{
<span style="font-weight: bold; color: #333399">var</span> categoryIl = categoryLinks.ToList().Find(cl => cl.SourceItemID == authorIl.SourceItemID);
<span style="font-weight: bold; color: #008800">if</span> (categoryIl != <span style="font-weight: bold; color: #008800">null</span> && categoryIl.SourceDatabaseName == newsRoot.Database.Name)
{
newsArticles.Add(categoryIl.GetSourceItem());
}
}
<span style="font-weight: bold; color: #008800">return</span> newsArticles.ToArray();
}
</pre></div>
<p>And what do we get? Well this.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNx4lrtEbbamxY-hgoyknjJDqlLxxATebvMOct5FmkWay-c4llUIdyKyCTiWBNoyuFkpcSyNnfJ64i1IYrTTHbTqr6waJ7NMc3mo2_Ia34LdJqj6hGChmVCickMu279VhQfMfq/s1600-h/image%25255B31%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxmHeX6lrJkNn65YDe8zPL58GqEyqDamw_s7FD3XqWLET5kQLSRFT2oKCEvxAs5lpxIw0kULYI6TMU7dAZqS9GkeB23CCnM3CGnOBL7NIV4X_scX8LJHk7RubKFNAxZ5mrOJ5s/?imgmax=800" width="583" height="187"></a></p>
<p>It matches up pretty well the results already established. Adding the extra query seems to have added a bit to all of the query execution times – this is completely expected. In relative terms however, I think it is still very clear who the winner is.</p>Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com2tag:blogger.com,1999:blog-20883010.post-75299397702539469552016-05-07T11:42:00.000+02:002016-05-07T11:42:39.033+02:00Sitecore Decennial Series #2 - Don't Sitecore Query your content<h2>
Sitecore does not query, how you think it queries</h2>
<div>
As a consultant, I am often brought in to projects and solutions that have - to some extent - gone wrong, or does not quite meet up to expectations. And when it comes to those, absolutely without question, the most common problem I come across, is under-performing solutions.<br />
<br />
And let's just clear something up. While you will find a lot of posts out there discussing the speed and performance of Sitecore (Sitecore 8, in particular); these are all referring to the performance of the Sitecore Experience Editor. The client environment. And while there is rarely this much smoke without a fire somewhere, I will say this:<br />
<br />
The Sitecore <b><i>runtime</i></b> is a very well oiled machine. If it doesn't perform, it means YOU broke it.<br />
<br />
You are welcome to quote me on this.<br />
<br />
Let me exemplify.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOH-fVj0vwS-pTRmw6QnNJqtt19P3Tk3UKuRfk2IkPnF_SIObzoZrZejeBvK_ZGRatQBdo6UYRKza8auPawUS7hVfk9RkTOwsUX26ACRX-iRMtVdXle0sMmwSEzcL9m10_erFb/s1600/07-05-2016+08-12-23.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOH-fVj0vwS-pTRmw6QnNJqtt19P3Tk3UKuRfk2IkPnF_SIObzoZrZejeBvK_ZGRatQBdo6UYRKza8auPawUS7hVfk9RkTOwsUX26ACRX-iRMtVdXle0sMmwSEzcL9m10_erFb/s1600/07-05-2016+08-12-23.jpg" /></a></div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Pretty gruesome, right? You'd not blame this on "poor performance of ASP.NET", if you found this in some web application I assume.<br />
<br />
Now believe me when I tell you; this horrifying example is actually very mild compared to what I often find hidden deep in some of the Sitecore solutions I visit. Usually tucked away in some "*Helper" or "*Utils" (pet peevees of mine) class, used arbitrarily throughout the entire project, never to have been visited by a developer since it was first put in place.<br />
<br />
So anyway. Let's take a look at, how situations like this come to be. I will begin with some fundamentals.<br />
<br />
<h3>
The Sitecore (Sql) Data Store</h3>
</div>
<div>
To try and understand what's going on, we're going to take a bit of a dive into the Sitecore Sql data store. Now before you start; I am very well aware that Sitecore abstracts this layer from view (and rightly so), and under no circumstances should you ever be working on the database layer when doing Sitecore. The structures I am about to show you do change from time to time, and you will end up with a bloody nose if you go down this road.</div>
<div>
<br /></div>
<div>
But we cannot escape the fact, that Sitecore data storage is based on Sql Server (in most cases, actually in every single one I have come across in the past 10 years) - and any data storage mechanism comes with a set of built-in limitations and restrictions. So how Sitecore uses its data storage matters, and it will affect how your solution performs.</div>
<div>
<br /></div>
<h4>
[Items]</h4>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-EXuIoJ1m2-iLN-pvf7aEyXmJf2U2in7ujo9w6s8gk-Mr_wfAgzm63WMexUD72gnG72GiLq5wy6VlO3DlMcp7PjbtEho9395Uj78c62Z85P-jIQTiPHnRRPHC6_kJbs2hasUh/s1600/07-05-2016+08-32-44.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-EXuIoJ1m2-iLN-pvf7aEyXmJf2U2in7ujo9w6s8gk-Mr_wfAgzm63WMexUD72gnG72GiLq5wy6VlO3DlMcp7PjbtEho9395Uj78c62Z85P-jIQTiPHnRRPHC6_kJbs2hasUh/s1600/07-05-2016+08-32-44.jpg" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Without question, the table most central to the Sitecore data store. I imagine the fields are self explanatory. 1 row in this table, for every Item in your Sitecore Database ("master", "web" and so on).</div>
<div>
<br /></div>
<div>
This table holds no other values other than Item.Name. For this, Sitecore looks to 3 other tables. They look almost identical.</div>
<div>
<br /></div>
<h4>
[VersionedFields]</h4>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5jWCTKczHchmQOWFhk2UNUIXjuQq0XFEFoTZgI6hMgJLsDLAcA7rA4IyhIUvd_KIMvPxpxGr4xo6fBKDdJR7LXaDzLUXgafD6onaGCbqO5c0fMHwFXk65C9MYteVpMJ9Sd_Rg/s1600/07-05-2016+08-39-25.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5jWCTKczHchmQOWFhk2UNUIXjuQq0XFEFoTZgI6hMgJLsDLAcA7rA4IyhIUvd_KIMvPxpxGr4xo6fBKDdJR7LXaDzLUXgafD6onaGCbqO5c0fMHwFXk65C9MYteVpMJ9Sd_Rg/s1600/07-05-2016+08-39-25.jpg" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<h4>
[UnversionedFields]</h4>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3XVMj1fgiEWbGecGAOjPmLS3YFyiEu0YaenFydrjgDErb4y6RnLsD6vNemnre8YiiUlxtJW7j3gQyUZfw_7AFQg_-l_clqsHOTzLenhFj3w7Bzmw2QmW36KQGEt00KaloUsm4/s1600/07-05-2016+08-40-59.jpg" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3XVMj1fgiEWbGecGAOjPmLS3YFyiEu0YaenFydrjgDErb4y6RnLsD6vNemnre8YiiUlxtJW7j3gQyUZfw_7AFQg_-l_clqsHOTzLenhFj3w7Bzmw2QmW36KQGEt00KaloUsm4/s1600/07-05-2016+08-40-59.jpg" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<h4>
[SharedFields]</h4>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhvObnN7-qKLAcr326agwxKgCNGpapRJkHsiUq16cXnjyvPVCOn42xk7DJQ_yYbuHWfBH8vypB23caD1vSiM7D2qmihNJXeoIbwj6NDEo3OMsr6aTdQwo4ebTSh7HdnbrApDyG/s1600/07-05-2016+08-44-03.jpg" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhvObnN7-qKLAcr326agwxKgCNGpapRJkHsiUq16cXnjyvPVCOn42xk7DJQ_yYbuHWfBH8vypB23caD1vSiM7D2qmihNJXeoIbwj6NDEo3OMsr6aTdQwo4ebTSh7HdnbrApDyG/s1600/07-05-2016+08-44-03.jpg" /></a></div>
<div>
<br /></div>
<div>
If you haven't guessed already; Sitecore decides where to place field values depending on how you check your "Shared" and "Unversioned" checkboxes when defining your Sitecore Templates. I will not go further into this, in this post.</div>
<div>
<br /></div>
<div>
But what does this mean? Well there's a couple of things to pay close attention to here. The first one being; Sitecore sees its data store as a Tree Structure (d'uh I hear you say, you probably already knew that). But this is important. Notice how [Items] stores Id and ParentId (and a few other Ids we don't care about right now). To go anywhere in the "Tree", Sitecore must therefore traverse up and down this chained list of Ids - starting from the top.</div>
<div>
<br /></div>
<div>
So if I were to do something simple as say; Sitecore.Context.Database.GetItem("/sitecore/content") - what this ends up as, is something like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><b>exec sp_executesql N' SELECT [ID] FROM [Items] WHERE [ParentID] = @parentId AND [Name] = @childName',N'@parentId uniqueidentifier,@childName nvarchar(7)',@parentId='11111111-1111-1111-1111-111111111111',@childName=N'content'</b></span></div>
<div>
<br /></div>
<div>
(Sitecore caching is in effect, so it was already aware of the parentId)</div>
<div>
<br /></div>
<div>
Which is not so bad; but do remember. Had I queried "/sitecore/content/home/data/articles/2015/february" - this would (uncached) have resulted in 6 queries like the one above, to Sql Server. Yep. Again; this is without considering caching, obviously, or we'd all be in big trouble.</div>
<div>
<br /></div>
<div>
And this is just getting an Item. As we saw above, the field values sit in separate tables. Once you start accessing actual content on the Item, this is what it looks like behind the scenes.</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">exec sp_executesql N'SELECT [ItemId], [Order], [Version], [Language], [Name], [Value], [FieldId], [MasterID], [ParentID]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> FROM (</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> SELECT [Id] as [ItemId], 0 as [Order], 0 as [Version], '''' as [Language], [Name], '''' as [Value], [TemplateID] as [FieldId], [MasterID], [ParentID]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> FROM [Items]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> UNION ALL </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> SELECT [ParentId] as [ItemId], 1 as [Order], 0 as [Version], '''' as [Language], NULL as [Name], '''', NULL, NULL, [Id]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> FROM [Items] </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> UNION ALL </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> SELECT [ItemId], 2 as [Order], 0 AS [Version], '''' as [Language], NULL as [Name], [Value], [FieldId], NULL, NULL</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> FROM [SharedFields] </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> UNION ALL </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> SELECT [ItemId], 2 as [Order], 0 AS [Version], [Language], NULL as [Name], [Value], [FieldId], NULL, NULL</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> FROM [UnversionedFields] </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> UNION ALL </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> SELECT [ItemId], 2 as [Order], [Version], [Language], NULL as [Name], [Value], [FieldId], NULL, NULL </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> FROM [VersionedFields]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> ) as temp WHERE [ItemId] IN (SELECT [ID] FROM [Items] WITH (nolock) WHERE [ID] = @itemId) </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> ORDER BY [ItemId], [Order] ASC, [Language] DESC, [Version] DESC',N'@itemId uniqueidentifier',@itemId='0DE95AE4-41AB-4D01-9EB0-67441B7C2450'</span></div>
</div>
<div>
<br /></div>
<div>
Incidentally; ever had a [Shared Field] that you switched to [Unshared], and now you're not getting the field values you expect? this query is the reason. When you toggle that checkbox, Sitecore starts a background task that will migrate the field values from [SharedFields] to [UnversionedFields]. It has to, as the above query will otherwise return the first field value it finds in the UNION - which will be the one near the top; [SharedFields]. If this task somehow doesn't complete - your data storage gets left in a bit of a pickle. </div>
<div>
<br /></div>
<div>
So yea. Sitecore essentially goes and grabs all your field values once you start accessing content on an item. And yes, this IS the efficient approach - a roundtrip to Sql for every field value you access hereafter would be absolutely crippling.</div>
<div>
<br /></div>
<div>
But keeping this in mind; picture this:</div>
<div>
<br /></div>
<div>
<i>Sitecore.Context.Database.SelectItems("/sitecore/content//*[@PageTitle='news']")</i></div>
<div>
<br /></div>
<div>
Caches aside; you've just instructed Sitecore to recursively go find anything that has /sitecore/content as an ancestor (1 SQL statement for each item, to find them all). And then your request for the PageTitle field will then, for each of the found items, execute the big UNION statement from above so that it can determine the field value for PageTitle.</div>
<div>
<br /></div>
<div>
Yep, you read that right. You may be imagining things like; "but I can construct a SQL statement that finds this more effectively". And you're right, you can. But you'd also be ignoring Sitecore through and through - you might as well not bother using a CMS at all. What you're overlooking (to name a few) are things like Versions (which of the 7 versions of /Home is the active one?), Language, Security, Workflow Status. </div>
<div>
<br /></div>
<div>
I'll say this as plainly and calmly as I possibly can. Do NOT use Sitecore Query for any code that executes at runtime on your solution. "If it doesn't perform, it means YOU broke it."</div>
<div>
<br /></div>
<div>
When it comes to constructing a more efficient query, you're not the first one to have this thought though; enter Sitecore Fast Query.</div>
<div>
<br /></div>
<h4>
Sitecore Fast Query</h4>
<div>
SFQ is a query mechanism that aims to address some of the limitations of regular Sitecore Query. It does so by compromising exactly some of the things I mention above. So sacrifice a little functionality to gain a little speed. I'm almost tempted to paraphrase Frankling and claim "you shall have neither".</div>
<div>
<br /></div>
<div>
But let's look at it. From the "Using Sitecore Fast Query Cookbook" (how I miss those)</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXQ0LPVRzCqqHdpu9JKB-gj10-YClHZUxdn-OqJwijgFq0wrhHKTgE7zvVz7_a0n7kRPT8D21IWFcfuvJlrIiTfvFQvHoLMjl79hiWzeCegoPT4xJqdbT0VyN6HVyos-PItmfI/s1600/07-05-2016+10-23-40.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXQ0LPVRzCqqHdpu9JKB-gj10-YClHZUxdn-OqJwijgFq0wrhHKTgE7zvVz7_a0n7kRPT8D21IWFcfuvJlrIiTfvFQvHoLMjl79hiWzeCegoPT4xJqdbT0VyN6HVyos-PItmfI/s1600/07-05-2016+10-23-40.jpg" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Now; given everything that I have just shown you - this makes perfect sense. The only supported special attributes - all of those are what you find on the [Items] table. Standard Values are not supported (as these sit on a separate item). I'll leave it to you to speculate why field values need to be surrounded by % signs ;-)</div>
<div>
<br /></div>
<div>
Ok, so let's take a look then. I cook up something new:</div>
<div>
<br /></div>
<div>
Sitecore.Context.Database.SelectItems("<b>fast:</b>/sitecore/content//*[@#Page Title#=\"%article%\"]");</div>
<div>
<br /></div>
<div>
And the resulting Sql looks like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">exec sp_executesql N'SELECT DISTINCT [i].[ID] [ID], [i].[ParentID] [ParentID] FROM [Items] [i] WITH (NOLOCK) LEFT OUTER JOIN (SELECT [Fields].* from [Fields] INNER JOIN [Items] ON [Fields].[FieldID] = [Items].[ID] AND lower([Items].[Name]) = ''page title'') [Fields1] ON [i].[ID] = [Fields1].[ItemId] INNER JOIN [Descendants] ON [i].[ID] = [Descendants].[Descendant] INNER JOIN (SELECT DISTINCT [i].[ID] [ID], [i].[ParentID] [ParentID] FROM [Items] [i] WITH (NOLOCK) INNER JOIN (SELECT DISTINCT [i].[ID] [ID], [i].[ParentID] [ParentID] FROM [Items] [i] WITH (NOLOCK) WHERE LOWER([i].[Name]) = ''sitecore'' AND [i].[ParentID] = @value1) [a] ON [i].[ParentID] = [a].[ID] WHERE LOWER([i].[Name]) = ''content'') [a] ON [Descendants].[Ancestor] = [a].[ID] WHERE (coalesce([Fields1].[Value], '''') LIKE @value2)',N'@value1 uniqueidentifier,@value2 nvarchar(9)',@value1='00000000-0000-0000-0000-000000000000',@value2=N'%article%'</span></div>
<div>
<br /></div>
<div>
And a footprint like this.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0j6PZ2497ZN8ZLVbl3qGgnDaJFsWyFVNcBGpRXaTb-1vQcw2S4Fkq2NFuwjWqYfX2qUgV-vc096WH2lEtZGxh1xCcXEsbWABEpjgiH_yY8kFh9nGHI6FvnsGdh-21hYHMqU9y/s1600/07-05-2016+10-38-48.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="137" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0j6PZ2497ZN8ZLVbl3qGgnDaJFsWyFVNcBGpRXaTb-1vQcw2S4Fkq2NFuwjWqYfX2qUgV-vc096WH2lEtZGxh1xCcXEsbWABEpjgiH_yY8kFh9nGHI6FvnsGdh-21hYHMqU9y/s640/07-05-2016+10-38-48.jpg" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Ok. So there's no doubt, this is BETTER than our previous situation. But only by a measure, it's not the end-all to our performance woes. I have about 20 (!) items in the vanilla solution I am using to write this blog post by the way. Twenty.</div>
<div>
<br /></div>
<div>
Interesting (unofficial) factoid: Do you know why FAST: query was introduced to begin with? To solve performance problems in Sitecore Content Editor. Yes. Think about it; opening up any item in CE presents you with a series of fields. Each of these fields may be Treelists and whatnot, and loading up all that related meta-data and presets, populating all the dropdown boxes and so on - well it pretty much killed performance in early Sitecore (5 and 6) releases. And for Content Editor, the limitations of Fast Queries are fine. No doubt they're (often) better than the regular Sitecore Query equivalent. But that still doesn't make them good.</div>
<div>
<br /></div>
<div>
Don't look to caching to save you either. It helps, surely. But even cached results need to be generated at least once; and now you find yourself in a whole heap of other problems instead. When to clear cache? on publish? you sure? So with editors publishing new content every 5 minutes (to test it out - trust me, they do), how much "cache debt" does your solution need to deal with?</div>
<div>
<br /></div>
<div>
Look; we're in my <a href="http://intothecore.cassidy.dk/search/label/Decennial">Decennial Series</a>. I'm giving you my hard earned advice. </div>
<div>
<br /></div>
<div>
Just stay away from any form of querying. There are other and better ways. I'll give you a few pointers; most of what I'm highlighting here is already well covered by blog posts everywhere (far from all of them, mine). Seek them out, please. Please. Next time you feel, you need to query for your content.</div>
<div>
<br /></div>
<h3>
What to do instead</h3>
<div>
<br /></div>
<h4>
Information Architecture</h4>
<div>
The first and most important thing to keep in mind, is how you organise your content. Try and keep related items close together and organise them in a meaningful deep tree hierarchy. So for, as an example, News Articles, do this:</div>
<div>
<br /></div>
<div>
/sitecore/content/news/2016/04/[your articles here]</div>
<div>
<br /></div>
<div>
Which gives you the option to do:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">var root = Sitecore.Context.Database.GetItem("/sitecore/content/news/2016/04");</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">var newsArticles = root.GetChildren();</span></div>
<div>
<br /></div>
<div>
And unless you have an extraordinarily "news'y" site, I assure you this will be better than the Query equivalent which might look something like:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">var newsArticles = Sitecore.Context.Database.SelectItems("/sitecore/content/news/*[@newsDate > '2016-04-01' & @newsDate < '2016-05-01'])</span></div>
<div>
<br /></div>
<div>
Don't be overly worried about reading in 10-20 items in a .GetChildren() call; be worried about reading in 2000 items and discarding 1980 of them.</div>
<div>
<br /></div>
<h4>
Code Smart(er)</h4>
<div>
There are often multiple ways of achieving something. This certainly holds true for Sitecore as well. One of the little known gems is the Sitecore LinkDatabase. Ever tried deleting an item, and have Sitecore pop up and tell you this:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgckIv1iQRPuLHAjgSAOGWetSnRZqYIezJFFOmbXVUsMB103g4Mu6cy4KVxXU_vEecHkCi7IVLipsik_D771AdPm4pih1Bc03ZW4b5HX2hM1-JUj6iCRSjw3Ovre3s_RVRUzQXm/s1600/07-05-2016+11-12-28.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgckIv1iQRPuLHAjgSAOGWetSnRZqYIezJFFOmbXVUsMB103g4Mu6cy4KVxXU_vEecHkCi7IVLipsik_D771AdPm4pih1Bc03ZW4b5HX2hM1-JUj6iCRSjw3Ovre3s_RVRUzQXm/s1600/07-05-2016+11-12-28.jpg" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
How does Sitecore know this? And you probably guessed it already. The LinkDatabase. The SLDB is easily worthy of a post of its own, but in essence it's this:</div>
<div>
<br /></div>
<div>
A table maintained by Sitecore, where all reference field references (!) are stored. This allows Sitecore to keep track of inter-Item relations so it can pop this warning to you.</div>
<div>
<br /></div>
<div>
Since we're doing the low-level thing today, here's what it looks like:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2vBLz_HU_iH4LZz5qDXPfD2Yc3RNpIHOygIc7XmaIUBAuId5lxMK8tQZ9OJB6yyLFvZl249VubA0mucFPimLKanwOusJ-KGqXiO327U49-Y1EKuqo8umBAkhO4U5CjhVKcfUA/s1600/07-05-2016+11-18-30.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2vBLz_HU_iH4LZz5qDXPfD2Yc3RNpIHOygIc7XmaIUBAuId5lxMK8tQZ9OJB6yyLFvZl249VubA0mucFPimLKanwOusJ-KGqXiO327U49-Y1EKuqo8umBAkhO4U5CjhVKcfUA/s1600/07-05-2016+11-18-30.jpg" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Turns out, this is an excellent tool in many other situations as well. I think my <a href="http://intothecore.cassidy.dk/2009/05/listing-related-articles-with-sitecore.html">Listing "Related Articles" using LinkDatabase</a> was the first time I blogged about it; and I regularly dust off SLDB and pick it up from my tool shelf even to this day.</div>
<div>
<br /></div>
<div>
A common scenario that often comes up, is "I need to find all News articles, but they're spread out all over my content tree. Silly editors". Well SLDB has a fix for that; although here it is used indirectly.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl-yiXZ3g-mEjP-Uk4yz9JHMuqt0ynIC1yZsLWT3TaV3qKafEOyi0y0rEGtdRTfomHg1YnXXzY8MWz1KkiTT30WpQvCUCAACy2c0_eJg_-MvDNtGnpPIgLVXAy3eayrojNuZRq/s1600/07-05-2016+11-21-29.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl-yiXZ3g-mEjP-Uk4yz9JHMuqt0ynIC1yZsLWT3TaV3qKafEOyi0y0rEGtdRTfomHg1YnXXzY8MWz1KkiTT30WpQvCUCAACy2c0_eJg_-MvDNtGnpPIgLVXAy3eayrojNuZRq/s1600/07-05-2016+11-21-29.jpg" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
In case you're wondering; .GetUsageIDs() uses SLDB internally to achieve it's result.</div>
<div>
<br /></div>
<div>
I highly recommend you read up on the Sitecore LinkDatabase. It can really make your life easier in a lot of situations, where you would otherwise feel tempted to start Sitecore Querying.</div>
<div>
<br /></div>
<h4>
Index your content</h4>
<div>
And finally, but also most importantly, use an index for every single Sitecore solution you ever do. I mean it. Seriously.</div>
<div>
<br /></div>
<div>
Want to find all News Articles in a specified date range? use an index.</div>
<div>
Want to find all News Articles in the "Business News" category, sorted by Author? use an index.</div>
<div>
<br /></div>
<div>
Even with all the tricks I have show you here, sooner or later you will come to a situation where the content cannot be organised any differently, where LinkDatabase cannot help you as we're dealing with content and not references - or flat out, we're dealing with so much content that we just have to narrow down the dataset to be able to work with it.</div>
<div>
<br /></div>
<div>
Fortunately, I've already given the answer to these problems. Use an index.</div>
<div>
<br /></div>
<div>
There are quite literally dozens and dozens of blog posts out there, on how to set up and use Sitecore ContentSearch (which is where I recommend you start out). It takes so little effort to get going, it's active out of the box (using Lucene as it's provider), there quite simply is not a single valid excuse not to use it.</div>
<div>
<br /></div>
<div>
I know it's "another thing to learn". This one is not optional however; you really cannot go around it. You may feel you're doing just fine and your Sitecore Queries run fine. Great. Give yourself a nice "It works on my machine" sticker, and then get those indexes going ;-)</div>
<div>
<br /></div>
<div>
Seriously.</div>
<div>
<br /></div>
<div>
Until next time :-)</div>
<div>
<br /></div>
<div>
<br /></div>
Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com0tag:blogger.com,1999:blog-20883010.post-56279777046329433312016-02-28T15:14:00.000+01:002016-02-28T15:14:13.142+01:00Sitecore Decennial Series #1 - Know your item, remember your context<h2>
Understand the basics</h2>
The most common problems I find when working with (other people's) Sitecore solutions, has a root cause in either a lack of understanding of the basic concepts of Sitecore and/or a misunderstanding of same. I don't actually know if this is surprising or to be expected - it is what it is.<br />
<div>
<br /></div>
<div>
I'll try and isolate some of the most common basic mistakes, in no significant order. This is also to say, I consider them ALL equally significant ;-)</div>
<div>
<br /></div>
<h3>
1. Understand your Item</h3>
<div>
Yes I'm talking about you, <span style="font-family: "courier new" , "courier" , monospace;">Sitecore.Data.Item</span>.</div>
<div>
<br /></div>
<div>
Chances are, <span style="font-family: "courier new" , "courier" , monospace;">Item</span> is one of the very first concepts you come across when you start developing your first solution. It might look something like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> Item home = Sitecore.Context.Database.GetItem("/sitecore/content/home");</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> string headline = home["Headline"];</span></div>
<div>
<br /></div>
<div>
Simple, yea? Not so. Reading the syntax like this has a high risk of tricking your mind into thinking a lot of things, all of which are false and will lead you astray sooner or later.</div>
<div>
<br /></div>
<h4>
1a. A context is implied</h4>
<div>
As with the very large majority of all interaction with the Sitecore API, a context is required for any interaction. I will dig into this in detail a bit further on.</div>
<div>
<br /></div>
<div>
That method call, is just a method overload for a call that looks like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> Item home = Sitecore.Context.Database.GetItem("/sitecore/content/home", </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> Language.Current, </span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> Version.Latest);</span></div>
<div>
<br /></div>
<div>
Why is this significant? It's significant because here, as in most API calls, Sitecore breaks out and pulls additional information required to execute the call; in this case it needs to determine what <i>Language Version</i> of the Item to get. And once it knows the language, it needs to know which <i>Version</i> of the <i>Language Version</i> to get.</div>
<div>
<br /></div>
<div>
And while this may not seem obvious to you while still learning the robes of Sitecore development (it didn't, to me), this is actually very significant.</div>
<div>
<br /></div>
<div>
Very many things in the Sitecore API requires some sort of context. And knowing them will be important to you, when you advance into more advanced development.</div>
<div>
<br /></div>
<h4>
1b. People say "Item" when they really mean "Item Version".</h4>
There really is no such thing as an "Item". Item is a construct in Sitecore that holds a lot of information related to the content of the Item Versions - but really holds none of the actual content. (For the advanced readers; I realise we could argue the merits of this - but as a general principle, this holds true).<br />
<div>
<br /></div>
<div>
So what is on <span style="font-family: "courier new" , "courier" , monospace;">Item</span>? Important things. Like:</div>
<div>
<ul>
<li>Name (by default, defines the URL string for the item)</li>
<li>Security</li>
<li>Template (could also be called the "Schema" for the Item Versions)</li>
<li>Statistics (Last Updated, Updated By, etc.)</li>
<li>Publishing Information</li>
<li>Workflow Information</li>
<li>Validation Rules</li>
</ul>
<div>
But for most intents and purposes, not things you need in your day to day life of creating Accordion components or whatever your task.</div>
</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;">Sitecore.Data.ID</span> uniquely identifies any Item in a Sitecore solution. And from this, we now also see, that <span style="font-family: "courier new" , "courier" , monospace;">Sitecore.Data.ID</span> does not adequately represent an Item Version.</div>
<div>
<br /></div>
<h4>
1c. Understand the different identifiers. ID is not always what you need.</h4>
<div>
Unbeknownst to many, judging from rarely I find these in use in Sitecore solutions I look at, Sitecore actually has many better options than <span style="font-family: "courier new" , "courier" , monospace;">Sitecore.Data.ID</span> available. All in the <span style="font-family: "courier new" , "courier" , monospace;">Sitecore.Data</span> namespace.</div>
<div>
<br /></div>
<div>
Given this piece of hackety webforms code (had to destroy the BR tags to keep Blogger happy):</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> var home = Sitecore.Context.Database.GetItem("/sitecore/content/home");</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> litOutput.Text += $"ID (home.ID): {home.ID}$br />";</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> litOutput.Text += $"Uri (home.Uri): {home.Uri}$br />";</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> litOutput.Text += $"DataUri: {new DataUri(home.ID, home.Language, home.Version)}$br />";</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> litOutput.Text += $"ItemUri: {new ItemUri(home.ID, home.Language, home.Version, home.Database)}$br />";</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> litOutput.Text += $"VersionUri: {new VersionUri(home.Language, home.Version)}$br />";</span></div>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div>
The output is:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> ID (home.ID): {DAC24EDD-44FB-42EF-9ECD-1E8DAF706386}<br /> Uri (home.Uri): sitecore://master/{DAC24EDD-44FB-42EF-9ECD-1E8DAF706386}?lang=en&ver=1<br /> DataUri: sitecore://{DAC24EDD-44FB-42EF-9ECD-1E8DAF706386}?lang=en&ver=1<br /> ItemUri: sitecore://master/{DAC24EDD-44FB-42EF-9ECD-1E8DAF706386}?lang=en&ver=1<br /> VersionUri: en, 1</span></div>
<div>
<br /></div>
<div>
Any <span style="font-family: "courier new" , "courier" , monospace;">Item</span> you have instantiated (like from a <span style="font-family: "courier new" , "courier" , monospace;">GetItem()</span> API call) will be uniquely identified by an <span style="font-family: "courier new" , "courier" , monospace;">ItemUri</span>, as found on the <span style="font-family: "courier new" , "courier" , monospace;">.Uri</span> property. <span style="font-family: "courier new" , "courier" , monospace;">.ID</span> tells you only the ID of the underlying <span style="font-family: "courier new" , "courier" , monospace;">Item</span>.</div>
<div>
<br /></div>
<div>
From this we also learn, that the <span style="font-family: "courier new" , "courier" , monospace;">Item</span> we get from the API does not exist outside of a Sitecore Context. With both Database, Language and Version information. This is undoubtedly become a pet peevee for you at one point or another, if you start looking to do Unit Testing or any kind of abstractions to the Sitecore API. My honest advice; leave this be for now. For at least a couple of years into your Sitecore learning curve.</div>
<div>
<br /></div>
<div>
Keep your <span style="font-family: "courier new" , "courier" , monospace;">Item</span> identifiers in mind. Don't use <span style="font-family: "courier new" , "courier" , monospace;">.ID</span> as a cache key when publishing Item Versions. Do use <span style="font-family: "courier new" , "courier" , monospace;">DataUri</span>, <span style="font-family: "courier new" , "courier" , monospace;">ItemUri</span> and <span style="font-family: "courier new" , "courier" , monospace;">VersionUri</span> as appropriate, don't re-invent the wheel with your own bespoke implementations or - worse - just ignore the fact that ID does not tell you all you need to know.</div>
<div>
<br /></div>
<div>
Speaking of pet peevees, here's one of mine.</div>
<div>
<br /></div>
<h3>
2. Understand your context</h3>
<h4>
2a. Don't break context or get a context you don't require</h4>
<div>
Given the following code:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> public Item[] GetNewsInCategory(Item categoryItem)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> List<item> articles = new List<item>();</item></item></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> if (!string.IsNullOrEmpty(categoryItem["Articles"]))</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> foreach (var articleId in categoryItem["Articles"].Split("|".ToCharArray()))</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> articles.Add(Sitecore.Context.Database.GetItem(articleId));</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> }</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> }</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> return articles.ToArray();</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> }</span></div>
</div>
<div>
<br /></div>
<div>
Actually there are 2 pet peevees of mine in here. One is not using the Sitecore API to properly deal with the MultilistField. The other is the breakout to Sitecore.Context.Database to get the items defined in the field. Why? We already take an Item as a parameter, so we already HAVE a Language and a Database context. At the very LEAST, do this:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> articles.Add(categoryItem.Database.GetItem(articleId));</span></div>
<div>
<br /></div>
<div>
In the inner loop. If you make methods and these happen to take an Item as an argument, by all means USE the context of that argument to carry on. You'll be happy you did, as you will one day find yourself wanting to call your code from say an Index Handler, an Item Saving event or whatnot - and you <b><i>cannot assume you have your normal page context available in these cases</i></b>. I've seen what happens on this particular road to hell, and it usually ends up with a line of code like this getting injected.</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> database myDb = Factory.GetDatabase("master");</span></div>
<div>
<br /></div>
<div>
To try and solve the problem, with Sitecore.Context.Database being NULL in some cases. No, no, no, nope, please, just don't. Use the <span style="font-family: "courier new" , "courier" , monospace;">.Database</span> property of the <span style="font-family: "courier new" , "courier" , monospace;">Item</span> you're dealing with. Whoever instantiated it, already made a context for it (see above; no <span style="font-family: "courier new" , "courier" , monospace;">Item</span> with no context).</div>
<div>
<br /></div>
<div>
That said; what the above code SHOULD look like this this. (Leaving out the argument about argument assertion for now).</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> public Item[] GetNewsInCategory(Item categoryItem)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> MultilistField articlesField = categoryItem.Fields["Articles"];</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> if (articlesField != null)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> return articlesField.GetItems();</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> return new Item[] {};</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> }</span></div>
</div>
<div>
<br /></div>
<h4>
2b. Context, context, context everywhere</h4>
<div>
I think you realise by now, I find the subject of Context in Sitecore very important ;-)</div>
<div>
<br /></div>
<div>
Here's the thing. Try and avoid using it. While it is indeed very convenient to just jump out and grab a <span style="font-family: "courier new" , "courier" , monospace;">Sitecore.Context.Site</span> whenever you need it, or <span style="font-family: "courier new" , "courier" , monospace;">Sitecore.Context.Language</span> or whatever it may be. But it is also very bad form for your code. It is in fact an anti-pattern.</div>
<div>
<br /></div>
<div>
"But all of Sitecore is written like this?"</div>
<div>
<br /></div>
<div>
CAREFUL! PERSONAL OPINION WITH SOME SPECULATION FOLLOWS!</div>
<div>
<br /></div>
<div>
<span style="background-color: #f3f3f3;">Yes. I don't know what to tell you. I'm pretty sure if the original development team was to start today, much of this codebase would have been done following a different mindset. But most of the codebase you're looking at is between 10 and 15 years old, and Sitecore has always been adamant that backwards compatibility be preserved unless there were very good reasons to break it. They follow principles followed by Microsoft very closely when it comes to this.</span></div>
<div>
<span style="background-color: #f3f3f3;"><br /></span></div>
<div>
<span style="background-color: #f3f3f3;">A good example of this is my own CorePoint.DomainObjects, one of the very first public ORM mappers for Sitecore. Written almost 8 years ago, and it can still be built on recent Sitecore versions without too much headache.</span></div>
<div>
<span style="background-color: #f3f3f3;"><br /></span></div>
<div>
<span style="background-color: #f3f3f3;">Sitecore uses static constructs all over the place. It will drive you nuts if you try and code to modern standards, e.g. using Dependency Injection (you should), but that's just how it is. I tell you another thing though; calling through a layered API of static methods and classes is faster than dynamically resolving types at runtime. While we accept this cost today, performance was a much different beast 10 to 15 years ago.</span></div>
<div>
<br /></div>
<div>
So anyway. Back to my original point. Forgive me for pointing out the obvious here. YOU'RE NOT WRITING A CMS SYSTEM. What you're doing, is writing a codebase that will eventually turn out to be an <a href="http://intothecore.cassidy.dk/2013/07/how-do-i-create-good-sitecore-solution.html">excellent Sitecore solution</a>, running the website of your (or your client's/employer's) dreams. Nowhere is it stated, your code standards need to follow those of Sitecore. Yes, you need to adhere to Sitecore Best Practices in your interactions with the Sitecore API and all that, obviously, but nothing else in Sitecore dictates how you should organise your project and solution.</div>
<div>
<br /></div>
<div>
And yes, this is a two edged sword, and why initiatives like the <a href="https://github.com/Sitecore/Habitat">Habitat</a> solution surfaces. Complete freedom, unfortunately, also means you have complete freedom to mess up things. Badly.</div>
<div>
<br /></div>
<div>
To bring some concrete suggestions out; if you need a Database in your method or class, ask for it in the constructor or as a parameter. If you need a SiteContext, ask for it. Don't - please don't - try and configure a full Dependency Injection setup and abstract all of Sitecore into interfaces if this is your first Sitecore solution. It will cost you a LOT of time, much much more than you realise, and chances are no one will ever make back that investment of time in your first solutions lifetime. </div>
<div>
<br /></div>
<div>
Yes, I really did just write that ;-) Take my word for it. </div>
<div>
<br /></div>
<h4>
2c. Are you sure you need that event handler? And if you do, are you sure you're hooked into the right one?</h4>
<div>
Look, I'm pretty sure you don't need that <span style="font-family: "courier new" , "courier" , monospace;">item:Saved</span> handler. Why? Because the real need for them is so very rare. I can probably count on one hand, how many times I've needed to implement one over the course of 10 years of Sitecore development.</div>
<div>
<br /></div>
<div>
Chances are, you're trying to make Sitecore do something it shouldn't really do. This is actually a reference I wrote a blog post about years ago; <a href="http://intothecore.cassidy.dk/2009/06/just-because-you-can-doesnt-mean-you.html">Just because you can, doesn't mean you should</a>. I'm going to rewrite this as part of this Decennial series, but for now the original post will have to do.</div>
<div>
<br /></div>
<div>
Take a step back; consider if what you're doing is really trying to solve a user training problem with a programming solution. Still need that handler? Ok then.</div>
<div>
<br /></div>
<div>
Consider the context of your handler then. I often see examples, like an item:Saved handler that manipulates other items or possibly creates and re-creates parts of the Sitecore content tree, all based on a particular field value or something similar. Are you aware that item:Saved is fired as part of the PublishItem process? (like when the published item is Saved to the "web" Database). </div>
<div>
<br /></div>
<div>
Check your context, filter your context.</div>
<div>
<br /></div>
<div>
If you do implement handlers and processors, at least make sure they only execute when you expect them to. Check if item.Database.Name really matches the ContentDatabase, abort if it doesn't. Check that your bespoke ItemResolver code is currently serving content for the website you expect. "publishing", "shell", "scheduler" etc. are all websites on your solution, are you aware of that?</div>
<div>
<br /></div>
<div>
Consider these things, whenever you hook into anything. Be it item events, request processors, link managers or otherwise. </div>
<div>
<br /></div>
<div>
As an example, look at the item:Saved handler that deal with keeping your LinkDatabase updated. (a hugely underestimated resource when it comes to Sitecore development, but this will be a subject for one of my next posts in this series).</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> protected void OnItemSaved(object sender, EventArgs args)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> {</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> if (args == null || LinkDisabler.IsActive || !Settings.LinkDatabase.UpdateDuringPublish && PublishHelper.IsPublishing())</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> return;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> Item obj = Event.ExtractParameter(args, 0) as Item;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> Assert.IsNotNull((object) obj, "No item in parameters");</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> LinkDatabase linkDatabase = ItemEventHandler.LinkDatabase;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> if (linkDatabase == null)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> return;</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> linkDatabase.UpdateItemVersionReferences(obj);</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> }</span></div>
</div>
<div>
<br /></div>
<div>
Notice how, the first statements in the event handler actually deals with asserting, if it should run at all. Sitecore makes no such determination for you, it is YOUR responsibility to ensure this.</div>
<div>
<br /></div>
<div>
This also goes for your PageMode.</div>
<div>
<br /></div>
<div>
<h4>
2d. What is your current PageMode. Is it relevant?</h4>
</div>
<div>
Considering the current PageMode becomes important, when you're making run-time decisions that could affect the user experience.<br />
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfNYrNexDiW3qQMU_rAxJBe2iFpczyWoIv-AGeIpIcs7qKMuHlL5ROHwneMuC_ozLWfLY06X2VhrlqjORsYTZ5IhLWSeYHWJc43jVjc9R3nez8QDsu10o_4-DauMO0rAm4Z1Lu/s1600/28-02-2016+14-57-25.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="112" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfNYrNexDiW3qQMU_rAxJBe2iFpczyWoIv-AGeIpIcs7qKMuHlL5ROHwneMuC_ozLWfLY06X2VhrlqjORsYTZ5IhLWSeYHWJc43jVjc9R3nez8QDsu10o_4-DauMO0rAm4Z1Lu/s320/28-02-2016+14-57-25.jpg" width="320" /></a></div>
<span id="goog_142596988"></span><span id="goog_142596989"></span><br /></div>
<div>
<br /></div>
<div>
Let's say you're putting in some code, to prevent your component from failing if it has been configured with a faulty Datasource. Or alternatively if you want to explicitly throw an Exception in that case, to help your fellow developers track down a bug.</div>
<div>
<br /></div>
<div>
Be careful. At site run-time (when PageMode.IsNormal) it could indeed be considered an error condition, if a component is configured with a Datasource that does not exist. This is quite likely NOT true for many of the other PageModes. Consider this:</div>
<div>
<br /></div>
<div>
An Editor is Page Editing (or Experience Editing, the new bling expression) inserts your component onto a page. What happens (simplified) is, that Sitecore adds your component to a placeholder key and renders it. You may or may not have a Datasource defined at this stage. Don't blow up. Don't YSOD. Your component is in a staging state, and your code needs to consider this. This is what PageMode is for. </div>
<div>
<br /></div>
<div>
In general, I find it really bad form to YSOD on these specific conditions; like a missing Datasource or a reference field pointing to items that do not exist. Why? Because it's very likely just User Error. An Editor forgetting to publish a related item (something that is VERY easy to do in Sitecore, even if the current publishing tools make this slightly easier). You really DON'T want to teach your users, if they make a mistake you're going to YSOD their site. You really don't.</div>
<div>
<br /></div>
<div>
Alternatively, if you can, discuss with your users what should happen. Either the component outputs some harmless content to alert them of this condition, or perhaps it hides itself completely. Again, only do this if PageMode.IsNormal or PageMode.IsPreview. Or maybe PageMode.IsDebugging, if you're using the Sitecore Debugger (you should). But consider it, don't just ignore it.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
So I think that's it. For this post, anyway. Until next time :-)</div>
<div>
<br /></div>
<div>
<br /></div>
Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com3tag:blogger.com,1999:blog-20883010.post-22607066844315543712016-01-12T08:00:00.000+01:002016-01-12T08:00:18.417+01:0010 years of Sitecore blogging – whereto now?<h2>
10 years of this blog and me (but probably mostly me)</h2>
So it all started in early 2006 with a <a href="http://intothecore.cassidy.dk/2006/01/idtable-breaking-changes-in-5118.html" title="IDTable (breaking) changes in 5.1.1.8">post
about IDTables</a> (of all things…). I was a newly coined Sitecore Certified
Developer, having recently left my “vanilla” .NET development consultant job to
go work for one of the premiere Sitecore Partners and was now doing Sitecore
full time.<br />
<br />
As the blog posts of that time will testament, I was very much learning at
the time. I was thrown straight into a rather deep dive, doing work that
involved custom Data Providers, item proxying and in general trying to figure
out ways to best integrate a lot of external data into Sitecore. Early versions
of Sitecore 5 were… let’s use the word “challenging”, and Sitecore documentation
and release notes were scarce, at best.<br />
<br />
So yea; the first couple of years of blogging was almost entirely focused on
sharing information that perhaps wasn’t otherwise apparent and openly discussing
the direction Sitecore was taking on what I then believed to be a vitally
important data integration features. These features are still important of
course, but my view on this has become somewhat more nuanced over the years.
More on this later.<br />
<br />
Right around the launch of Sitecore 6, we’re talking 2008/2009 here, I had
left the safety of employment behind and had started CorePoint IT Ltd – my 1 man
Sitecore freelance/contracting company and was trying to work closely with the
Sitecore UK office on helping out new Sitecore partners as they were brought
into the fold. Sitecore UK was a very different entity those days, only a
handful of people, working very hard to spread the Sitecore gospel to the
British Isles.<br />
<br />
“Yes, all fine and well, but how does that relate to this blog?”. Well it
does, see. Unlike today, there WAS no real market for Sitecore contractors back
then. We were maybe a handful doing it, and LinkedIn did not have a constant
flow of “Contract Requests” coming through on a daily basis.<br />
<br />
So initially, I had a lot of time on my hands. I wrote what I believe to be
one of the first ORM mappers for Sitecore – <a href="http://intothecore.cassidy.dk/2008/07/how-do-you-do.html">CorePoint Domain
Objects</a>; built a lesser known Ecommerce framework, and as my post count for
08/09 will show – wrote a lot of blog posts. It was all about visibility.<br />
<br />
And it paid off, too. Work started coming in, to pay the almost extortionist
London rent rates. I was <a href="http://intothecore.cassidy.dk/2009/01/sitecore-mvps-announced.html">awarded
Sitecore MVP</a> as one of the 10 first in the world and things were getting
busy. So much so that I pretty soon found myself almost drowning in work and
Sitecore services was coming in demand all over London (it was still
predominantly London, at this time).<br />
<br />
It also shows in my blogging activity ;-) More posts were written in 2009
alone, than all remaining 9 years on this blog. As a contractor, one doesn’t get
paid to blog (obviously) and while many bloggers today find time to blog on
their employer’s dime, I have always been doing this on my own time. Time I take
away from either work or (more often) family activities.<br />
<br />
2010 through 2012 was “more of the same”. I went back to Denmark due to a
family related issue, ended up staying almost 2 years working almost exclusively
with Sitecore ecommerce related sites and solutions – architected yet another
version of an ecommerce framework (based on ideas developed in 2008/09 and
further refined here) – again being kept so busy, there was little time (or
rather; energy) to focus on other things such as this blog.<br />
<br />
2013 sees me back in the UK, this time outside of London. The market in
London was now brimming with Sitecore contractors (though still not enough, by
far) and my idea was to head out west – to Bristol – and service an area that
was not well covered by other consultants. Not long after (but entirely
unrelated, although I like to claim otherwise ;-)), Sitecore themselves opened
up a branch office in Bristol. Excellent news, this also meant local Sitecore
User Group sessions – and I remember the very first one I attended, was when
Sitecore 7 was being presented. <br />
<br />
Initially I get involved in yet more ecommerce on Sitecore with a local
Sitecore client (as opposed to an agency) where I spent the better part of a
year, but pretty soon thereafter it becomes apparent that the market “out west”
isn’t all I had initially hoped. To keep work coming, meant I had to consider
contracts pretty much all over the UK. That meant a lot of travelling, a lot of
hotel rooms and you know… to be honest, it all became a bit too much.<br />
<br />
I spend
2015 re-considering what I want to do with it all and by late 2015, having
arrived at some conclusions, I make my biggest change yet and move to Switzerland. The German speaking market is massive; and there is currently quite
a huge lack of qualified Sitecore developers and consultants – I figure I can
keep quite busy here for years to come :-)<br />
<br />
But enough about me.<br />
<br />
<h2>
Visitors and readers – Who are you?</h2>
<i>Apparently, Blogger has decided to discard some of its data so unfortunately I cannot share with you the complete traffic numbers. Only data from the period "May 2010 – January 2016" is kept.</i><br />
<br />
So who are you? This blog doesn’t run on WordPress so I cannot do one of
those fancy “You filled the Sydney Opera House 27 times over” type of posts. I
do have some statistics to share with you however.<br />
<br />
And it would appear, the vast majority of you are from the US, with United
Kingdom and France as runners up. If anything, France being in the top 3
surprises me; I am unaware of France having a particularly high Sitecore
adoptation rate. Looks like I could be very wrong on that :-)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVgHZUWdEIYZMiJGpOn0jMl8zZCbDtdSeiQXlLOfrhJGqzT4mqLqfQamjjmTNelxGyi5I2sYJ_AUAlp9L79NFR5L2IkkJzCEWD4EglICrPdKiPGKbbJeF7MEwRFevs4moQIdD6/s1600/10-01-2016+12-57-37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVgHZUWdEIYZMiJGpOn0jMl8zZCbDtdSeiQXlLOfrhJGqzT4mqLqfQamjjmTNelxGyi5I2sYJ_AUAlp9L79NFR5L2IkkJzCEWD4EglICrPdKiPGKbbJeF7MEwRFevs4moQIdD6/s640/10-01-2016+12-57-37.png" width="516" /></a></div>
<br />
All of this adding up to: “Pageviews all time history - 237,063” (again, since May 2010).<br />
<br />
In terms of what posts you liked, 2009 seems to have been the absolute golden year with 6 of 10 posts in the top 10 coming from that year.<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJjGNlhaLzZcJV9u4SEsX4jTEAfOMyHLxu0-4JR6mjIn165xH8U2icNtHzX1YbvdsidJXqKBhnlpSEA7YIoburFIDX4JZbiruUrdrpRIPHwulZ4Od0t5EtXfjiyfg9-uRv9Rb0/s1600/10-01-2016+13-04-48.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="235" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJjGNlhaLzZcJV9u4SEsX4jTEAfOMyHLxu0-4JR6mjIn165xH8U2icNtHzX1YbvdsidJXqKBhnlpSEA7YIoburFIDX4JZbiruUrdrpRIPHwulZ4Od0t5EtXfjiyfg9-uRv9Rb0/s400/10-01-2016+13-04-48.png" width="400" /></a></div>
<div>
<br /></div>
<div>
<div>
On a personal level it pleases me to see topics such a Sitecore migration at the top since this is one of the areas I would consider myself a specialist in. For some reason or other, I’ve just ended up doing SO many data migration projects with Sitecore over the years. Looking at this, I should probably schedule a few follow-up posts on this.</div>
<div>
<br /></div>
<div>
Also pleased to see some of my “recent” posts (and one from my friend and fellow blogger; Finn Nielsen) make it into this list. Gives me hope that this blog has more than just historical significance ;-)</div>
<div>
<br /></div>
<div>
There is also a statistic for traffic sources that I won’t even bother screenshotting and posting here. 97+% of traffic comes from Google referral, the rest comes from StackOverflow and a few forum posts on Sitecore Community Forums.</div>
<div>
<br /></div>
<div>
And whereto from here?</div>
<div>
<br /></div>
<h2>
The next 10 years of this blog</h2>
<div>
10 years is a very long time. When speaking of Internet technologies it’s almost more than a lifetime. So no, I don’t actually know that I will be actively blogging and working with Sitecore, 10 years from now. What I do know, however, is that I expect to be.</div>
<div>
<br /></div>
<div>
What I also know, is where I want this blog to be heading in the time to come. </div>
<div>
<br /></div>
<div>
In my opinion, for this blog to have any justified existence (or any blog for that matter), it must be focused on original content. To me this translates to less “How-to” content, and more “Why or why not?” content. If you take a quick look around the Sitecore blogosphere landscape today, the reasons for this should be obvious. </div>
<div>
<br /></div>
<div>
When I started blogging in 2006 there was little to no documentation, and probably there were less than 20 of us blogging. If even 10. Blogging original content was a no-brainer. We were all working with pretty much a blank slate and there was so much to talk about.</div>
<div>
<br /></div>
<div>
Today, 10 years later, there are more Sitecore blogs than I know the number of. I used to follow them all on a blogroll (up until Google shut down Reader :/) – today this is (almost) impossible. Impractical, at least. Ironically this leads to the almost exact opposite situation as we had in 2006; it is becoming increasingly difficult to now find quality information on any particular Sitecore subject. Not because it isn’t there; there’s almost too much of it. </div>
<div>
<br /></div>
<div>
So anyway. To stay relevant, I am going to take my 10 years of experience, over 20.000 billed hours of Sitecore consulting, and put myself out there and post (even more) opinions. The way I see it, opinions and practices I have formed on working with Sitecore over all these years are <b>highly</b> sought after by employers and agencies; surely these must have value to the readers of this blog as well.</div>
<div>
<br /></div>
<div>
So more “What does IoC and DI do to your development teams velocity?”, “What developer profiles make for good Sitecore developers?”, “The Content Release project milestone”, “How does Agile fit with your Sitecore development project?” – and less “Using Lucene and SOLR side by side with Sitecore Content Search”, “Load balancing your MongoDB setup”, “Multi-select dialogues with SPEAK 1.1”.</div>
<div>
<br /></div>
<div>
To be clear, there is absolutely nothing wrong with the “less of” examples I mention here. I just won’t be focusing on them; I believe we as a community are pretty well covered on those as it is.</div>
<div>
<br /></div>
<div>
To start things off, I will launch a project I have been working on for quite some time now. The “Sitecore Decennial Series”. What this is, is basically 10 posts each focusing on one aspect of Sitecore that in my opinion is key to success with Sitecore implementations. Not necessarily all from a developers perspective. Each post will be rather long – beware, those of you not into that kind of thing – and for this reason I am also going to set a realistic schedule for myself on these. The series will run over the course of this year, with a post every month – leaving 2 blank slots (TBD) which I hope to fill up with a bit of R&R and holiday time ;-)</div>
</div>
<div>
<br /></div>
Happy New Year everyone.<br />
<br />Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com2tag:blogger.com,1999:blog-20883010.post-16572203085375948942013-11-30T15:56:00.001+01:002013-11-30T15:56:10.195+01:00Working with Page Templates<h4>While keeping your Sitecore solution flexible</h4> <blockquote> <p><em>This blog post is part 4 in a series on “</em><a href="http://intothecore.cassidy.dk/search/label/Creating%20good%20Sitecore%20solutions"><em>Creating good Sitecore solutions</em></a><em>”. Rather than constantly going back and adding edits to the previous posts, just follow this link to get an overview of the series.</em></p></blockquote> <h5>The story so far</h5> <p>By following the recommendations I’ve presented in this series, you’ll find yourself building a site that has little or no reliance at all on Page Templates. For starters, this is a good thing. Don’t worry about it.</p> <p>By doing so, your solution stays on top of the Sitecore feature set – it can be personalised, conditional renderings can be swapped in and out and your marketing users can select winners of M/V tests and go about their business – just like intended. As I’ve argued earlier, the reason you are building this site in Sitecore at all over say… some of the competition… would be because of the distinguishing features of Sitecore. Not for the ability to build websites in Sitecore – I’m reasonably sure this can be accomplished in most competing CMS systems – but because Sitecore offers this great suite of tools surrounding it. The Sitecore Customer Engagement Platform or CEP.</p> <p>Enabling your solution for these tools really comes down to this: </p> <ol> <li>Individual components on your site must respond to changing Datasource settings (configured by marketers, controlled by Sitecore)</li> <li>In the extreme, you as a developer really have no real say at all in what components end up on any given page. Marketers can, will and should perform testing of various component combinations on any given page and select winners accordingly. Sure, you can restrict this to some extent – but why should and would you?</li></ol> <p>All of which is fine, as long as you’ve implemented your components following the principles from this series of posts.</p> <h5></h5> <h5>What then, do we use Page Templates for?</h5> <p>Having come this far, it’s probably time to take a look at what Page Templates then really should be, and how they can be used to make not only your life easier, but also that of your marketing users.</p> <p>The first and most obvious thing that belong on your Page Templates, is “everything that isn’t really part of any component on your page”. Depending on how far your take your components, this could be things like:</p> <ul> <li>Page description and keywords – for Google (if you still use these)</li> <li>NOINDEX, NOFOLLOW checkboxes</li> <li>Page titles (browser title, menu title and so on)</li> <li>Page theme</li> <li>Canonical URLs</li></ul> <p>In essence, anything that you would reasonably output as part of your Layout and Layout code. Data you will be pulling, at runtime, from Sitecore.Context.Item and where the concept of a Datasource makes no sense at all.</p> <p>Fields like these, I tend to bundle up on a base template I name “Page” (appropriately), and all further Page Templates I implement will be inheriting from this. </p> <h4></h4> <h5>And what else?</h5> <p>You’re not going to like this answer. “It depends”. From this point on, what you choose to implement as Page Templates is up to you. I can tell you a few considerations you should have when deciding, and a few of the trade-offs you’ll be making however.</p> <ul> <li>Are your marketing users accustomed to working with Sitecore? And if they are, have they primarily worked with “traditional” Sitecore implementations, where pretty much all content sat on just one single item (a Page Template) in the Sitecore Content Editor?</li> <ul> <li>If so, you should probably cook up a series of Page Templates for them. Fortunately, creating and altering Page Templates is now a much simpler task than it would have been otherwise, if you’ve followed the principles in this series of posts. I’ll demonstrate shortly.</li></ul> <li>Are there areas you are absolutely sure will not be subject of M/V testing?</li> <ul> <li>I can tell you this; pretty much EVERY single time I’ve made a “judgment call” on this, I’ve been wrong. Sooner or later, once the marketing users really get into the spirit of really using Sitecore and working with their site, they will be wanting to M/V test the “strangest” things (and they should). All of these from real life, by the way.</li> <ul> <li>The ordering of the main navigation menu</li> <li>The naming for the “My Account” area</li> <li>Placing the “left navigation” on the left and right side of the layout</li> <li>Backdrop images for the site</li> <li>“Sign in” versus “Log in” button text</li></ul> <li>Truth is, if your marketing users follow the spirit of continuously testing the site to improve conversion rate (and why wouldn’t they?) – every component is a candidate for testing</li></ul> <li>What about content pages that your marketing users create often?</li> <ul> <li>Indeed the traditional candidate for Page Templates, and you probably should set up Insert Options for these</li> <li>However keep in mind that the concept of <a href="http://sdn.sitecore.net/upload/sitecore6/sc60keywords/clientconfigurationcookbook-a4.pdf#search=%22preset%22">Layout Presets</a>. You could in reality just give your marketing users a set of insert options that all are simple inheritances of the “Page” template, with different presentation details on them or – alternatively - different Layout Presets</li></ul> <li>Large volumes of “page” items</li> <ul> <li>Like an article repository or some such. Yes, you definitely should base these on a common Page Template. But that’s not the same as saying, you should necessarily have a tonne of fields on that template; you’re doing it primarily to have a single place to globally change Presentation Details for these pages.</li></ul></ul> <p>What I’m getting at is this. Most of the reasons that I – and I suspect many – Sitecore consultants have set up Page Templates in the past, really came down to ease of managing Presentation Details for a given page type. So we’d have a lot of content in the solution, say 1000s of “News Article” items for instance. And then along comes a marketer, wanting to show the “Spring Sale” banner on the top of all of them. No problem; find the “News Article” template, edit the Standard Values Presentation Details for the template and you’re done.</p> <p>The less common scenario – but quite challenging if you’re following the “old ways” would be; marketing user comes along and wants to replace “Component XYZ” on the “News Article” pages with “Component ZYX”. The two components are not alike in any way, and use completely different fields.</p> <p>Sure, part of the challenge is easy enough. You go to the “News Article” template Standard Values, you swap the components around. But what then? Do you then change the inheritance of “News Article” so it includes the fields required by “Component ZYX”? You would have to, unless it was implemented following the principles of these posts – always respecting it’s Datasource, and being based on a known Datasource Template. If it doesn’t, you’re out of luck.</p> <p>So you proceed and change the inheritance of “News Article” – you put in some default values on the Standard Values item for the template. The rest… well that’s up to the marketing users. There’s 2 problems left lingering here:</p> <ul> <li>What about the fields being used by “Component XYZ”? If you’re lucky, you know exactly what they are and you can proceed to remove them from the “News Article” template. Let’s just hope the fields are not being used by other components on the page</li> <li>Your marketing users can’t edit Standard Values. If the default values you configured need to change, they would need to come to you to get them changed – alternatively manually edit the 1000s of pages (and this, they won’t be happy about)</li> <li>Ok so there’s 3 problems. What if “Component ZYX” really needed a different set of values based on where in the content hierarchy the News Article resided?</li></ul> <p>No matter your approach, none of this is truly ideal. Having fields on Page Templates is a compromise in pretty much any way you look at it. Both in terms of flexibility and what you can do with the site, but also how you make changes to it going forward.</p> <h5>But if you really want to, here’s how easy it is to make and amend Page Templates following these principles</h5> <p>Having said all of the above, you will still be doing Page Templates. Especially if you’re dealing with marketing users who’ve never known Sitecore to be anything else than the Sitecore Content Editor interface, never did an M/V test in their life and don’t plan to. My own opinions aside; these absolutely do exist. Cater for their current needs, but don’t bar them from moving forward using Sitecore.</p> <p>Fortunately, this is now easier than ever. Say I have a page like this.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIHTg98iXA3Fw3GdCgalqOPrmD6YJgIf69mg0l5d2noHptTo8l8FD7XCMFT332-klufCj_XThmYOvQ5mnjAmxp0Apd3JMdm4p6oQL3y5hutWWOqKyInuU4URfbS8d2jP7GLb8M/s1600-h/30-11-2013%25252012-53-13%25255B6%25255D.png"><img title="30-11-2013 12-53-13" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 12-53-13" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimpQFs9752SjPX1s-g3KIxFq806Sj_0ZDi3UW6bChngYdYFfxGun6ewp-Gg2UzfxnQJr9YAHJQgQzepIpb8-P8wsCtVt6IIFpx6mbuGCuQh29AYWzbQVGBjwoR4B5CVwWdjD6_/?imgmax=800" width="556" height="418"></a> </p> <p>As shown in my previous posts in this series, this just sits on a very basic “Page” template with no fields on it. All of the components are implemented following these practices, they respect the Datasource given and will fall back to Sitecore.Context.Item if no Datasource is provided for them.</p> <p>Presentation details for this item looks like this:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj23hig0GZx3T0dKDVuU-UhXzv5dPTcwMDOLucNttos_BNgnbX73FYMEKzoqFXCUOMU78NPaqoNiWGM9nNxxl2DwI1C_LxgEG4Vih5E_Y3caDWm0ekCLA9zhLNT2CCPsWeB5WWA/s1600-h/30-11-2013%25252012-56-03%25255B3%25255D.png"><img title="30-11-2013 12-56-03" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 12-56-03" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjN91vW63QZnwAjBgnr_h9y2f6kFOY7ekq5-tHXdY2BXj5jMwPWEQUXgHLJwDeak5303dITYSfpgAmAlPHsXvVeMdDAgaqlrEzH2Ryf8kJrTqGhXC-NbZ4Nec2a2tVUI3gcdtia/?imgmax=800" width="397" height="486"></a> </p> <p>And here’s the item as shown in Sitecore Content Editor</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLcDPkfWtFqUEynPe_t7LkW0tR_82K9excedbpftpnYIFOzz3wfbtb0bufJLb_gj9JDUmXuqSMpOqt8iIKJ1f8DmuJ5uNGYPsQYexGtJhMVGshGMhDGFwUv_b3SwJcI_-20IRE/s1600-h/30-11-2013%25252012-57-36%25255B9%25255D.png"><img title="30-11-2013 12-57-36" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 12-57-36" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXeYBMbs72iB4SLfEaZJgRexQryS3cAbYwRW3JiLk2OGo5cGyPB13Vf-4juARpEN068JpWvI_dpIB_MJpoWZa8A9CR2V_Y4g3A6F9VkBLyA8q2OOQSKrpaAal6SvceEDnwxLuI/?imgmax=800" width="617" height="297"></a> </p> <p></p> <p>Let’s then say that I want to make some of the components part of the template for this page. Header and the Top Menu. No problem at all, I go to the Site Root template I’ve set up for this page. This becomes even easier if you have a snapshot of your presentation details showing on the screen.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNzo2_FtZsd89P_G7jpoFJaFLrPGIrFV4Y92ytx-Wnhmu7iX3i3Ry8IVshx8dNxQc2k7lJB9eq7z5UjVuhfdaEZqcA172NvwsLH40y6c9UTReTK8oNnFU2UMdS3TXLwgWyYZXU/s1600-h/30-11-2013%25252013-02-19%25255B6%25255D.png"><img title="30-11-2013 13-02-19" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 13-02-19" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-OH86V1MnRnBRgQ8aqmrq0_OWuUQpvqzQJZJ0H20kM9VSTKRXZg8CW26ftWwRbK0bEDNbN1ZtN2flnTQo6C9C89mLwWOv9V2bPm2qyQPyH_phUkSj3cUQePi_A8htzg7lkECs/?imgmax=800" width="628" height="271"></a> </p> <p>The icons you’ve configured for your Datasource Templates help you here. Having done this change, my Home item now looks like this.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwlZEUYgwKyMNDfF62YgmNxeaVaDlqNEvXpcv7CW_i-Keg2pHqrZ0snzgumNo3m1Gcjx_9Ur5n7vPkpVsxkrxRrXLeMUNJA_71TYgOy_3pQgJDAA-_EO5ZbbEnRG8COJAh_qxP/s1600-h/30-11-2013%25252013-04-43%25255B7%25255D.png"><img title="30-11-2013 13-04-43" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 13-04-43" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPZ-DLAPFGrsQhcLS_fc3KTl-coSLJC43CzqoKZTcBolP549JzIDDzGtsXTDr6bwS5wQOd4Ymr8PyQVMEW_MkkCi0Tbvsrh087nPDmqVN2BiHsDI_SM6ycv6TN1OCCgFeKG8wL/?imgmax=800" width="640" height="415"></a> </p> <p>So far, nothing has changed on my site. The presentation details for my components on this page still point to the Datasources I have configured. To change this, I need to then go to my Standard Values for the Site Root, and clear these fields. While I’m there, I set up fill in some Standard Values content on my new fields.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig6Drt9VktzBbNHh5-IyEuuylcpO1k-6wREBTE1tChymKFwKO0BDioMVnYAl-lVuBrbsz-e0PlyXkvMgrVO59hRtJUF1bHqe_jaRYW3uev99aDtRqZhndFOX5aPksdrxJc3Tyg/s1600-h/30-11-2013%25252013-08-25%25255B4%25255D.png"><img title="30-11-2013 13-08-25" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 13-08-25" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiFmsXbm1itfI3Q3z-mqw8Rlh0pV8iLWxNE29tYDd_NtnH1ZjOinatT-zGK5pl1RVHu6YAv6cjMERk3TjskrcbZgRWqtEeXWi4W9Jv6drZ4BHWTlzgBB_2J5RS33Kby5Lw46Ex/?imgmax=800" width="652" height="542"></a> </p> <p>I clear the Datasources for the components I’m now making part of my Page Template</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOT0Kika2rjB3Z9WCY11ig0eQtORCqJrbdZ0IA6tMTTyuYDbg8d7UjbBPCmFCC7g3bRbPUpMVo1RShFPaCp_GBVTy1_gzp6232Ta52CiN7MF_MMty6UbKrtoFGvO6W5q-pHH_l/s1600-h/30-11-2013%25252013-09-33%25255B4%25255D.png"><img title="30-11-2013 13-09-33" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 13-09-33" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYe5E5O9cQ-cJlEeqb9a3yX0Nzz6Ho_jXqhzWE0WUAFpWUmhdk4_pNKz0vWk1uJjQ1GBZN4pogKaLd1Cv9gExi2o1cfJWAEbO_VcKnccDsGcWo1QmdJatAbnM-Hp7JI-MD1nNA/?imgmax=800" width="541" height="318"></a> </p> <p>And after a quick publish, the site now looks like this.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwVNE5PNZEfbR7Qx-i805X3DpuY2D3fKFJUrkSh_8a5oFpODV2O9LgzP-yKPQyNVUz9v3hMspOKNS4kaxRNeKhz-MtqP_Pm0b0WPcGtagwuJQOM42M5qpMlaMILILp7kbfHIXg/s1600-h/30-11-2013%25252013-14-10%25255B6%25255D.png"><img title="30-11-2013 13-14-10" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 13-14-10" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijpWJ8eV0my_eAoE32nP9mn5G7MZ4PFId4FkvsSD0k0LjWrGjUc_WwBQ3xWG14cpuvghZ56yNLnojPNwChluUgSdlMrFQTg8W7Vf5IWQbvNonParWla1x5BU-kiI-VwCLP2Eud/?imgmax=800" width="554" height="175"></a> </p> <p></p> <p></p> <p></p> <p></p> <p>For all intents and purposes, my Home item now looks and acts like it was created using traditional “Page Templates”, but I retain the confidence that I can make amendments to this structure easily. The components can still be tested and swapped around and so on – but my marketing users can still keep using the Sitecore Content Editor to work with my “Page”.</p> <p align="center"><em>For those of you who read my previous post in detail, I would actually normally <br>choose the DatasourceOrSiteRoot strategy for things such as <br>the Header component, but that’s not really relevant in the context of this post.</em></p> <p>You can, of course, add as many base templates to your Page Template as you desire – for a full blown “Sitecore Content Editor” experience. Remember I said in a previous post; you’re not really building designing your information architecture in the manner I lay out in these posts only to cater for the Page Editor users? Well this is it – you do it for your Content Editor users as well. And your solution will be better off for it, for both types of users. </p> <p><strong>And you’ve still not incurred any cost!</strong></p> <p><em>I stand on my argument made in previous posts; building up your information architecture in this manner from ground up adds no cost to your implementation. </em></p> <p>And I can honestly say, making and amending Page Templates has never been easier. Making them becomes as easy as just identifying which components on the page you consider to be “always part of the page” (and disregarding what I said above; in reality you risk making the wrong judgment call on this) – and make your Page Template inherit from the respective Datasource Template templates.</p> <p>By doing this; everything still works “as before”. But you now have the certainty (because you’re components are done right) that you can make amendments to this decision in the future. And yes, everything on the page can still be personalised and tested(!). Yes it really can; setting up a test is as simple as defining the test variations (this, your marketing users would need to do in any event) – and setting them up. Default would just be the blank Datasource.</p> <h5>M/V testing when using Page Templates</h5> <p>Set up the variations. I suggest creating a folder with a meaningful name, so you can later do a bit of cleanup and get rid of the losing variations.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWd6KeOxGWxX4y_J1u-m7TMk4MVdm79ECiic-0yGhhpGh1BHWFYNkHUNb9-Z91qfFhAVrdqVQQN5fmWrwavZI3HTHh6rpafpKbw7tTmRbi47Py9hABAWeU8ICSGDlBymzHVn1O/s1600-h/30-11-2013%25252013-46-51%25255B2%25255D.png"><img title="30-11-2013 13-46-51" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 13-46-51" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYwAfXjT8Svx-CO-VS2hQxjdiV8uqJctoa0J_z8QBA53QuQhzYEtwimmPdL6QUkMvaaKtnfHSpVzaJKYa8OGCtQNLfhpSlnTku3KRDrUW93BiBbuLfka1QpL_UrwojcI-HLgWh/?imgmax=800" width="244" height="155"></a> </p> <p>Set up the test, as you normally would. </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZg3wUkf_mMXxgh32pL0xJgJ-YjF5khU0i_iglRo_B9y4_hCpLIhYwG_M33t3159EIvRW7VtwheNASCcl7T3TOBBleHlfSVdVRSN54G1quBNnVU2J4FVScLP-1lqHXBp3l9bkX/s1600-h/30-11-2013%25252013-49-53%25255B4%25255D.png"><img title="30-11-2013 13-49-53" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 13-49-53" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpiPE2bW12EsIA1_WF_fqIegtHfIehq5VmRBRN2LF9Hz0rtMUi7btDAKbFxDWuTXEjm8-tcQ-UI29kkDUOhN1Cga_FUIzII7w6aWDAwOZ9FS-U55oMWQoeNhARRJLFTN2FHIq-/?imgmax=800" width="562" height="360"></a> </p> <p>And you’re good to go. Your problem only comes, when a winner is selected. This problem is inherent to Page Templates. Consider this:</p> <p>The test runs, and ultimately the marketing user will select Variation B as the winner. What Sitecore will do is this; it will change the presentation details – so that the <em>Datasource</em> for the <em>Header</em> component gets set to <em>Variation B</em>. And this is fine – if your component is done right, the site will now be showing Variation B whenever rendering the Header component.</p> <p>Only problem is; you’re still stuck with fields on your Page Template that now no longer hold any meaning. The Header fields on your Page Template are not being used and only act to confuse your marketing users. At least the ones who use the Sitecore Content Editor; in Page Editor everything will still appear normal.</p> <p>Out of the box, Sitecore does not directly offer any solutions to this problem. Personally, I try and avoid Page Templates as much as possible. Fortunately there are several things you can do to solve this; easiest being to just go to your Site Root Page Template and remove the Header inheritance – fields will be gone and the world moves along. I’ve also seen a number of blog posts recently (although a specific one fails to come into memory right now) that deal with ways of hiding fields from the Content Editor for various reasons.</p> <h5></h5> <p></p> <h5>Making changes to Page Templates</h5> <p>Not really any different than before, but you do have a few more options available to you if you’ve followed the practices laid out in these posts.</p> <p>In the example from above, you could also choose to just add your new “Component ZYX” to the Presentation Details of the Standard Values item and have its Datasource point to somewhere in your content repository where the marketing users can reach it. That way you avoid the problem of restricted access to Standard Values entirely.</p> <p>And what if you had to solve the problem, of changing the content of “Component ZYX” based on where the News Article resided in the content tree? Still not a problem; personalise the component using the Sitecore Rules Engine – like this for instance:</p> <p>(I don’t have a Component ZYX, so I’ll pretend my Header component is it)</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwBw2TaIBp8T8tE5MczMoq2EKORpXvO2zjAuX2G4JcwvSTB-FW90a_bdE3mFEqJH0Cm32YZ51u8xJdBYKyoR5W9minw_BXPz8YX9csBR6IMAQnQJ-qWSTywpNWXuSjLEz126BQ/s1600-h/30-11-2013%25252014-17-47%25255B4%25255D.png"><img title="30-11-2013 14-17-47" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="30-11-2013 14-17-47" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqy-J68XuUu_s3QJagB3B6k5tDns-ZMDjHFBEI1oxbfA98Pwh-isE-zfxPh6P0QZk3nWorYCj7Oz897VGA7PJbGSbTPCyjaLCVBc-5l9MNvDl7BQvrDhtYw-OpRa2lh0c8fWnU/?imgmax=800" width="571" height="327"></a> </p> <p>Easy, but not so easily achievable had you been using the traditional Page Template approach. Or as I’d also like to put it; had you made <a href="http://intothecore.cassidy.dk/2013/08/the-page-template-mistake.html">The Page Template Mistake</a>.</p> <h5>In summary</h5> <p>This will be the last post in the series “<a href="http://intothecore.cassidy.dk/search/label/Creating%20good%20Sitecore%20solutions">Creating good Sitecore Solutions</a>”, but it will not be the end of what I have to say on this subject – not by a long shot. Everything I write about is based on my personal experiences, and I do follow the practices in these posts in my daily life as a Sitecore Consultant / Architect. I will keep posting about the experiences that come from this, and I also have a few tricks up my sleeve for solving some of the common issues that might arise, when working in the manner described here.</p> <p>There are a few pitfalls, I’ll be the first to admit. But I’ll quickly add to that; the pitfalls I’ve encountered using this approach are by far less and fewer, than the problems I’ve been in when using Page Templates and components that clinged to them.</p> <p>I hope – if nothing else – that the series has inspired a few new ideas out there :-)</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com2tag:blogger.com,1999:blog-20883010.post-35450666354881653212013-09-05T00:56:00.001+02:002013-09-05T00:56:16.366+02:00Working with Component / Data Templates<h4>Avoiding the Page Template Mistake</h4> <blockquote> <p><em>This blog post is part 3 in a series on “</em><a href="http://intothecore.cassidy.dk/search/label/Creating%20good%20Sitecore%20solutions"><em>Creating good Sitecore solutions</em></a><em>”. Rather than constantly going back and adding edits to the previous posts, just follow this link to get an overview of the series.</em></p></blockquote> <h5>A few thoughts</h5> <p>As promised in my last post; <a title="2nd post in the series on "Creating good Sitecore solutions": The Page Template Mistake" href="http://intothecore.cassidy.dk/2013/08/the-page-template-mistake.html">The Page Template Mistake</a>; I will now attempt to put my money where my mouth is (so to speak) and provide something more concrete as to how the <em>Page Template Mistake</em> can be avoided, and how your implementation might carry forward without encountering it.</p> <p>Before I do however, I will just clarify a few things that perhaps got lost in the previous posts.</p> <p>I <strong><em>do</em></strong> believe that there is a time and a place for <em>Page Templates</em>. It just follows much later in the process and is a much more dynamic entity than what was certainly the case for my own solutions. I would not recommend content editors being forced to add any and all components to a blank “page canvas” for every page they create – we have Page Templates, Layout Presets and other technologies to help them out here.</p> <p>What I advocate is, that you move the entire consideration of <em>Page Templates</em> much much further back in the process. Move it to the very end. Both to help you verify and assess components as the project comes about (if you’re tasked with that role), but also to help developers break free of the Page Template mindset entirely.</p> <p>Your project will be better off for it.</p> <p>My next post will be coming full circle, and demonstrate how Page Templates can be set up once you have all your Component Templates in place and – and I feel this point is important, because this used to be such a hassle for me in the past – how you can easily and quickly mock up new <em>Page Templates</em> as the situation requires, without breaking a sweat of what might break. Think yellow screens, think “Object Reference Not Set…” – all the common mishaps that you’ve experienced when tossing components onto a newly set up Page Template in the past.</p> <p>Lastly; You’re not doing this specifically when “designing for the Sitecore Page Editor” as a few have suggested. That is a misunderstanding. What I am suggesting here is an approach that you would be taking if you’ve decided to make <a href="http://intothecore.cassidy.dk/2013/07/how-do-i-create-good-sitecore-solution.html">a good Sitecore solution</a>. That it also happens to be a prerequisite for most meaningful work in the Sitecore Page Editor is consequential and accidental, you should not set out to “design for the Page Editor” at all.</p> <p>After all – in doing so – you are inferring that this is a specific task, possibly an extra task. It is not. What I am suggesting is <em>cost free</em>, adds no overhead to your project. If done up front, that is. If you start by deciding to <a href="http://intothecore.cassidy.dk/2013/07/how-do-i-create-good-sitecore-solution.html">build a good Sitecore solution</a>.</p> <p>And on that note, let’s get to it.</p> <h5></h5> <h5>Implementing a Component Template</h5> <p>To serve as an example, I’ll pick a component from the – in theory – up and coming CorePoint IT website (my own one-man consulting company). I say in theory because just as the mechanics own car, somehow working on this website always seems to end up fairly low on my priority list ;-)</p> <p>I’ll pick something simple to start off with. In the HTML I’ve had done for the site, it looks like this:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4TNLbscDJ758XkvqGKEJAh70BaNYivCqcaaOIKGSN3DgwBgwFqe-dHYo3fTp-7U5kWpeSsQSxWy6nZ5UB1IMr7DpcEwoneG_-U1BMVrCyny5rfY7AOM7hFcv9oHdQAsHNzkxD/s1600-h/04-09-2013%25252019-13-20%25255B4%25255D.png"><img title="04-09-2013 19-13-20" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 19-13-20" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhAu1vbDxTJLL_sI8x_mMKkz_i9zYngJ73UBWWrP1W9kAP8MTvxVTQ_j32p0TQpuZMZV8nW71iCQ1MePr0HaU0nBaUsZ-8qpZeRkHa9HMBBiHRRatfPYdivkEjSvipwEDHTeWb/?imgmax=800" width="555" height="185"></a> </p> <p>Looking at the HTML snippet for this component, it looks like this.</p><pre class="code"><span style="background: white; color: blue"><</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">id</span><span style="background: white; color: blue">="topBanner">
<</span><span style="background: white; color: maroon">img </span><span style="background: white; color: red">src</span><span style="background: white; color: blue">="images/banners/glass.gif" </span><span style="background: white; color: red">alt</span><span style="background: white; color: blue">="" />
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">id</span><span style="background: white; color: blue">="topBannerContent">
<</span><span style="background: white; color: maroon">h4</span><span style="background: white; color: blue">></span><span style="background: white; color: black">The company that does anything...</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">h4</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">p</span><span style="background: white; color: blue">></span><span style="background: white; color: black">Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">p</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">a </span><span style="background: white; color: red">href</span><span style="background: white; color: blue">="#"></span><span style="background: white; color: black">Read more >></span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">a</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
</span></pre>
<p>No big surprises there. Not too happy about the “id” attributes in there but let’s set aside that discussion for the time being.</p>
<p>I identify 5 tokens (tokens is a bad term. Anyone have another suggestion?) in there that I would like to see content managed on this component. </p>
<ul>
<li>Heading</li>
<li>Body Text</li>
<li>Image</li>
<li>Link Text</li>
<li>Link</li></ul>
<p>Breaking up the link near the bottom into two separate entities is a habit I’ve adopted. While it is possible on a Sitecore “General Link” for content editors to specify things such as Link Text; this doesn’t hold true for “Internal Link”. Doing it like this as a convention just saves a lot of grief.</p>
<h5>Setting up the Component Template</h5>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtXHQDb1CIGfzsu8Aw0dfx9BdT8e4FMZvn8NnBcza7_coMViNZgICmxEwBOMJTGt4mIW0NYjESKHy099IuZUUkLTVM4cDFN-f3Vpeqnd3S87eD0pjIqXm5ctg0hZqoHjEPZ9gE/s1600-h/04-09-2013%25252019-34-45%25255B6%25255D.png"><img title="04-09-2013 19-34-45" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 19-34-45" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYbvVNWcMx2U9HsVoTH8EmX2ft5GFkxueDqb37X57iL8GTAfGdTuKGgF_LZKRFUluAbA0j-vq2C91W4EqAFOKYdYXqAw0RgcckrnneiOnNl_tcsZhfys7selqJH-JWQbFRYzeo/?imgmax=800" width="578" height="296"></a></p>
<p>While this may look apparent, there are quite many (of my own) conventions in play here. I recommend you follow them, each of them will grant you “+2 to the overall Sitecore goodness feel”.</p>
<ul>
<li>Give your component a good name. As “good” is subjective, here’s a few examples of component names that are NOT good (all of them from real life):</li>
<ul>
<li>phInnerHeadingBox</li>
<li>HeroBoxTopRight</li>
<li>Content Spot</li></ul>
<li>Set an icon for your Component Template. First thing, no delay. And while you can try and be creative and find an icon that “matches what your component does”, you will likely fail – the icon set is too generic for that. Don’t worry though, as it doesn’t really matter WHAT icon you choose – just that you choose one, and choose the same one in the steps to follow.</li>
<li>Place your fields in one “Section” named in exactly the same manner as your component. </li>
<li>Assign the icon from above, to that Section as well. Here’s how:<br><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMk-6t1utXXHm9GOlfWju8voO3rKhcwCxZlOnwGYwfUTKh0bP_zDTU4-av5T-kBC5TS3ykijpcUakcfDIyA8h7wPaJSB0cvRu16u0d_0pLERPv1gEgsK4m3ltq4icQ3WJzovTL/s1600-h/04-09-2013%25252019-43-12%25255B4%25255D.png"><img title="04-09-2013 19-43-12" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 19-43-12" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVeMZdGHJFgam-9-x0ChUJinOsKyp_KAORJ1nncBDHUB7D7Tcm6FTeEGlMnD-nRbF_lpaiD-pH_7D_8efRCf7p6D9-HFDW7r-9c5ANk35cwc7dTnyjNSq_HeSGdq43W_BdJhOY/?imgmax=800" width="397" height="388"></a> </li>
<li>Name your fields, prefixed with the Component Name.</li>
<ul>
<li>This is controversial, I know. But given that Sitecore does not distinguish Sections when addressing fields, and you cannot make any assumptions at this stage about the <em>Page Template</em> that your component will be living on (since you don’t know, and never will) – doing it like this provides the <a href="http://www.economicexpert.com/a/Principle:of:least:astonishment.html">Path of Least Surprise</a> later on.</li>
<li>Also keep in mind; field names mean nothing (really) when your content editor user is in the Page Editor – and given that the same user is given the context via the Section in the Content Editor, they won’t have much trouble scanning the fields for the one they need there either.</li></ul>
<li>Set a short help text for each of your fields. This often overlooked step will mean a world of difference for your Content Editor users, takes you about 10 seconds per field (at this stage in the process) and grants you “Sitecore goodness” karma. If you haven’t done this before – try it. Take my word for it. Here’s how:<br><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKT4tWBaBN2eLasJVzXvjmJwz0gPZUDE___nMoB-WwxiHqX_L-iQlIq1zUKmOnohAnyUDEoi09TlcOT-wEXU6tTSnVo3lmByFdt9NFWfQD3qL-L8K59pnxqJStpdv-8sv0EKnz/s1600-h/04-09-2013%25252020-04-21%25255B4%25255D.png"><img title="04-09-2013 20-04-21" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 20-04-21" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHNgY0DSMIumkTUDj9GIfGkBGYWtYJeyiRzt_OI6SoRjdDaSRTLUfTLfjmC88k8XiB2jnpmP2qP91ZWOGxI39FhGyUT0d-8kzWlViw9MF4opzHR7v5A543KWdzKjNK-CexqFty/?imgmax=800" width="494" height="192"></a> </li></ul>
<p>And you’re done. For now.</p>
<p></p>
<p></p>
<h5>Setting up the Component</h5>
<p>Proceed with your method of choice to set up the Sublayout Component in Sitecore. (Covering everything involved in this step probably could use a blog post of it’s own. I deem this out of scope for this one). Name it the same as your Component Template.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxeJstpahZ-HJXAmUYVHTq1mim55x8sPMGqAZ7VFU4CJxp2asl0Iq0gmQcYd1RQXbbine4tTTnXeW2l0ote2eL8I4aJ3vArSKn0wG_I4kYX2lzsN3ZaW14OQyh_e4izbgsVNLz/s1600-h/04-09-2013%25252020-11-03%25255B5%25255D.png"><img title="04-09-2013 20-11-03" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 20-11-03" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjWsRrhchTWjPdkkopW5aqX6cr-jNygUSBqWc_4Vc2EFKgb5pB64UbjRUB-HQUcg8YyKHOl80kN5BMXI6PSWcEOGs8wEsVVbo2NublSXzyUg-8Etwo6K3NeQx9TDg3VnpBXzCz/?imgmax=800" width="441" height="349"></a> </p>
<p>Carry out these steps:</p>
<ul>
<li>Set the icon for the Component to the same icon you chose for the Component Template.<br><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgg4pqfKi6VOMo-TqohlwS_wf8ciX844ZkMJO17XneYJgcHVz7ua0IbCuRA72CWvUURxC8Nv7ezjcn3Qts9tE2B7Wmqn3U2J5nW-Fls9cbg_tGEbqoeAcwV8Fe4CsHgDkt4haAP/s1600-h/04-09-2013%25252020-13-34%25255B9%25255D.png"><img title="04-09-2013 20-13-34" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 20-13-34" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieckYsnOq8UmnFNrAGp5tz57a2RYbVDHbNw4kTW8sGjP4SIYFvtjOgAaNHSuApipy7zL1PXLTTDm-k3ZLG7P6aRrcUvnCEvoNtkCFqrFsuDf8bKJ_LisJ9EecNP_iogtMx-ICU/?imgmax=800" width="338" height="303"></a> </li>
<li>Set the Datasource Template to your Component Template.<br><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvxDtNnnDtjWA-MeBRyQJqp353JqqAI33-t24kutycphYq2oY__OikkT3XgqwhPW3oAyn_FPmuoHRnI0Ve7i2MTh19hIwXj8BGYNpz8hS7gjrQ6OT9OhtJspVBrB7FiPgL0dnU/s1600-h/04-09-2013%25252020-14-59%25255B3%25255D.png"><img title="04-09-2013 20-14-59" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 20-14-59" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAA1IVoP67NOtc3RIghxa0Iv9vNja9bjsQgPmXSpbj0XQZW3v37_6C8RhoxGilDohpVqJLMe6LoycE7K458vR0yfUZLk9h5ZHKHj_oCNcL9Pko_AwXP1uWki_lxOJwA-Gbm_4v/?imgmax=800" width="413" height="197"></a><br><br><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnJVsmXayJ2uqSqyJUFc8A-7TeWf8h793YSLVP-CMt6CwSvXksIsKAJZ3GjGV3paTY8Hg_5KU0ZRlnIxfxqXQGVmPKMwHCn1vhBmWuk7yLBqQg9XO0or34D1hypguAE23UopoH/s1600-h/04-09-2013%25252020-16-04%25255B7%25255D.png"><img title="04-09-2013 20-16-04" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 20-16-04" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7rAhtBSbeML_Ijm59ro1pEbrLXy5nkA1lBA9xGe5BK9Nktwn6hu_sPjLmgEEOc9Vqo1sPZ0XLN5pf2M_Cwn4NhelsYoqokrKtLmIlXxoRz4X0vRHkfkDYNb1gfZwsLyvwA2aW/?imgmax=800" width="422" height="165"></a> </li>
<li>Finally take a snapshot of your component, and set up the Component thumbnail. </li>
<ul>
<li>If your Component is part of a multi-site solution and look completely different between the sites, leave out this step. Sitecore requires customization to be able to do site-specific thumbnails, something I might cover in a later post. Tweet if you want it. <a href="https://twitter.com/cassidydotdk">@cassidydotdk</a> ;-)<br><br>If there is no thumbnail defined, Sitecore will show an enlarged version of your chosen icon for the component when adding it in the Page Editor. Not ideal, but still sticks with the established convention pretty good.<br></li>
<li>Set it up like this.<br><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaGPhrQWohjkvwDCIJjsqeI-nsxwiPZjE4Wc_48gXscTIaru0UIm-qyJ_EVzXFriQRJCLJ0ixJoJ6qGV0zG_gxkLU0wr-JKnVHJmaJpCYnLXZo2NhJ5JXLCk6pm1i9zyfW6g2l/s1600-h/04-09-2013%25252020-28-44%25255B4%25255D.png"><img title="04-09-2013 20-28-44" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 20-28-44" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOUu6XLoRleIduHcOknXbyM8D0GYxKKt2kcmR3ZOkDOqz0rT9JNNEUTb57pnEeH2di4a1jTyARzAyFB4Lzz3_0kCKCTO5TkfHAgEBYJ0pJRNqQoaM_tQ6K_3-752cjpR5mUQDL/?imgmax=800" width="439" height="179"></a> </li></ul></ul>
<p>And that’s pretty much it. There may be additional things to consider when setting up your Component; Rendering Parameters and so on – I’m going to skip that for this post, as it doesn’t lean towards the points I am making here. I know I say that a lot, but hey… this post is going to be more than long enough already.</p>
<h5>Implementing the Component</h5>
<p>Like a TV chef, I’m going to skip ahead quickly and show you the resulting Sublayout .ASCX. The layout I’ve created does nothing but set up one Placeholder; “content” and include the relevant CSS files. For those of you riding the MVC wave, I’m sure you can adapt.</p>
<h6>The .ASCX</h6><pre class="code"><span style="background: yellow; color: black"><%</span><span style="background: white; color: blue">@ </span><span style="background: white; color: maroon">Control </span><span style="background: white; color: red">Language</span><span style="background: white; color: blue">="C#" </span><span style="background: white; color: red">AutoEventWireup</span><span style="background: white; color: blue">="true" </span><span style="background: white; color: red">CodeBehind</span><span style="background: white; color: blue">="Top Banner.ascx.cs" </span><span style="background: white; color: red">Inherits</span><span style="background: white; color: blue">="Website.layouts.CorePoint.Top_Banner" </span><span style="background: yellow; color: black">%>
</span><span style="background: white; color: blue"><</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">id</span><span style="background: white; color: blue">="topBanner">
<</span><span style="background: white; color: maroon">sc</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">Image </span><span style="background: white; color: red">runat</span><span style="background: white; color: blue">="server" </span><span style="background: white; color: red">Field</span><span style="background: white; color: blue">="Top Banner Image" </span><span style="background: white; color: red">ID</span><span style="background: white; color: blue">="sciImage"/>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">id</span><span style="background: white; color: blue">="topBannerContent">
<</span><span style="background: white; color: maroon">asp</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">PlaceHolder </span><span style="background: white; color: red">runat</span><span style="background: white; color: blue">="server" </span><span style="background: white; color: red">ID</span><span style="background: white; color: blue">="phHeading">
<</span><span style="background: white; color: maroon">h4</span><span style="background: white; color: blue">><</span><span style="background: white; color: maroon">sc</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">Text </span><span style="background: white; color: red">runat</span><span style="background: white; color: blue">="server" </span><span style="background: white; color: red">Field</span><span style="background: white; color: blue">="Top Banner Heading" </span><span style="background: white; color: red">ID</span><span style="background: white; color: blue">="sctHeading"/></</span><span style="background: white; color: maroon">h4</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">asp</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">PlaceHolder</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">sc</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">Text </span><span style="background: white; color: red">runat</span><span style="background: white; color: blue">="server" </span><span style="background: white; color: red">Field</span><span style="background: white; color: blue">="Top Banner Body Text" </span><span style="background: white; color: red">ID</span><span style="background: white; color: blue">="sctBodyText"/>
</</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">asp</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">PlaceHolder </span><span style="background: white; color: red">runat</span><span style="background: white; color: blue">="server" </span><span style="background: white; color: red">ID</span><span style="background: white; color: blue">="phLink">
<</span><span style="background: white; color: maroon">sc</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">Link </span><span style="background: white; color: red">runat</span><span style="background: white; color: blue">="server" </span><span style="background: white; color: red">Field</span><span style="background: white; color: blue">="Top Banner Link" </span><span style="background: white; color: red">ID</span><span style="background: white; color: blue">="sclLink">
<</span><span style="background: white; color: maroon">sc</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">Text </span><span style="background: white; color: red">runat</span><span style="background: white; color: blue">="server" </span><span style="background: white; color: red">Field</span><span style="background: white; color: blue">="Top Banner Link Text" </span><span style="background: white; color: red">ID</span><span style="background: white; color: blue">="sclLinkText"/>
</</span><span style="background: white; color: maroon">sc</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">Link</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">asp</span><span style="background: white; color: blue">:</span><span style="background: white; color: maroon">PlaceHolder</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">></span></pre><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a>
<p>The codebehind is empty, at this point.</p>
<h6>Creating a Data Item based on my Component Template</h6>
<p>I then create an item, based on my newly created Component Template.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjj8VGnjhuxFDaIezWA_g4dPFjhkbG8xmJS8IUBqUFkDiRqGVNUmaivwO94NGEcNtruxJGXWbncXrIVhqO6tNxAFoHRAENJQxj0WdLbxmvFigWYu6QOE7HQIkycpeZ5tV7_FzRE/s1600-h/04-09-2013%25252021-27-47%25255B4%25255D.png"><img title="04-09-2013 21-27-47" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 21-27-47" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn7n2wd85xKKSKJn13t3s93Sjw4RAYR7hYIw-9WJFk3MIhq_BFa51Px9-s_QGVxKH_vNzopd9FAJKEWNDWbLqkgl2Cdkv8tfwxC7kin0Y3i8c-t2fJFkAfzk7Qy0FArmkl5Tlw/?imgmax=800" width="548" height="476"></a> </p>
<p>And to further stress my point that <em>Page Templates</em> have no place at this point, I will just proceed to modify the presentation details of the default /content/home item that ships with Sitecore.</p>
<h6>Setting up the Sitecore page</h6>
<p>I change the presentation details for the item, to look like this. As mentioned; the Layout only holds the basics – and a placeholder keyed “content”.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvS2k-LJjBLcpvv-9pesjHHqUQk0wSe9HknCDsh8uyHwi9_qavogM1hl6XiuJZmKTYoVRlvlUu_afTovfBw7MnLH6mOzXtg2_ntGPkjojDc1QS0IWj1FQi0n_TYqb6u_rL-4e4/s1600-h/04-09-2013%25252021-31-04%25255B4%25255D.png"><img title="04-09-2013 21-31-04" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 21-31-04" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTpVQS284-xIDttbtf-hLimUxvOdUmumVu2wHJbrac-DQM0M3-212MfbZn2i5Bio8fC5eyeKpC_fJnlWhDSPj46eRnJgNI7yvAelRC0NFKeHNAK1VJANWCzHtyXxKRghjWG0NV/?imgmax=800" width="564" height="417"></a> </p>
<p>And finally I configure my Top Banner component to use my newly created Content Item as a datasource.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFKuzj8M-ZWujX-9VS98x3F_JEJH3bS2cKMvs3YBZjpvGTm8M5NDh39apb5yvYQqYOjlAGDRdjUkP2QngNG5lFbE8DbPcoXcjMpleS7excajZ-F7U1oBud09-HWROD8kbGg_SO/s1600-h/04-09-2013%25252021-32-58%25255B4%25255D.png"><img title="04-09-2013 21-32-58" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 21-32-58" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt50PEEFC5FmoZdPa0iu26ioUKqZG1v5qMngBTjLvUxKIajOs_UpDG62cTaNCkUzpKGWojtA6D4C5UBEWMjylAErx7uMXVyagpt-HQxEfXdf7yuSlZbVK9ca3HN4UIIfuavHTt/?imgmax=800" width="569" height="282"></a> </p>
<p></p>
<p></p>
<p>After all this, I do a quick publish – and this is what I see. If I hadn’t read <a href="http://intothecore.cassidy.dk/2013/08/the-page-template-mistake.html">The Page Template Mistake</a> or <a href="http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog/Posts/2010/11/How-to-Apply-Data-Sources-to-Sitecore-ASPNET-Presentation-Components.aspx">John Wests post on how to apply data sources to components</a>, I might be surprised at this point.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5V2HEYThJtKf0t2lQxEbYIHSyfOG0efiDx-7IeF-iQ8iZpRErq92tarf7lknbgm9I6X43uJwJxyEpgZM90MjLqE6pzyMGZhZVfrpKJ2hMKFu8BhJYTShoNyU0HeUTES5Mqhyphenhyphenb/s1600-h/04-09-2013%25252021-38-01%25255B4%25255D.png"><img title="04-09-2013 21-38-01" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 21-38-01" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnMdhgsDB4YgaWLUOcD1DwfYoSMP0JEb-5JtCo_xGRrxYYkr6KGpMClarBeK88rVcW7rYjJF3eWqWQNjKmph4ikShjNPgBhn_9UPxjrMypgVr2om8zx8cLP6fwsuanVMF8HhV-/?imgmax=800" width="585" height="295"></a> </p>
<p>Nothing shows. Your first instinct might be to have a look at the page source. It looks like this.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyx6uQso2NC0UckVFG0FdNCi8XpW2IxaLvLkNiGJ5PEhhYm92EtyIv-XK5QDBdovnlMnpf_q9CDJKqnnO1UPavartroi5fTQVvgc62gzwSmpvhtIzMP-p9T_WKG6hx30AK0gin/s1600-h/04-09-2013%25252021-39-34%25255B3%25255D.png"><img title="04-09-2013 21-39-34" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 21-39-34" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWvyRP3muU2_x6jVL7BWhgAN0Kfgj37ObRsuvCFlJdRsKjlIvCp2csdnwsVYvGiehp9ji7a29bLltwHzyQmpoZIu9kZQwPdl51txBy9xCOtl9whc3XRLtLNryRbMqoY183PJ86/?imgmax=800" width="571" height="353"></a> </p>
<p>So essentially; the component gets rendered ok. There’s just nothing in it.</p>
<p>And this is the crust of the <em>Page Template Mistake</em>. Sitecore does nothing (for you) to respond to your configuration; setting the datasource to your content item. And I do believe this is the first reason many venture down that mistaken road and begin making the <em>Page Template Mistake</em> in the first place. There is no immediately obvious way to get to the datasource, and the standard Sitecore web controls don’t respond to it. So one retorts to hitting the Sitecore.Context.Item, and from here on out your fate is sealed.</p>
<p>Fortunately there’s a better way to approach this. Let’s introduce a bit of codebehind.</p>
<h5>Implementing the Component code (basic)</h5><pre class="code"><span style="background: white; color: blue">public partial class </span><span style="background: white; color: #2b91af">Top_Banner </span><span style="background: white; color: black">: </span><span style="background: white; color: #2b91af">UserControl
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">protected </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">_actionItem = </span><span style="background: white; color: blue">null</span><span style="background: white; color: black">;
</span><span style="background: white; color: blue">public </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">ActionItem
{
</span><span style="background: white; color: blue">get
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( _actionItem == </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
{
</span><span style="background: white; color: blue">var </span><span style="background: white; color: black">sl = Parent </span><span style="background: white; color: blue">as </span><span style="background: white; color: #2b91af">Sublayout</span><span style="background: white; color: black">;
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( sl != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
{
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( !</span><span style="background: white; color: blue">string</span><span style="background: white; color: black">.IsNullOrEmpty( sl.DataSource ) )
{
</span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">datasourceItem = Sitecore.</span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Database.GetItem( sl.DataSource, Sitecore.</span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Language );
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( datasourceItem != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">&& datasourceItem.Versions.GetVersions().Any() )
_actionItem = datasourceItem;
}
}
}
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( _actionItem == </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
_actionItem = Sitecore.</span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Item;
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">_actionItem;
}
}
</span><span style="background: white; color: blue">protected void </span><span style="background: white; color: black">Page_Load( </span><span style="background: white; color: blue">object </span><span style="background: white; color: black">sender, </span><span style="background: white; color: #2b91af">EventArgs </span><span style="background: white; color: black">e )
{
sctHeading.Item =
sctBodyText.Item =
sciImage.Item =
sclLink.Item =
sctBodyText.Item = ActionItem;
}
}
</span></pre><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a>
<p>And the result.</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhty4OnnOIdmrrQtznFbh4RoydcGgxejQs2QCpnyuWbUIOqdHJ9CQ8RmcLED3jLagEOykQse2aHxUrFck1NE0BGxUrOwloQAc6Dnn_uyqNMsCTgCHs8OkL08rLqfJlgjx3IGbDq/s1600-h/04-09-2013%25252023-42-10%25255B5%25255D.png"><img title="04-09-2013 23-42-10" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="04-09-2013 23-42-10" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_tOE7z3VonWo_QDP1_rPyS_k9_iZUru7804wIfCWa6QUnJQifz4b_YFPwrtfevEZXV7Ff-pUEAw-GZRaXzjFW_eE-RWHdAMcTBm1-IyKSjxrfnUVybu5whFO6l6n4t2nW-PAm/?imgmax=800" width="607" height="227"></a> </p>
<p>This is more or less <strong>Datasource 101</strong>. Implement like this, and you are more or less imitating XSL Renderings and how $sc_item is set up to work by default. </p>
<p>Look here’s the thing:</p>
<blockquote>
<p><strong>If you do this, and you do it consistently, your solution is already in the top N percent of well implemented Sitecore solutions. Good solutions. DMS enabled solutions. Almost by default.</strong></p></blockquote>
<p>You can work your way up from here, and I will give a few examples of that. But it starts here. It starts by deciding that what you want to do, is <a href="http://intothecore.cassidy.dk/2013/07/how-do-i-create-good-sitecore-solution.html">build a good Sitecore solution</a>. You could do nothing more than just this in your solution, and you’d have pretty much all DMS scenarios covered. I really am not kidding.</p>
<p><em>Just one gotcha; Sitecore 7 expands upon the Datasource concept. Since I am blogging in the context of personal experience and practices I myself have found to be successful – I cannot account for how the above code fits with Sitecore 7. I have no live sites under my belt at this stage that uses Sitecore 7. My initial guess would be; “it’s probably fine” however.</em></p>
<h5>Implementing the code (intermediate)</h5>
<p>The next natural step up from here would be to push the ActionItem code to a base class, and make all your Components inherit from here. Quite many of my readers of the previous posts in this series point this out – and until recently this has also been my own sole approach to this problem.</p>
<p>The base class could look like this.</p><pre class="code"><span style="background: white; color: blue">public class </span><span style="background: white; color: #2b91af">BaseSublayout </span><span style="background: white; color: black">: </span><span style="background: white; color: #2b91af">UserControl
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">#region </span><span style="background: white; color: black">:: Action Item ::
</span><span style="background: white; color: blue">protected </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">_actionItem = </span><span style="background: white; color: blue">null</span><span style="background: white; color: black">;
</span><span style="background: white; color: blue">public </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">ActionItem
{
</span><span style="background: white; color: blue">get
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">(_actionItem == </span><span style="background: white; color: blue">null</span><span style="background: white; color: black">)
{
</span><span style="background: white; color: blue">var </span><span style="background: white; color: black">sl = Parent </span><span style="background: white; color: blue">as </span><span style="background: white; color: #2b91af">Sublayout</span><span style="background: white; color: black">;
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">(sl != </span><span style="background: white; color: blue">null</span><span style="background: white; color: black">)
{
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">(!</span><span style="background: white; color: blue">string</span><span style="background: white; color: black">.IsNullOrEmpty(sl.DataSource))
{
</span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">datasourceItem = Sitecore.</span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Database.GetItem(sl.DataSource, Sitecore.</span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Language);
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">(datasourceItem != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">&& datasourceItem.Versions.GetVersions().Any())
_actionItem = datasourceItem;
}
}
}
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">(_actionItem == </span><span style="background: white; color: blue">null</span><span style="background: white; color: black">)
_actionItem = Sitecore.</span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Item;
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">_actionItem;
}
}
</span><span style="background: white; color: blue">#endregion
protected void </span><span style="background: white; color: black">ApplyActionItemRecursively()
{
</span><span style="background: white; color: blue">var </span><span style="background: white; color: black">ai = ActionItem;
</span><span style="background: white; color: blue">foreach </span><span style="background: white; color: black">( </span><span style="background: white; color: #2b91af">Control </span><span style="background: white; color: black">c </span><span style="background: white; color: blue">in </span><span style="background: white; color: black">Controls )
RecurseControls( c, ai );
}
</span><span style="background: white; color: blue">private void </span><span style="background: white; color: black">RecurseControls( </span><span style="background: white; color: #2b91af">Control </span><span style="background: white; color: black">parent, </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">actionItem )
{
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( parent.GetType().FullName.StartsWith( </span><span style="background: white; color: #a31515">"Sitecore.Web.UI.WebControls" </span><span style="background: white; color: black">) )
{
</span><span style="background: white; color: blue">var </span><span style="background: white; color: black">itemProp = parent.GetType().GetProperty( </span><span style="background: white; color: #a31515">"Item" </span><span style="background: white; color: black">);
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( itemProp != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
itemProp.SetValue( parent, actionItem );
}
</span><span style="background: white; color: blue">foreach</span><span style="background: white; color: black">( </span><span style="background: white; color: #2b91af">Control </span><span style="background: white; color: black">c </span><span style="background: white; color: blue">in </span><span style="background: white; color: black">parent.Controls )
RecurseControls( c, actionItem );
}
}
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>And with this, every one of your components would look like this in their basic form. This is where my claim “this adds no cost to your solution”. <strong>I’ll say it again; implementing Components this way carries no significant overhead</strong>. You set up this base class once, and that’s it.</p><pre class="code"><span style="background: white; color: blue">public partial class </span><span style="background: white; color: #2b91af">Top_Banner </span><span style="background: white; color: black">: </span><span style="background: white; color: #2b91af">BaseSublayout
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">protected void </span><span style="background: white; color: black">Page_Load( </span><span style="background: white; color: blue">object </span><span style="background: white; color: black">sender, </span><span style="background: white; color: #2b91af">EventArgs </span><span style="background: white; color: black">e )
{
ApplyActionItemRecursively();
}
}
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>All I have left to do, is a slight bit of housekeeping. This is another convention I apply and if you stick to it, I am fairly confident your content editor users are going to love it.</p><pre class="code"><span style="background: white; color: blue">public partial class </span><span style="background: white; color: #2b91af">Top_Banner </span><span style="background: white; color: black">: </span><span style="background: white; color: #2b91af">BaseSublayout
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">protected void </span><span style="background: white; color: black">Page_Load( </span><span style="background: white; color: blue">object </span><span style="background: white; color: black">sender, </span><span style="background: white; color: #2b91af">EventArgs </span><span style="background: white; color: black">e )
{
ApplyActionItemRecursively();
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( </span><span style="background: white; color: blue">string</span><span style="background: white; color: black">.IsNullOrWhiteSpace( ActionItem[ </span><span style="background: white; color: #a31515">"Top Banner Heading" </span><span style="background: white; color: black">] ) )
phHeading.Visible = </span><span style="background: white; color: blue">false</span><span style="background: white; color: black">;
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( </span><span style="background: white; color: blue">string</span><span style="background: white; color: black">.IsNullOrWhiteSpace( ActionItem[</span><span style="background: white; color: #a31515">"Top Banner Link Text"</span><span style="background: white; color: black">] ) )
phLink.Visible = </span><span style="background: white; color: blue">false</span><span style="background: white; color: black">;
}
}
</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>And that’s it, essentially. The Component is done. It’s well behaved, it can be used in M/V tests – call it “DMS Enabled” if you must. And if all your components are implemented in this manner, your life when creating <em>Page Templates</em> later on will be a heck of a lot easier. Painless is more like it. And it never cost you a dime.</p>
<p>For that, however, you are going to have to wait for the next post in this series.</p>
<p>You can stop reading now :-)</p>
<p>But for those persistent enough to make it this far in an already excessively long post; I’ll demonstrate where I am currently at myself in my pursuits of a mythological “best practice” in this area. I will be brief and post mostly code with minimal commentary. If you’re at this level of Sitecore implementation experience, I’m sure it will make sense.</p>
<h5>Implementing the code (advanced)</h5>
<p>The problem with the above approach is of course, as many commenters have pointed out. In real life, not all components fit nicely into this pattern. Sitecore themselves sort of indicate this as well; every default XSLT rendering has a $home variable defined (albeit commented out). Sometimes applying “Datasource or Context Item” just isn’t good enough.</p>
<p>Think Headers and Footer Components for instance. While you likely could be implementing a Datasourced Header component on one of your (deep) base templates, for many purposes this just isn’t practical.</p>
<p><a href="http://intothecore.cassidy.dk/2013/08/the-page-template-mistake.html?showComment=1378316880771#c6668001859677178777">Mark Ursino rightly mentions this problem in a comment</a>; </p>
<blockquote>
<p>“Global information (e.g. header and footer) cannot really be componentized too well unless you use standard values on a very low level "base page" template to assign the same header and footer data source items to all pages. I typically instead just define a predefined structure in a global area to support the header and footer.”. </p></blockquote>
<p>Mark, I’m with you here, but I still believe that any component that “breaks out” of the imaginary “bounding box” that is its Component Template is creating an <strong>anti pattern</strong> in the solution – not unlike how <a href="http://en.wikipedia.org/wiki/Global_variable">Global Variables</a> do it for traditional structured programming.</p>
<p>And there’s likely many similar scenarios.</p>
<p>Fortunately this can be resolved easily as well – without tweaking much in the code I just presented. AND – and this is important – without starting to make up a “meta CMS in the CMS” by adding global configuration structures and similar. I’ve taken this approach myself, and I always found them to be inhibiting me sooner or later in the lifetime of the solution.</p>
<p>Here’s what I suggest. </p>
<p>Have your components adhere to relevant strategies for resolving the ActionItem. Mostly – what I just lined out above will be fine. For some components, it would be more appropriate to EITHER respond to the Datasource (if set) or retort to $home. For others again; perhaps responding to Datassource (WHATEVER else you do; always <em>always</em> <strong><em>always</em></strong> respect the Datasource if one has been set. Always. Please. Having a “global header” that cannot be datasourced to make a quick micro-site is just a right pain) and retorting to crawling “up the tree” until you find an item that inherits from your Component Template.</p>
<p>It looks like this. I’ve abbreviated slightly. It’s going to be a long paste, so I’ll quit writing now and just let the code do the rest of the talking (mostly).</p>
<p>Until next time :-)</p><pre class="code"><span style="background: white; color: blue">namespace </span><span style="background: white; color: black">Website.layouts.CorePoint
{
</span><span style="background: white; color: blue">public abstract class </span><span style="background: white; color: #2b91af">ActionItemStrategy
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">public abstract </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">Resolve( </span><span style="background: white; color: #2b91af">Control </span><span style="background: white; color: black">c );
}
</span><span style="background: white; color: gray">/// <summary>
/// </span><span style="background: white; color: green">Classic Datasource handling
</span><span style="background: white; color: gray">/// </summary>
</span><span style="background: white; color: blue">public class </span><span style="background: white; color: #2b91af">DatasourceOrContextItemStrategy </span><span style="background: white; color: black">: </span><span style="background: white; color: #2b91af">ActionItemStrategy
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">private </span><span style="background: white; color: #2b91af">Language </span><span style="background: white; color: black">_fallbackLanguage;
</span></pre><pre class="code"><span style="background: white; color: black"> </span><span style="background: white; color: blue">public </span><span style="background: white; color: black">DatasourceOrContextItemStrategy( </span><span style="background: white; color: #2b91af">Language </span><span style="background: white; color: black">fallbackLanguage = </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
{
_fallbackLanguage = fallbackLanguage;
}
</span></pre><a href="http://11011.net/software/vspaste"></a><pre class="code"><span style="background: white; color: black"></span><span style="background: white; color: black">
</span><span style="background: white; color: blue">public override </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">Resolve( </span><span style="background: white; color: #2b91af">Control </span><span style="background: white; color: black">c )
{
</span><span style="background: white; color: blue">var </span><span style="background: white; color: black">sl = c.Parent </span><span style="background: white; color: blue">as </span><span style="background: white; color: #2b91af">Sublayout</span><span style="background: white; color: black">;
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( sl != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
{
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( !</span><span style="background: white; color: blue">string</span><span style="background: white; color: black">.IsNullOrEmpty( sl.DataSource ) )
{
</span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">datasourceItem = </span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Database.GetItem( sl.DataSource, </span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Language );
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( datasourceItem != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">&& datasourceItem.Versions.GetVersions().Any() )
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">datasourceItem;
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( _fallbackLanguage != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
{
datasourceItem = </span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Database.GetItem( sl.DataSource, _fallbackLanguage );
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( datasourceItem != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">&& datasourceItem.Versions.GetVersions().Any() )
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">datasourceItem;
}
}
}
</span><span style="background: white; color: blue">return null</span><span style="background: white; color: black">;
}
}
</span><span style="background: white; color: gray">/// <summary>
/// </span><span style="background: white; color: green">Resolves the ActionItem by datasource and falls back to Site Root.
</span><span style="background: white; color: gray">/// </summary>
</span><span style="background: white; color: blue">public class </span><span style="background: white; color: #2b91af">DatasourceOrHomeItemStrategy </span><span style="background: white; color: black">: </span><span style="background: white; color: #2b91af">ActionItemStrategy
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">public override </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">Resolve( </span><span style="background: white; color: #2b91af">Control </span><span style="background: white; color: black">c )
{
</span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">datasourceItem = </span><span style="background: white; color: blue">new </span><span style="background: white; color: #2b91af">DatasourceOrContextItemStrategy</span><span style="background: white; color: black">().Resolve( c );
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( datasourceItem != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">datasourceItem;
</span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">home = </span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Database.GetItem( </span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Site.StartPath, </span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Language );
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( home.Versions.GetVersions().Any() )
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">home;
</span><span style="background: white; color: blue">return null</span><span style="background: white; color: black">;
}
}
</span><span style="background: white; color: blue">public class </span><span style="background: white; color: #2b91af">BaseSublayout </span><span style="background: white; color: black">: </span><span style="background: white; color: #2b91af">UserControl
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">protected </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">_actionItem = </span><span style="background: white; color: blue">null</span><span style="background: white; color: black">;
</span><span style="background: white; color: blue">public </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">ActionItem
{
</span><span style="background: white; color: blue">get
</span><span style="background: white; color: black">{
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( _actionItem == </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
{
_actionItem = GetActionItemStrategy().Resolve( </span><span style="background: white; color: blue">this </span><span style="background: white; color: black">);
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( _actionItem == </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
_actionItem = Sitecore.</span><span style="background: white; color: #2b91af">Context</span><span style="background: white; color: black">.Item;
}
</span><span style="background: white; color: blue">return </span><span style="background: white; color: black">_actionItem;
}
}
</span><span style="background: white; color: blue">protected virtual </span><span style="background: white; color: #2b91af">ActionItemStrategy </span><span style="background: white; color: black">GetActionItemStrategy()
{
</span><span style="background: white; color: blue">return new </span><span style="background: white; color: #2b91af">DatasourceOrContextItemStrategy</span><span style="background: white; color: black">();
}
</span><span style="background: white; color: blue">protected void </span><span style="background: white; color: black">ApplyActionItemRecursively()
{
</span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">ai = ActionItem;
</span><span style="background: white; color: blue">foreach </span><span style="background: white; color: black">( </span><span style="background: white; color: #2b91af">Control </span><span style="background: white; color: black">c </span><span style="background: white; color: blue">in </span><span style="background: white; color: black">Controls )
RecurseControls( c, ai );
}
</span><span style="background: white; color: blue">private void </span><span style="background: white; color: black">RecurseControls( </span><span style="background: white; color: #2b91af">Control </span><span style="background: white; color: black">parent, </span><span style="background: white; color: #2b91af">Item </span><span style="background: white; color: black">actionItem )
{
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( parent.GetType().FullName.StartsWith( </span><span style="background: white; color: #a31515">"Sitecore.Web.UI.WebControls" </span><span style="background: white; color: black">) )
{
</span><span style="background: white; color: #2b91af">PropertyInfo </span><span style="background: white; color: black">itemProp = parent.GetType().GetProperty( </span><span style="background: white; color: #a31515">"Item" </span><span style="background: white; color: black">);
</span><span style="background: white; color: blue">if </span><span style="background: white; color: black">( itemProp != </span><span style="background: white; color: blue">null </span><span style="background: white; color: black">)
itemProp.SetValue( parent, actionItem );
}
</span><span style="background: white; color: blue">foreach </span><span style="background: white; color: black">( </span><span style="background: white; color: #2b91af">Control </span><span style="background: white; color: black">c </span><span style="background: white; color: blue">in </span><span style="background: white; color: black">parent.Controls )
RecurseControls( c, actionItem );
}
}
}</span></pre>
<p><a href="http://11011.net/software/vspaste"></a>And with this in place, the individual Component implementations could “do nothing” – in which case they would just apply default behaviour. But for some cases, overriding this behaviour is desirable – so we configure these with simple overrides. A few examples.</p>
<h6>Site Header Component</h6><pre class="code"><span style="background: white; color: blue">protected override </span><span style="background: white; color: #2b91af">ActionItemStrategy </span><span style="background: white; color: black">GetActionItemStrategy()
{
</span><span style="background: white; color: blue">return new </span><span style="background: white; color: #2b91af">DatasourceOrHomeItemStrategy</span><span style="background: white; color: black">();
}
</span></pre><a href="http://11011.net/software/vspaste"></a>
<h6>Implementing Language Fallback</h6>
<p>Make this your default sublayout, overriding the default I listed above.</p><pre class="code"><span style="background: white; color: blue">protected override </span><span style="background: white; color: #2b91af">ActionItemStrategy </span><span style="background: white; color: black">GetActionItemStrategy()
{
</span><span style="background: white; color: blue">return new </span><span style="background: white; color: #2b91af">DatasourceOrContextItemStrategy</span><span style="background: white; color: black">( Sitecore.Globalization.</span><span style="background: white; color: #2b91af">Language</span><span style="background: white; color: black">.Parse( </span><span style="background: white; color: #a31515">"en" </span><span style="background: white; color: black">) );
}
</span></pre>
<p>Make up your own as you go along. Not too many though – or the point of this whole exercise gets lots completely. I can attest from experience however; just the two first strategies solved the very large majority of my component worries. And I don’t implement any meta-structures any longer to support neither menus, nor headers or footers, or pretty much anything else for that matter.</p>
<p>And the flexibility this approach gives me in setting up any and all <em>Page Templates</em> that my content editors require; is near phenomenal. More on that next time.</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com6tag:blogger.com,1999:blog-20883010.post-30834748924373580012013-08-21T23:32:00.001+02:002013-08-21T23:32:36.905+02:00The page template mistake<h4></h4> <h4>How to recognise it and how to avoid it</h4> <blockquote> <p><em>This blog post is part 2 in a series on “</em><a title="All posts tagged "Creating good Sitecore solutions"" href="http://intothecore.cassidy.dk/search/label/Creating%20good%20Sitecore%20solutions"><em>Creating good Sitecore solutions</em></a><em>”. You will find part 1; “</em><a title="How do I create a good Sitecore solution?" href="http://intothecore.cassidy.dk/2013/07/how-do-i-create-good-sitecore-solution.html"><em>How do I create a good Sitecore solution?</em></a><em>” here.</em></p></blockquote> <h5>Getting more, for less. Or even for free.</h5> <p>Having decided to create a good Sitecore solution, or even just “a Sitecore solution”, there is one thing you need to get right that adds more “good Sitecore value” to your solution above all other things you could be doing.</p> <p>And the best thing about it is; it will (should) <em>not add any costs to your implementation</em>. If the practices lined out here are followed from the start, my own personal experience has shown me that your implementation time is not increased. In fact I would argue, following these guidelines <em>will bring your overall implementation time down </em>more than anything. And from whichever side of the table you are on – buying or selling a Sitecore solution – time will be money.</p> <p>I will explain this in more detail, as I go along.</p> <h5>The problem with (most) Sitecore implementations.</h5> <p>In my first post in this series, I hinted that a very large majority of Sitecore solutions I come across in may day to day work were, to some extent, not entirely “fit for duty”. While some of the shortcomings can be put down to just this – the solutions were never truly designed to be good Sitecore solutions to begin with; the decision to to so was just plain never made. Most of them have a very fundamental architectural shortcoming in common. And this particular shortcoming – more than anything else – will nothing short of completely rule out pretty much any DMS scenario you can think of. Right then and there – one glitch, if you will, in the way these solutions were architected – and you can forget about the (in my view) most important set of features in Sitecore or possibly any CMS platform on the market today.</p> <blockquote> <p><em>Before I go on, and step up high and mighty on my soapbox – let me be the first to admit this. Up until just a few years ago, I was happily designing and implementing Sitecore solutions following these same – faulty, as this post should demonstrate – practices and guidelines. </em></p></blockquote> <p>It wasn’t until around the beginning of 2012 and having just gone through a greenfield Sitecore 6.5 implementation that it dawned on me; the way I was perceiving and approaching basic Sitecore information architecture was just – well plain wrong. I recall asking on LinkedIn “<a title="Have you built a site yet, truly "true" to the changes in the 6.5 platform?" href="http://www.linkedin.com/groupItem?view=&gid=1841774&type=member&item=88464656&qid=4264fbb8-4d0b-4d53-aebf-b3f9fb0b6c7a&trk=group_most_popular-0-b-ttl&goback=%2Egmp_1841774">Have you built a site yet, truly "true" to the changes in the 6.5 platform?</a>” (Sitecore MVP group, for those who have access) – asking if anyone else had revisited the way they implemented Sitecore solutions following the release of Sitecore 6.5. It created a bit of debate, but nothing too ground shaking.</p> <p>I was under the mistaken impression at the time, that it was Sitecore 6.5 and DMS that brought on these changes. Having mulled over this ever since, I now know that I was wrong. I know now that the way one is supposed to be implementing and architecting Sitecore for the solutions to end up “DMS ready” and being well on the way to becoming a “good Sitecore solution” dates back at least as far as Sitecore version 5. It might even be sooner, but I have little to no experience with Sitecore versions pre-dating version 5.</p> <p>If you are still with me here, I think it’s about time I unveil what I believe to be at the core of the problem, in the way I was designing Sitecore solutions – and the problem in the vast majority of <em>less than ideal</em> Sitecore solutions I come across today.</p> <h5>The problem is: “Page Templates”</h5> <p>Yes indeed. Bear with me, and allow me to explain.</p> <p>Having worked my way through I don’t know how many Sitecore releases dating as “far” back as Sitecore 5.1.1.12 (the earliest I have on record) and all the way up through to version 7 – it wasn’t until Sitecore 6.5 that Sitecore brought on a truly serious alternative offering to the ever popular “Sitecore Content Editor”. In fact the Sitecore Content Editor is so widely used that most people probably don’t even realise, this is just one content editor interface in Sitecore. To most I would bet; this editor IS “Sitecore”.</p> <p>A quick re-cap. Sitecore 5 and version 6s prior to 6.5 did offer alternative interfaces. As far as I can tell however, nobody really used them. I practically never came across any implementations were these played any active role. You had to implement for these alternatives specifically (sc:Dot anyone?) and in my personal view they just plain didn’t work very well. In essence you had to do a fair amount of <em>extra</em> (key word here) development effort to support anything but the Sitecore Content Editor. And as I’ve already argued – time equals money – so it doesn’t get done. Only in the rarest of circumstances.</p> <p>Along came 6.4 and in particular 6.5 – and changed all that. For the first time – at least for me – could one switch to this “new” Sitecore user interface (which is, in fact, not new at all) and see the site one was building become truly <em>interactive</em>. Without doing much if anything at all, to support it. Alright so there is a few things to consider, but I consider these insignificant in the grand scheme of things.</p> <p>All of a sudden, teaching clients to do their work in the Page Editor became a viable option. And in many (most) cases a very strong alternative to the Sitecore Content Editor. I mean the Sitecore Content Editor is fine, if you’re working on <em>Page Templates</em> – marketing people will <em>get</em> that, and manage fine to go in and alter some headlines, swap out a few pictures and even add a few new pages (based on <em>Page Templates</em>) to the site. Not much more than that however, not in my experience.</p> <p>The Page Editor, however, allows so much more than that. Following just the standard Sitecore implementation guidelines – the Page Editor allows the content editor to work with the very fabric of the page. Swapping out components, personalising components, M/V testing them with alternating content. And while all of these possibilities are also present in the Sitecore Content Editor, the Page Editor beats it by miles in terms of overall user experience for the average non-developer user. </p> <p>And here’s the thing. Swapping out components, M/V testing components on a page, personalising components on a page. <strong>None of this – none of it – fits into the mindset of a “<em>Page Template</em>”.</strong> Your page has become a dynamic entity, something to be shaped and molded into nothing short of the “Perfect personalised user experience for your site visitors” – this is the way of the Digital Marketing Future. <a title="We’re going to the Moon" href="http://www.sitecore.net/Community/Business-Blogs/Industry-Insight/Posts/2013/05/Sitecore-7-Going-to-the-Moon.aspx">We’re going to the Moon</a> and all that ;-)</p> <p>The fact of the matter is; if you perceive each page on your site as something that fits the <em>static</em> nature of a Sitecore template – keep in mind, these can only be created by developers, and once a template has been set for an item it stays with the item forever. Or at least until you forcibly decide to change it. Also keep in mind, a template for “Page A” will also be the same template for any other pages of the same type as “Page A”. You can’t have the template holding X fields here, and Y fields there. Sitecore just isn’t designed for that (yet, I hope).</p> <p>To overcome this, Sitecore uses Datasources. I think this was recently dubbed “Related Content” in a recent Sitecore release, but I’m going hardline and stick with the original term. “Feeding” your component with a Datasource is the way you instruct it to “go get your content from this place over here, don’t look at the page you happen to be sitting on”. None of this means anything to the Page Editor – it just shows (and allows edits of) the content from where ever it was sourced – whereas you need to jump around between items in the Sitecore Content Editor to achieve the same (reference: <a title="Content Authors Reference and Cookbook for Sitecore 6.6" href="http://sdn.sitecore.net/upload/sitecore6/66/content_author's_cookbook_sc66-a4.pdf">section 4.2.1 of the Content Authors Reference and Cookbook for Sitecore 6.6</a>).</p> <p><strong>For both personalisation and M/V testing to work – your components must be able to be datasourced. No if’s, no buts'.</strong></p> <p>And this is it. This just doesn’t happen (much) in most solutions I visit. I, as most of the Sitecore developer community, implement the general term <em>component</em> as an ASP.NET User Control or the up-and-coming MVC alternatives. Sitecore also supports other types of components, most notably the “XSL Rendering” type of component. For a variety of reasons, this component type is being ruled out everywhere – performance reasons, lack of familiarity with the XSLT syntax among the developer base – the reasons are many. Rightfully so, I might add. It’s a relic from a time where Sitecore seemed to be designed on the idea, that the Sitecore data structure should be represented in abstract as one big XML Document – and transformed for multiple purposes by the up and coming XSLT technology. While Sitecore still supports this notion, the product is moving more and more away from this line of thinking.</p> <p>So. When writing components in ones language of choice – I guess c# is the dominant choice these days, and working with a user base that exclusively accesses Sitecore through one common interface – the Sitecore Content Editor – you set yourself up for <em>The Page Template Mistake</em>.</p> <h5>The Page Template Mistake</h5> <p>While going through all the motions of setting up Page Insert options for your users, you would probably also be considering what fields to present to your users on each of these “pages” they can insert. If you’re doing this well, you will break down your Data Templates into smaller bits, so as to make them easily available for re-use across different <em>Page Templates</em>. So you’ll isolate your “Page Header” and “Sub Heading” fields into one Data Template – and then have some or most of your <em>Page Templates</em> inherit from this one, as well as any other Data Templates that the particular Page Type will support.</p> <p>As the site evolves you shape and mold your Data Template structure, to make the best use possible of the common fields across pages.</p> <p>You’ll begin to implement components – like a common “Left Menu” component for example – and have these traverse through your <em>Page Items</em> based on <em>Page Templates</em> – outputting a nice and nested logical representation of your site. The site visitor clicks elements on the menu, and navigates to a new page. The story repeats itself.</p> <p>Eventually you’re done. The site launches, editors make the last minute changes to headers, round off corners of the images and changes a few icons to the <a title="Fight Club quote: Cornflower Blue" href="http://www.subzin.com/quotes/Fight+Club/Can+I+get+the+icon+in+cornflower+blue">Cornflower Blue</a>. Everyone is happy.</p> <p>Everyone is happy, that is, until 3 days into the launch and your pesky content editors decides to try out some of this “fancy M/V testing that we’ve heard so much about”.</p> <p>You’ve just made <em>The Page Template Mistake</em>. Don’t worry though matey, so have I and so have we all.</p> <p>You will find, that once the content editors start moving stuff around, adding YouTube Video Player Component onto the “Contact Us” page – you’re in trouble. Sure you can go; “oh we’re sorry. We didn’t make it so that this component can work on that page” but really all you’re doing at this stage is making up excuses and adding a healthy dose of CYA. I know I did.</p> <p>If all your considerations when designing your page and template structure was focused around the Sitecore Content Editor (and it is an EASY trap to fall into – you DO do all of the designing and “architecting” in this very editor) – chances are slim to none that you have all the dynamics in place to allow the kind of flexibility the Page Editor truly inspires.</p> <p>M/V testing? “Uhm sure…” (muttered on the phone while desperately scrambling to add a few template Insert Options to the solution and hoping it will work).</p> <p>Ever been there?</p> <p>Fortunately there’s an easy way to avoid it. Not an easy fix, mind you, refactoring your solution AFTER the fact is going to be a fairly extensive job. Avoiding this problem however, adds no cost to your solution implementation AND puts you on the road to delivering a “good Sitecore solution”.</p> <h5>The Data Template solution</h5> <p>If you look closely in the Sitecore documentation, you will find the term Data Template everywhere when speaking of content and data structures. In fact I don’t recall anywhere within official Sitecore documentation seeing the term “<em>Page Template</em>” mentioned. I could be wrong though, there is a vast amount of cookbooks dealing with these topics. Here’s a tip:</p> <p>Don’t think Data Template, think “Component Template”.</p> <p>Because that’s what you should be doing. </p> <p>When going through the site designs, part of that process is identifying “Components” that has to be built for the site to be completed. You do this already – regardless of whether you’re thinking in terms of <em>Page Templates</em> or not. Each of these components will have a certain number of Sitecore fields supporting them. It will be the obvious ones; “Image Heading”, “Image”, “Subtitle”, “Citation” and so on – we all know how these look and work. There will (should) also be the more subtle ones like “Banner Rotation Delay”, “Allow Fullscreen” and so on. You won’t necessarily recognise all of these at the get-go, but don’t worry. These can be added as you go along with the implementation. Make sure you do, though. Things like “Banner Rotation Delay” are perfect candidates for M/V testing. Try it, you’ll probably be as surprised as I was about the difference it can make.</p> <p>This is what should be defining your Component Template. A Component Template for a Component – this makes perfect sense. Name them by convention; a “YouTube Video Template” matching the “YouTube Video” component. This makes sense. To you and to your content editors alike. While configuring each component in Sitecore, make sure you configure the components Datasource Template to point to your corresponding Component Template – and you’re already more than half way on your pursuit of the “good Sitecore solution”.</p> <p>Forget about <em>Page Templates</em> at this stage. You can do them at the end. At the very end of the project, I’m serious. Keep working your way through components and if you’re fortunate enough, get your team of developers started on implementing them right away. If not, start doing them yourself. Just make sure that the implementation of these components follow one of the most basic of rules:</p> <p>Make sure the component respects it’s data source.</p> <p>Mine didn’t. Not until that epiphany I had with Sitecore 6.5. As so many others, I implement my components as a Sitecore “Sublayout” (essentially an ASP.NET User Control) – and I would frequently sport code such as the following (not literally – the example code is ghastly, but am making a different point):</p> <p><font size="2" face="Courier New">if ( Sitecore.Context.Item[“show in menu”] == “1” )<br>{<br> litMenu.Text += “<li>” + Sitecore.Context.Item[“menu title”] + “</li><br>}</font></p> <p>Right up there on The Page Template Mistake, page 1, subheading “Sitecore.Context.Item”. Don’t use it. Well don’t use it <em>in general</em>, there are obviously cases where it is perfectly acceptable to go visit Sitecore.Context.Item.</p> <p>What you need is something to the effect of (pseudocode, this post is already long enough without me diving in to a long technical explanation. I will do a follow-up post with concrete code examples to follow this one, I promise):</p> <p><font face="Courier New">IF this_component HAS DEFINED Datasource<br> SET $ActionItem TO Datasource<br>ELSE<br> SET $ActionItem TO Sitecore.Context.Item</font></p> <p>And then have all the component implementation respect draw its data exclusively from $ActionItem.</p> <p>That’s all there is to it. Really. And since you’re ignoring <em>Page Templates</em> for now, testing and verification of the implemented components becomes very easy. Developers will be forced to add their components to “blank canvasses” as they go along and Datasource them to their items based on the respective Component Templates.</p> <p><strong>This is not new. This is not something “clever” I am making up, or a “clever idea I just had”. No my faithful reader – this principle sits at the very foundation of Sitecore presentation architecture.</strong> This behaviour has been expected from the very beginning (of Sitecore 5, maybe sooner) from every Component. </p> <p>This is $sc_item versus $sc_contentitem in your (no longer used) XSLT renderings; one of the first types of components you could implement in Sitecore. Also known as a “Rendering” – a term still being broadly used today to cover most anything used to output content. I prefer the Page Editor term Component.</p> <p>$sc_item would behave exactly like the pseudocode above. </p> <blockquote> <p>A rendering can retrieve data from its data source item. The $sc_item variable represents the data source item for an XSL rendering. If the developer does not specify a data source item for a rendering, the default data source item is the context item, and $sc_item and $sc_currentitem are the same item. – <a title="Presentation Component XSL Reference" href="http://sdn.sitecore.net/upload/sitecore6/64/presentation_component_xsl_reference_sc62-64-a4.pdf">Presentation Component XSL Reference section 3.2.1</a>.</p></blockquote> <p>And while I have found little to no documentation in Sitecore actually specifically instructing you to be designing Component Templates in the manner I describe, I can tell you this much: If you want to support all the DMS goodness that everyone is talking about these days – this is the first step. </p> <p>Forget about Page Templates; think Component Templates.</p> <p>Do this from the start, and your solution will come into existence, ready and waiting to be M/V tested, personalised and whatnot. At no extra cost. Creating your information architecture in this manner from the ground up will add no extra time to your project. In fact I have found that it subtracts slightly from the implementation time.</p> <p>And on that note, I think it’s time I step down from my soapbox. I will follow-up this post with a more technically to-the-point post explaining and demonstrating how an information architecture can come into place, following these principles. Also a few pointers to how your components can easily adapt these principles and finally; “what DO we do with Page Templates then?”.</p> <p>Until then.</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com8tag:blogger.com,1999:blog-20883010.post-12779581998393687512013-07-30T22:31:00.001+02:002013-08-21T23:34:22.261+02:00How do I create a good Sitecore solution?<p><em>This blog post is part 1 in a series on “</em><a title="All posts tagged "Creating good Sitecore solutions"" href="http://intothecore.cassidy.dk/search/label/Creating%20good%20Sitecore%20solutions"><em>Creating good Sitecore solutions</em></a><em>”. You will find part 2; “<a title="The page template mistake" href="http://intothecore.cassidy.dk/2013/08/the-page-template-mistake.html">The page template mistake</a>” </em><em>here.</em></p> <p>Having now worked as a Sitecore consultant for well over 8 years – sometimes in regular employment at one of the many <a href="http://www.sitecore.net/Partners/Find-Partner.aspx?country={1520F577-D9DC-493C-B120-B4F4E3D1A181}">Sitecore Partners in Denmark and the UK</a>, but more often as a freelance consultant/contractor for the very same clients – I have been exposed to pretty much all aspects of what it means to create a Sitecore solution.</p> <p>Sometimes I’m brought in to help a new and upstart Sitecore partner get some of their first Sitecore clients. They usually need help in scoping, defining and estimating the proposed project. Sometimes they keep me on to help in the actual implementation of the site as well – more often I am kept on to help train their existing developer resources in the practices and uses of Sitecore.</p> <p>Other times – and these days, this is more often the case – I am hired as a Sitecore Architect for a project. In many ways the tasks I perform are the same as what I just mentioned, but the title Architect tends to imply doing all the above, alongside ensuring that whatever implementation eventually ends up getting built; will also fall in line with the almost <a href="http://sdn.sitecore.net/Reference/Sitecore%206/Recommended%20Practices.aspx">mythological Best Practices for Sitecore implementations</a>.</p> <p>I say mythological with some irony, of course. While <a href="http://intothecore.cassidy.dk/2008/12/showing-sitecore-how-to-improve.html">a lack of documentation used to be a problem</a>, we now have nothing short of 1000s of pages of documentation and “cookbooks” available to us. These cookbooks will describe in detail, all the intricacies of implementing your Renderings, Rules, Templates. How to set up security, how to set up cloud solutions – pretty much how to do everything you’d want to with your Sitecore solution.</p> <p>And this is it, really. If one was ever to define <a href="http://sdn.sitecore.net/Reference/Sitecore%206.aspx">Sitecore Best Practices</a>, this complete set of cookbooks would be it. <strong>NOT</strong> this – <a href="http://sdn.sitecore.net/Reference/Sitecore%206/Recommended%20Practices.aspx">The Sitecore Recommended Practices Cookbook</a>. While I would certainly recommend that the practices described in this cookbook be followed, it <strong>in no way can be said to hold a complete set of “Best Practices” for a Sitecore solution</strong>. There’s so much more to it than that.</p> <p>There simply is no single “manual” that will tell you everything you need to do. Everything you need to be aware of, everything you need to consider. There is no substitute for experience, as the proverb goes. This certainly holds true here.</p> <p>And that actually brings me closer to the point I was wanting to make, and has brought me back to the keyboard after such a long absence from blogging. See the thing is – when I first started blogging, it was mostly about technical details. “The little things”. Information on Sitecore was somewhat scarce, and sharing whatever information we learned was the name of the game. With all the information now available, not to mention the probably 100s of bloggers who has taken to the blogosphere – I find myself in need of a new game. Experience. I will start using this blog to write and share some of the information you will not find in cookbooks, other peoples blogs or even <a href="http://www.amazon.co.uk/Professional-Sitecore-Development-ebook/dp/B007ZQ5UV6/ref=sr_1_1?ie=UTF8&qid=1375212906&sr=8-1&keywords=john+west+sitecore">John West’s book</a>.</p> <p>I’ll kick this off by beginning to answer the very question I pose in the title of this post. You will find it is going to take quite many posts to follow, to arrive at any sort of answer – but we have to start somewhere.</p> <p>To create a good Sitecore solution; <strong>you must first decide to create a Sitecore solution</strong>.</p> <p>“What? That it!?”</p> <p>No. That’s not all of it, but this is most definitely where you need to start.</p> <p>See the thing is; as any project I’ve been part of goes through the same motions – the same pattern. Doesn’t really matter if the process in place is Agile or Waterfall. If you have a Scrum Master or a Project Manager (or both).</p> <p>In the beginning (or as you go along, if you are truly Agile. I have yet to see my first truly Agile Sitecore project implementation); everyone is on about features, design and (usually most importantly) the cost. If you’re in an agency, your agency has to win the bid. If you’re building in-house, the project will still have a cost and will need an approved budget.</p> <p>Then comes the construction. Developers, designers and whatnot get to work. Here the focus becomes jQuery libraries, lightboxes, valid HTML, performance and of course the actual Sitecore implementation. The perceived necessary stuff from the cookbooks mind you, whatever it takes to make it work.</p> <p>And if all goes to plan, eventually the “site” (which is usually what a Sitecore project will be – the delivery of a “site” for Company A, or a re-make of a “site” for Company A – and herein lies the problem) will get handed off, go live, start receiving visitors and everyone shakes hands.</p> <p>I know, I know. I am both simplifying the whole process and adding a bit of irony on top. Bear with me, this post is already turning out to be much longer than it seemed to be in my mind.</p> <p>My point. The project is about a “site”. Or new features on a “site”. <strong>Very very rarely – if ever – does it get discussed, how the end user (the Sitecore end user, not the “site” visitor) will be interacting with the Sitecore solution</strong>. One simple – and when you look at it; astonishing – fact, and no-one usually thinks to ask it.</p> <p>What happens is; the “site” gets built, gets handed off, gets approved – all from looking at the “public face” (if you will) of the “site”. If it ticks all the boxes, “we’re done”.</p> <p>Sure enough, depending on the actual level of experience (see above) of the team that actually did the scoping and ultimately the Sitecore implementation of the “site” – parts of it will also be content manageable. Not all of it – very rarely – but parts of it.</p> <p>Is this a good Sitecore solution then? Well I would argue; “no”. It’s a “site” running on Sitecore and could therefore in the strictest sense be dubbed a “Sitecore solution” (as the customer ordered, and got). But a good one?</p> <p>At this point, if you don’t really know what a good Sitecore solution should feel like, I would recommend you contact your local Sitecore office and have one of their excellent sales people demonstrate the “Nicam” site for you. But in summary, I would say that a good Sitecore solution should at <strong>the very least</strong> tick the following boxes:</p> <ul> <li>It doesn’t restrict the use of the built-in Sitecore tools <ul> <li>The Sitecore Page Editor <li>The Sitecore Debugger <li>The Sitecore Profiler</li></ul> <li>It allows you to use at least the basics of Sitecore functionality – probably the very reasons being why Sitecore was chosen over a competing system to begin with <ul> <li>All components on any page can be M/V tested <li>Sitecore Analytics Goals and Failures can be defined for relevant parts of the site <ul> <li>Alternatively that these are triggered in the code, hardcoded</li></ul> <li>Editor User usability has been considered <ul> <li>Placeholder settings have been set up <li>Insert Options <li>Permissions, if necessary</li></ul></li></ul> <li>It can run on a single-server environment or scale to multi-server</li></ul> <p>I mean… The CEO (usually) who ultimately approved the considerable license fee for a Sitecore solution was told by Sitecore sales people; “Sitecore can do all this”. In most cases I have seen, the CEO doesn’t dig deeper into the subject matter – he or she will sign off, and (quite naturally) expect to be getting exactly this. As well as the “site”, mind you.</p> <p><strong>Less than 5% of all the Sitecore solutions I have seen delivered, would tick all of the above boxes.</strong></p> <p>Yes. This includes some of “my own” – albeit delivering any Sitecore solution can probably never be put down to just one persons influence.</p> <p>The “reasons” are many, but the most common one that gets passed around is this. “It would require extra work for us, if we were to expand the “site” with the necessary “stuff” so you can work with it in Page Editor/Set it up for Sitecore Analytics/Configure bespoke security settings”. Extra work in this business means “more money” or at the very least “more time” – none of which any of us has enough of to pass around in this economic climate.</p> <p>And so it usually ends. From time to time customers will put in tasks like; “We want a split-test set up for our Add To Basket button – how much time will that take/what will it cost?”. Small improvements like that are made over time – everyone is happy. Except – if you’ve read this far, you will once again sense a slight irony behind my words – perhaps me.</p> <p>Over the posts to follow I will demonstrate how most of these boxes can be ticked, and actually not cost you any more time or the client any more money. Not to any significant degree anyway.</p> <p>It comes back to experience. It comes back to what can only be read between the lines in the cookbooks and blog-posts. It comes back to first deciding:</p> <p><strong>“I want to build a Sitecore solution”</strong></p> <p>And if you get at it from the onset with that attitude, and start considering what that actually means – you are on the right track.</p> <p>And if you’re in the other end; the person buying the solution. Don’t go “I need my “site” on Sitecore by the end of this year”. Go: “I need this “site” implemented as a proper Sitecore solution. I’ve seen the impressive features of Sitecore and I want to use that platform to improve my business/user experience/conversion rate”.</p> <p>Decide to build or get a good Sitecore solution, and you’ve at least started with the right mindset.</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com1tag:blogger.com,1999:blog-20883010.post-38307341447765124272012-05-05T15:43:00.000+02:002012-05-05T16:22:09.734+02:00Email Confusion - Configuring SMTP options for your Sitecore (Modules)<span style="font-family: arial">I’ve been working a lot with the different modules in Sitecore, and it seems everytime I need to send out an email I run into difficulties. The reasons for this are quite numerous.</span><br><span style="font-family: arial">In web.config we have the default MailServer settings, which are used through-out the system:</span><br><pre class="csharpcode"> <span class="kwrd"><</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">="MailServer"</span> <span class="attr">value</span><span class="kwrd">=""</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">="MailServerUserName"</span> <span class="attr">value</span><span class="kwrd">=""</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">="MailServerPassword"</span> <span class="attr">value</span><span class="kwrd">=""</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">="MailServerPort"</span> <span class="attr">value</span><span class="kwrd">="25"</span> <span class="kwrd">/></span></pre><span style="font-family: arial">Now.. No statement without exceptions, so firstly: The Email Campaign Manager(ECM) uses its own provider settings – this makes sense, since you might not want to send out 10k+ emails through your regular SMTP provider ;-). So you either use the Sitecore Message Transfer Agent(MTA) though the Sitecore App Center with a subscription, or you setup a local MTA.</span><br><pre class="csharpcode"><span style="font-family: arial">Secondly: Web Form For Marketers(WFFM) has it’s own send email action (/sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Save Actions/Send Email Message) which by default overrides your MailServer with it’s own "example.host” – not to keen on their choice of default values here, but just remove it and it will default to your web.config settings.</span></pre><pre class="csharpcode"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnS4YnK3QymEM_QQkGow8HhCpFIFg5urASO5Ua3uemm2k_xJSs-3r6lVscbZk35rfNnYQnbcKy9-sNv0s_rOS7kDyuPY8Cd5Eu7iiN9T4VvXhu1nhSxoJw1pNTac8S5_pZDrOY/s1600-h/SNAGHTML3d66de%25255B9%25255D%25255B5%25255D.png"><span style="font-family: arial"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="SNAGHTML3d66de[9]" border="0" alt="SNAGHTML3d66de[9]" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizFsZZfBXSKve_oHZby7fWLoaQwfJBhf3An08HUgh4t_hdxkQUl91Shdt_0WzQtcyQLQM0_99mlV1wp7KTr28ShDYHJbvKQZj0mYp2EWM6zGnIcmPZ3QDPD9iyRv5Y6hc_Act1/?imgmax=800" width="595" height="188"></span></a></pre>
<h1>The trouble begins</h1><span style="font-family: arial">This is all well and fine, until you have a client that want to use a SMTP service with either SSL or TLS – The default Sitecore send mail method (Sitecore.MainUtil.SendEmail) do not implement SSL. This is curiously handled in both ECM and WFFM, just not in the base Sitecore system.</span><br><br><span style="font-family: arial">In ECM, when using a local MTA, you have the option to set the “SMTP.StartTLS”. And in WFFM send mail action, you have some undocumentet settings available (not really a surprise to experienced Sitecore developers ;-)) but add the “enableSSL” attribute in you “send email message” parameters section, and this will ofcourse not enable SSL – it will however enable TLS :-P.You can verify this by sending trough gmail. To make it work you need to send trough port “587”, and not “465” which is for normal SSL encryption.</span><br><span style="font-family: arial"></span><br><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5nzL5jFfrOfFVHcWahv4VykKhQNdMjpDgSGGRxWPGFE7HcP88PhrRzmaua0ZaFEhESfXuutgyFCIz1Tj1ilqd-byn7bI3qS9T15xmy9_UFdhMuMHYYjN4Cxc85gqqhPtrH5tT/s1600-h/SNAGHTML5b41f1%25255B7%25255D.png"><span style="font-size: x-small"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="SNAGHTML5b41f1" border="0" alt="SNAGHTML5b41f1" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjE8chz8ie8jA4YpLK2TJ5PPJcE9ls3K_nbis_zj0HyXlmmz5uBpgkrB4YQ6jQGJsxal8rSYu3oAM70INGi_UooZww7HL2cLmAcBOsz0rMYIAvKbRxqVqUykUyHhzZcHnuIayp9/?imgmax=800" width="606" height="127"></span></a><br>This is because Sitecore uses the System.Net.Mail.SmtpClient which states<br>
<blockquote><span style="font-size: x-small">The </span><a href="http://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient.aspx"><span style="font-size: x-small">SmtpClient</span></a><span style="font-size: x-small"> class only supports the SMTP Service Extension for Secure SMTP over Transport Layer Security as defined in RFC 3207. In this mode, the SMTP session begins on an unencrypted channel, then a STARTTLS command is issued by the client to the server to switch to secure communication using SSL. See RFC 3207 published by the Internet Engineering Task Force (IETF) for more information.</span><br><a href="http://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient.enablessl.aspx">http://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient.enablessl.aspx</a></blockquote>Now we still have a problem with the rest of Sitecore – Password recorvery, Sitecore Ecormerce Services and so on.. But since Sitecore uses the System.Net.Mail.SmtpClient we then have the option to “enableSsl” trough our web.config file. Just add this entire segment to the bottom of your webconfig file (before the end </configuration> tag)<br><pre class="csharpcode"><span style="font-family: arial"> <span class="kwrd"><</span><span class="html">system.net</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">mailSettings</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">smtp</span> <span class="attr">deliveryMethod</span><span class="kwrd">="Network"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">network</span> <span class="attr">enableSsl</span><span class="kwrd">="true"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">smtp</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">mailSettings</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">system.net</span><span class="kwrd">></span></span></pre><pre class="csharpcode"><span class="kwrd"><span style="font-family: arial; color: black">This enables the rest of Sitecore to use a SSL(with TLS) SMTP provider, like gmail, for all email functions. Just set your MailServerSettings to:</span></span></pre><pre class="csharpcode"><span style="font-family: arial"> <span class="kwrd"><</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">="MailServer"</span> <span class="attr">value</span><span class="kwrd">="smtp.gmail.com"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">="MailServerUserName"</span> <span class="attr">value</span><span class="kwrd">="yourAccount@gmail.com"</span><span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">="MailServerPassword"</span> <span class="attr">value</span><span class="kwrd">="yourGmailPassword"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">setting</span> <span class="attr">name</span><span class="kwrd">="MailServerPort"</span> <span class="attr">value</span><span class="kwrd">="587"</span> <span class="kwrd">/></span></span><style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style><style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style></pre><pre class="csharpcode"><span class="kwrd"><span style="font-family: arial; color: black">And you are good to go. Be aware that you still need to include the <enableSSL>true</enableSSL> tag in WFFM “Send Email Message” or it will specificly set enableSSL to false.</span></span></pre><pre class="csharpcode"><span class="kwrd"><span style="font-family: arial; color: black">Also Sitecore may include this in a later version, and then you should remove this and use the provided Sitecore settings. This is testet on a Sitecore CMS 6.5.0 rev 111230 with WFFM 2.3.0 rev 120216. using .Net 4 Framework</span></span></pre><pre class="csharpcode"><span class="kwrd"><span style="font-family: arial; color: black"></span></span> </pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style> Finn Sandbeck Nielsenhttp://www.blogger.com/profile/00211348726784051909noreply@blogger.com2tag:blogger.com,1999:blog-20883010.post-58577191939795508192012-04-28T16:00:00.000+02:002012-04-28T16:00:52.349+02:00Back into the core ;-)Followers of this blog will probably have noticed, there has been a distinct lack of posts lately. Truth be said, I have had a very busy 18 months working intensely with Sitecore. In fact so busy, I’ve not had any spare time at all to share my thoughts and insights with you.<br />
This, however, I intend to change now. And not only that, I’ve also invited my friend and colleague <a href="http://www.blogger.com/profile/00211348726784051909" target="_blank">Finn Sandbeck Nielsen</a> to co-author this blog with me. Finn has been working with me on a series of complicated Sitecore projects over the past couple of years and has lots of insights, tips and tricks and nice gotcha’s to share with you. He already has a handful of posts on the scetchboard, so expect things to liven up a little around here soon.<br />
Other than that, I myself will be sharing more insights into the art of integrating external data into Sitecore – something which has been a professional pass-time for me ever since I started working with the product. And as always, there’s be bits and pieces of information from the trenches at the fronlines of Sitecore implementation-land.<br />
We hope to see you all at Sitecore Symposium EU later this year.Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com1tag:blogger.com,1999:blog-20883010.post-27635720495885997032011-01-21T09:25:00.001+01:002011-01-21T09:25:25.745+01:005 years of Sitecore blogging<p>Without any fanfares, ticker-tape parades or national holidays, this blog’s 5th anniversary came and went just this last week <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWD3353j51eFCtbqhufdIdVLIfWZKgaSNUlu3OF5V2M1l_WskL_wtejVx-7wFKh2zUT0qZxIVBRvFwBQUMYtBxJwrB09wZfBkROqb1nJ9eRIaTp8ib4Sj0TZgtqVqivADOJWoy/?imgmax=800" /></p> <p>Now I can’t be certain, but I think that might just qualify as being the earliest Sitecore blog outside Sitecore itself. Certainly should qualify me for the Old Boys League, if nothing else *g* <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK9CZ4ShNKB5c5nPZXJGX-1EY7hvL7UXBmvBe_GY1me9dipGLU0SqE-rxr8yNLuWiaoPfExyrfjh5k1UOTkpqkHgVAGlZr0GVum6LgOeaidqFSROJ7q72pN13hnlGtJUR3kEXI/?imgmax=800" /></p> <p>I do remember people like <a href="http://larsnielsen.blogspirit.com/archives/">Lars F. Nielsen</a> was blogging back when I started (his archive stretches back to October 2005), and there was also various grassroots projects going on, on communities such as Yahoo and probably more.</p> <p>I figured I’d use this post to do some sort of recap over the past 5 years of Sitecore blogging, at least from my own little narrow perspective of the world.</p> <p> </p> <h2>5 years ago</h2> <p>Apparently, it all started with some <a href="http://intothecore.cassidy.dk/2006/01/idtable-breaking-changes-in-5118.html">comments on the IDTable</a>.</p> <p>I remember we were working a lot with integration of external data and Sitecore. This was in part due to the song of the Sitecore Evangelists – this feature was being marketed back then as one of the key advantages to Sitecore CMS over its competition – and back then, putting too large quantities of data into a Sitecore solution wasn’t really a great hit in terms of performance either. So we used data sources and data providers.</p> <p>It’s actually quite funny, but the key document we used to guide us - <a href="http://sdn.sitecore.net/SDN5/Developer/Integrating%20External%20Data%20Sources.aspx">Integrating External Data Sources</a> is still on the SDN today and as far as I can tell, completely unchanged.</p> <p>Now if you’ve never had to work with custom data providers, you probably don’t know this. But it’s a matter of extending your Sitecore database’s capabilities and feeding it from multiple sources (other than just your scSolution_Master database, keeping track of the relations between the Sitecore ID and your external key, setting up Proxy items, enabling Virtual Item publishing…  all sorts of black magic in short, and while we did eventually get things working – it was never a happy marriage.</p> <p>Ironically, Sitecore eventually cracked this problem. By means of a complete re-architecture – with the release of 6.3 (I believe it was, perhaps 6.4) and Item Cloning. I’m not sure this feature was specifically targeted at solving the same types of problems we were having back then, but it certainly seems to be a side effect. </p> <p>All fine and well <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWD3353j51eFCtbqhufdIdVLIfWZKgaSNUlu3OF5V2M1l_WskL_wtejVx-7wFKh2zUT0qZxIVBRvFwBQUMYtBxJwrB09wZfBkROqb1nJ9eRIaTp8ib4Sj0TZgtqVqivADOJWoy/?imgmax=800" />    Except I very rarely do these types of integration today. I <a href="http://intothecore.cassidy.dk/2009/04/migrating-data-into-sitecore.html">import the data into Sitecore</a> instead, since it just makes a lot of things a lot easier once you’re inside under the Sitecore Feature Umbrella to take care of “boring” issues such as Versioning, Language Versions, Workflows, and so on.</p> <p>So anyway…</p> <p>By no means was this blog ever meant to be a commentary on the workings of Sitecore Data Integration, even if there has been a lot of posts about that over the years. It so happens that this type of work fits my personal developer profile quite nicely, so I seem to end up doing those types of jobs quite a bit.</p> <p>Looking back over the early posts, this blog partly came to be out of frustration. Back then, the Sitecore product was highly unstable in many areas, and each new release seemed to remove a couple of issues only to introduce a handful of new ones. And none of this was being documented, no release logs of any note, no real change logs, barely a “Known Issues” list. At least that’s how I saw it at the time, because the information COULD have been there – and just very hard to locate. SDN, while still looking almost like I remember it, wasn’t the same either – searching for content was near impossible – and that lead myself (and I suspect others) to start putting information out where Google (well… Yahoo! in my case) could find it. The thinking was; “If I can save some other developer the grief of spending 4 unscheduled project hours on combatting this problem by putting the information out there for him or her to find, it’ll be worth it”</p> <h2>Up through to today</h2> <p>Of course, most of this has changed today.</p> <p>“Aah you young kids of today, you know nothing about the hardships us old folks had to endure” <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK9CZ4ShNKB5c5nPZXJGX-1EY7hvL7UXBmvBe_GY1me9dipGLU0SqE-rxr8yNLuWiaoPfExyrfjh5k1UOTkpqkHgVAGlZr0GVum6LgOeaidqFSROJ7q72pN13hnlGtJUR3kEXI/?imgmax=800" /></p> <p>I think it was probably best summarised in <a href="http://intothecore.cassidy.dk/2008/12/showing-sitecore-how-to-improve.html">Showing Sitecore how to improve</a>, where the number one grief about it all was the inability to find absolutely anything on SDN. Documentation was starting to come out at that time, and has now evolved even more – there’s a Sitecore Cookbook on how to do just about anything with the product – now it’s actually more become a problem of finding the right document your information is located in. *g*   Still, I’d much rather have that problem, than no information being available at all.</p> <p>SDN search itself is better too. While it’s still not perfect, I think it is certainly good enough so as to silence the critique.</p> <p>So over the years, the nature of posts in this blog started to change as well.</p> <p>For one thing, I started becoming more proficient in the use of Sitecore. It could of course mean that I’ve learned to avoid the grey areas of Sitecore functionality efficiently and therefore don’t as often fall into a pit from which there can be no hope of returning. It could also mean that the product has stabilised, and there are in fact not as many pits to fall into any more.</p> <p>I think both.</p> <p>The core product has become very stable, with the only issues I really run into these days being related to only the newest of features and language handling. Releases are being well documented and there’s enough cookbooks out there now to probably even shut Gordon Ramsey up <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK9CZ4ShNKB5c5nPZXJGX-1EY7hvL7UXBmvBe_GY1me9dipGLU0SqE-rxr8yNLuWiaoPfExyrfjh5k1UOTkpqkHgVAGlZr0GVum6LgOeaidqFSROJ7q72pN13hnlGtJUR3kEXI/?imgmax=800" /></p> <p>So this blog turned, in part, from it’s original function into something else. I am still motivated by sharing information to save my fellow Sitecore developers some grief, but the posts are more informational these days. Of course, the entire scene has changed as well. There’s a virtual army of corporate Sitecore bloggers now (The Sitecore Corporation does seem to assimilate Sitecore professionals quicker than the Borg collective) and on my blogroll I get on average 6-8 blog posts a day related to everyday Sitecore usage. Most of these guys are being paid to blog, and I certainly won’t pretend to be able to keep up with the sheer volume of information they output on a monthly or yearly basis. I am worried though. Sitecore never liked public critique (despite what is being said in public), and now that 90% of all Sitecore related blogging is being moved and syndicated with Sitecore.net itself – how will this affect the public debate that they themselves have always encouraged (yet not truly sought)?</p> <p>I wonder what will happen, if one was to start commenting critique on their posts on servers that is under corporate control…   <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWD3353j51eFCtbqhufdIdVLIfWZKgaSNUlu3OF5V2M1l_WskL_wtejVx-7wFKh2zUT0qZxIVBRvFwBQUMYtBxJwrB09wZfBkROqb1nJ9eRIaTp8ib4Sj0TZgtqVqivADOJWoy/?imgmax=800" /></p> <h2>And onwards</h2> <p>I can’t predict what the future will bring. I’ve been working – without pause – with the Sitecore product since I started blogging about it. I think it’s a safe assumption that I will continue to blog about it for as long as I have something to say, add or comment about it. My target group will continue to be the Sitecore developer base, possibly shifting the focus slightly upwards towards the Solution Architects and Project Managers.</p> <p>There’ll still be no post schedule. And English will still be my second language, not my first <img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK9CZ4ShNKB5c5nPZXJGX-1EY7hvL7UXBmvBe_GY1me9dipGLU0SqE-rxr8yNLuWiaoPfExyrfjh5k1UOTkpqkHgVAGlZr0GVum6LgOeaidqFSROJ7q72pN13hnlGtJUR3kEXI/?imgmax=800" /></p> <p>To the next 5 years….</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com2tag:blogger.com,1999:blog-20883010.post-55761470017230773532010-10-21T17:53:00.001+02:002010-10-21T17:53:53.341+02:00Sitecore Language Settings, what is your best practice?<p>After reading <a href="http://adeneys.wordpress.com/about/">Alistair Deneys</a> recent <a href="http://adeneys.wordpress.com/2010/10/12/configured-dictionary-being-ignored/">blog post about the Sitecore Dictionary</a>, the whole issue about Sitecore and langguages started stirring in the back of my mind again. I’ve come across issues with it on a number of different occasions, and especially so in the past 2-3 years or so since my work as a Sitecore Consultant has taken me to many different environments across Europe.</p> <p>To start with, just so we’re clear, there’s nothing inherently wrong with the way Sitecore handles multiple languages. Atleast not as far as I am aware. Various features have been discussed in the Sitecore Community in regards to feature requests and so on, and perhaps in summary this post could be interpreted as such as well.</p> <p>My beef, if you will, is with how the language features are being <em>used</em>. And what best practices can be put in place, when implementing multi-lingual solutions.</p> <p>As Alistair points out; “English” is not just english.   This becomes even more evident, when you pair the language with a flag (as Sitecore does, when selecting languages in the Content Editor for instance).</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0HvBZW3ozA7FgCRIxnqUxgtJVQiQ9qW561xaPRlTacIdrhuVwSqP9N5U1KjglZZ1j7ojYQhrY0ZVaGIhk3mtg8NyXNGgBwsL2o5gaThqFe2gr-Yzmm-wDBcqqjQCywBaaxd2x/s1600-h/languages%5B2%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="languages" border="0" alt="languages" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-r9N6Uk8dtKOtE8hhQrx8NWZr8SZdHaOZi45A31HbVFbpjiT0NpKewLVt7AKvNz94bv5V0YMVDaZnol5zgBtSSGFjTRlachlOuULWtrucoMeuU7BNV4sb4LdK5WbeYQNKxcWU/?imgmax=800" width="244" height="93" /></a> </p> <p>The issue isn’t just with English though. I am pretty sure regionalization is an issue in many places around the world. I recently worked with a German Sitecore Partner, and right from the word “go”, they were dealing with multiple variations of German (de-DE, de-AT (Austria) and so on… I might remember it wrong with the code for Austria, I don’t use it much personally).</p> <p>Even a small country such as Denmark could have a similar issue; da-GL, da-FO and so on.</p> <p>Now, to get to my point.</p> <p>In most cases that I have worked on, regionalization is being used for geographically specific information, while the bulk of the solution runs on the root language. English sites run on English, and only rarely will it become an issue whether we’re dealing with British English, American English or Australian English for that matter. Rarely – except for the fact that you can be almost positively sure your clients/editors will complain if the flag isn’t what they expect ;-)</p> <p>Ideally (in my book), this would be configured in Sitecore as “en” (without any regional specifier), the flag would be neutral, and that would be that. Whenever regional information was in play, this would be further qualified with en-GB, en-US or en-AU and so on. When the site was configured to display “en-GB”, it would grab content in that language, and “fall back” to the root (stem, perhaps?) language when specific content wasn’t available.</p> <p>Sadly, this is not how Sitecore works at present.</p> <p>What we do these days, is install new languages via the Control Panel, and then configure Sitecore (well the site) to exclusively run on this new language. No obvious fallback mechanisms are in place (although I will discuss a few options at the bottom of this post).</p> <p>Given no fallback mechanism, I would argue that one should always explicitly declare Language and Region whenever configuring new languages in Sitecore. Fortunately, Sitecore seems to agree because this is the default behaviour as well.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid-c9Y4vHjeMxbguDkzcYNEQH2Yv2cr7bgw-SB1Qo7J4Os9MuVW578jkr8p2ciX_G62ee9e0BZHQNa0KQRLT7cMC0KUol5GI3TO4uu_CY_-E5ooncYmd23FrS5cFjeVN7fvxBM/s1600-h/languageselection%5B2%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="languageselection" border="0" alt="languageselection" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaReCxq-OJPR5EJOkjm3qEFxA-yiEas_KOs4HMddBu0CQU3jh29ehcNCA5Ghoc_KKUvZqNDCYgQyosBqKE_iTHh2IoIuqvlTNtH4KeAeD3zbje5vTTpvNC5sBaxfPdAl6It4c9/?imgmax=800" width="244" height="177" /></a> </p> <p>And</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHm-4i2R1A852q2iE7ix8pQJBJ_GH-S26ldXoRxI_0G-i-6hOEdEmFZt5VB8T_xSJbJ_WjCd1_xUGcyJmk2HugceLRVRPTtZ2watVo2_P1WF8iPwUW5kom1oNBX61YAWlZFP0A/s1600-h/languagedialog%5B3%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="languagedialog" border="0" alt="languagedialog" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyfjgsAG8FKkBRV-eNbuhYuiLcaayv4lkwCqoisV8BpI4a8LYVn9_BNvM_goGmVUZjhWkqz8OIKiRcJRnb2J0PPykzHkvpzJLvwJylxw3GFkXkZ2xse0lqCE_moGlMucMYG0Qv/?imgmax=800" width="277" height="325" /></a> </p> <p></p> <p></p> <p>Notice how, after selecting one of the (many) predefined languages, a full ISO code is generated. en-NZ in this example.</p> <p>All good. This is also what I have been recommending my clients to follow, for as long as I can recall. Specify full languages, and then we’ll see if Sitecore in the future comes up with something new and clever in terms of language fallback and so on.</p> <h3>But to every exception, there is a rule… or is it the other way around? ;-))</h3> <p>While Sitecore will, by default, install for instance the Danish language as “da-DK” (fully regionalized), the actual content we get whenever Sitecore releases localized functionality will be only “da”. (just as “en” has always been just that, without regional information although with an American flag).</p> <p>Here’s a view on a Conditional in the OMS. The Danish language has been installed using default Sitecore settings, and now we end up with this.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTNBQEOwgQjz1qjZfVvrU49_qFEOv4dpJXawtl10BDsmu5elHmOuLMBXaMFB9tesLYc-PFyqDOtG2idFz2r17ghGRLDUwXgZN1czBdVXPBTwtDNtpQkytH-xbDpkGOXxtvMAB6/s1600-h/languagemess%5B3%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="languagemess" border="0" alt="languagemess" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtRnzhJlwaMkccxHS8e7PMhosunBWuAq0DESZYF7SDY45pBvb7q_QbpeVqMpAYIoAI1B2jLKo28OYGPBoF1mXxmHZSV2TJXB5wpwE7cc5FUEd3vFMHadm_s_4IiDQwL1829V-s/?imgmax=800" width="651" height="187" /></a> </p> <p>Which was, I’m betting, not entirely what one expected. It certainly wasn’t what I expected, this much is certain :-)</p> <p>And this is where I now pass the ball on to you, readers of this blog. What are your own experiences in this area? What do you recommend?</p> <p>In summary, personally, I would love to see a fallback mechanism. This would allow me to run my regional sites in en-GB, and fall back to “en” where required. I’ve implemented a similar mechanism on some sites I’ve worked, although this was sort of messy and hooking into field renderers and whatnot.</p> <p>Today, I would probably go about it differently. Thinking something like a Condition/Action to the effect of “when <u><strong>no language version exists</strong></u> set <strong><u>item language</u></strong> to <u><strong>“en”</strong></u>”. Maybe this could be a feature at some point?</p> <p>(and yes, I’ll be happy to mock up something to this effect and Share Source it, if there’s enough interest)</p> <h3>Your thoughts?</h3> <p>So what’s your view?</p> <h3></h3> <h3>In the meantime</h3> <p>Here’s a fix to the above language issue. It will work whether you prefer your Danish (or whatever language) as “da” or “da-DK”, just reverse the statements. Alistair has his Revolver, I only have my standard tools… :P</p> <p><strong><u>You do this at your own risk however.</u></strong></p> <ul> <li>Backup your solution. Don’t tell me I didn’t warn you ;-)</li> <li>Do a full publish</li> <li>Stop your IIS</li> <li>Open up your Sql Management Studio (Express, if that’s what you have)</li> <li>Execute the following:</li> </ul> <blockquote> <p>USE Sitecore_master <br />UPDATE UnversionedFields SET [Language]='da-DK' WHERE [Language]='da' <br />UPDATE VersionedFields SET [Language]='da-DK' WHERE [Language]='da'</p> </blockquote> <p>After which, you are one step closer to avoiding language confusion later on. I’ve done exactly this on a solution I am working on, and have found no ill effects from this procedure. However, do keep in mind that directly working on the Sitecore database is not (and probably never will be) supported and you’re on your own.</p> <p>Here’s the result, in my solution</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtJxg2zD3zE8bZwWL04OZsGYS1zxPjdAxmn8ZO8vPlelkKgoH6SjzQSWGWqf4SjlfJv-HKDLgNAQvEH-5G7WD1OWO4x7Qo42H09u-SAnrEbAMiYiE-nFtW4SHehQkvgs6ThRwZ/s1600-h/languagesorted%5B3%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="languagesorted" border="0" alt="languagesorted" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEia6N_FmjWU_74QV5aBDVreHrXiBC9pcx0KGzYcVUx896alNQfo3FUcXfMtj5LM0fkDMompdWr2u50yHIfrGMh9Hov_lkCuZSnTn2Fm0RJAfNRvtRkZ9y1uyFcsgkwY04s1Z8Gd/?imgmax=800" width="682" height="114" /></a></p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com5tag:blogger.com,1999:blog-20883010.post-44822642989775253262010-09-02T08:27:00.001+02:002010-09-02T08:27:29.699+02:00Disabling Lucene indexes (programatically)<p>Over the past 18 months or so, my work has taken me around a great deal of integration jobs. As those of you who follow this blog will know, integration of external data into Sitecore is something that has often been explored (and still is).</p> <p>Recently, while faced with the task of importing, cross-referencing, IDTable mapping – about 14.000 items on a regular (scheduled) basis, I began to look for even further ways of improving integration performance.</p> <p>One thing I put my attention towards, was entries like these in the log files, while the import was running:</p> <blockquote> <p>3796 10:34:59 INFO  'ImportCategories' Executing <br />ManagedPoolThread #2 10:35:13 INFO  Starting update of index for the database 'master' (7 pending). <br />ManagedPoolThread #2 10:35:13 INFO  Update of index for the database 'master' done. <br />ManagedPoolThread #5 10:35:13 INFO  Starting update of index for the database 'master' (8 pending). <br />ManagedPoolThread #5 10:35:13 INFO  Update of index for the database 'master' done. <br />ManagedPoolThread #4 10:35:13 INFO  Starting update of index for the database 'master' (9 pending). <br />ManagedPoolThread #4 10:35:14 INFO  Update of index for the database 'master' done. <br />ManagedPoolThread #4 10:35:14 INFO  Starting update of index for the database 'master' (10 pending). <br />ManagedPoolThread #4 10:35:14 INFO  Update of index for the database 'master' done. <br />ManagedPoolThread #4 10:35:14 INFO  Starting update of index for the database 'master' (1 pending). <br />ManagedPoolThread #4 10:35:14 INFO  Update of index for the database 'master' done. <br />ManagedPoolThread #4 10:35:14 INFO  Starting update of index for the database 'master' (1 pending). <br />ManagedPoolThread #4 10:35:14 INFO  Update of index for the database 'master' done.</p> </blockquote> <p>So basically, as the items were being imported, the indexer ran along in the background desperately trying to catch up. Well maybe not desperately… ;-)</p> <p>I started looking around for an official way to disable these indexes. The idea being, disable the indexes – run the integration – enable them again and let them catch up. Has to be more effective yea?  Kind of like harddisk trashing otherwise, for those of you who still remember that phenomenon ;-)</p> <p>Alex Shyba came up on top of most searches I did, describing how to <a href="http://sitecoreblog.alexshyba.com/2009/03/how-to-completely-disable-lucene-index.html">disable Lucene indexes</a>. The post is minded towards how to improve performance in the production environments however, and not to mention it’s well over a year old by now. Sitecore has been doing a lot of work on the Lucene/Sitecore search API, and odds are that things may have changed in the year that passed.</p> <p>Besides, I needed a way to do it programatically.</p> <p>I started looking into ways of achieving this. Basically exploring APIs, configuration settings, even considering … patching-via-reflection into the Sitecore Patching Configuration API to comment out sections of Sitecore Configuration *grin*.  Fortunately, before exploring any of these options in great detail, I had the common sense to approach the excellent Sitecore Support team to get a better (and more importantly, supported) solution. And fortunately, as it turns out, there is one. Clean, and very very simple. Thanks guys :-)</p> <pre class="csharpcode">Sitecore.Configuration.Settings.Indexing.Enabled = <span class="kwrd">false</span>;</pre>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }</style>
<p>Couldn’t be simpler :-)</p>
<p>And the configuration equivalent exists as well, even if it is not shown in the 6.3 default web.config.</p>
<p><setting name="Indexing.Enabled" value="false" /> </p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com2tag:blogger.com,1999:blog-20883010.post-20454794758233656392010-07-29T14:53:00.001+02:002010-07-29T14:53:15.851+02:00Sitecore 6.3, ComponentArt, WebForms For Marketers, and you<p>Apologies for the almost complete silence in this blog, but Real Life(tm) has been taking me for quite a spin these past months.</p> <p>Just a quick heads-up, in case you run into the same issue that I just had.</p> <p>After installing Sitecore 6.3 (100716), WebForms For Marketers 2.1 (100629), I ran into the following issue when attempting to edit values on a DropList Field.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhm2aeQMXWvg08mRnzGi-CSVZ6fuzKWInK4uFJa_m4rJuqqQcE2pGHeOuQEd77x5q6X_Wq2Bdq5fXO5Vtxp2dAEZZX5c1YS0uV4RWntV3JJU8GuS1w_QqygMXOKlnNErC9U9fuO/s1600-h/WFM1%5B4%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="WFM1" border="0" alt="WFM1" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhow6BmKyyIUjt_iX8stNS41Q_YyuVs80oTLM7KuxxiDIdwrpV-TptCr0fdKKyFo-Do9JFUAF-nhPo7FwSA79bEoXwld5pqmfOTqzbIBrtYFnF9IbcMP4ndVdVsowJlzsOAzs4g/?imgmax=800" width="570" height="410" /></a> </p> <p>“Could not load file or assembly ‘ComponentArt.Web.UI, Version=2007.1.1617.2,Culture=neutral,PublicKeyToken=9bc9f846553156bb’ or one of its dependencies. HRESULT: 0x80131040”</p> <p>It seems, Sitecore 6.3 comes with an upgraded version of the ComponentArt library, and version information on this new assembly reads 2010.1.2637.35. Also seems that WFFM is compiled against the previously distributed version, and fails because of it.</p> <p>The fix I applied is relatively straight forward, and should work for you as well. Add a new assembly dependency mapping in your web.config as</p> <pre class="code"><span style="color: blue"><</span><span style="color: #a31515">dependentAssembly</span><span style="color: blue">>
<</span><span style="color: #a31515">assemblyIdentity </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">ComponentArt.Web.UI</span>" <span style="color: red">publicKeyToken</span><span style="color: blue">=</span>"<span style="color: blue">9bc9f846553156bb</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">bindingRedirect </span><span style="color: red">oldVersion</span><span style="color: blue">=</span>"<span style="color: blue">2007.1.1617.2</span>" <span style="color: red">newVersion</span><span style="color: blue">=</span>"<span style="color: blue">2010.1.2637.35</span>" <span style="color: blue">/>
</</span><span style="color: #a31515">dependentAssembly</span><span style="color: blue">>
</span></pre>
<a href="http://11011.net/software/vspaste"></a>
<p>And the problem goes away. I don’t know if the assembly versions are fully compatible, so don’t come banging on my door if this fix doesn’t make all potential problems go away ;-)   It appears to work like a charm here, in our solution.</p>
<p>Issue has been raised with Sitecore Support (331878).</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com5tag:blogger.com,1999:blog-20883010.post-43455519499937136752009-09-03T18:57:00.001+02:002009-09-03T18:57:49.280+02:00Partial Cache Clearing in Sitecore<p>For a very long time, some very serious performance issues have affected certain types of Sitecore deployments. It has to do with scaling a Sitecore solution into webfarms (multiple content delivery servers) and the use of the Sitecore Staging Module.</p> <p> </p> <p>In very short summary, if the website is updated frequently (say, for instance, an editorial staff of 10 or so, posting relevant market updates) AND the website is under heavy load, most practical uses of the tool Sitecore recommends and supports will more or less kill performance on your SQL Servers or whatever storage mechanism you have in place.</p> <p> </p> <p>I’ve never blogged in detail about this issue, as I didn’t have a client myself that was affected directly by this issue. Paul George has blogged about it in detail however, and if you want to learn more about what this issue is and if it could be affecting you, take a look at these posts:</p> <p> </p> <ul> <li><a href="http://blog.paulgeorge.co.uk/2009/07/22/problems-with-sitecore-and-the-staging-module/">Problems with Sitecore and the staging module part 1</a></li> <li><a href="http://blog.paulgeorge.co.uk/2009/07/26/problems-the-sitecore-staging-module-part-2-extranet-user-loses-session/">Problems with the Sitecore 6 staging module part 2 – extranet user loses session</a></li></ul> <p> </p> <p>So why write about it now?</p> <p> </p> <h2>The good news</h2> <p><a href="http://sitecoresupport.blogspot.com/2009/09/announcement-partial-item-cache.html">Alex Shyba posted about a new Sitecore Shared Source module</a> that was just made publically available; <a href="http://trac.sitecore.net/SitecoreStager/browser/Tags/rev090820">SitecoreStager – Sitecore Partial (item) Cache Clearing Module</a>.</p> <p> </p> <p>In short, instead of just uncritically clearing the entire cache on your target server, dropping user sessions, putting out the cat and so on – the SitecoreStager will instead execute a partial cache clear and in essence just clear items from the cache that were affected by the publishing operation.</p> <p> </p> <p>This is very good news indeed, and for those clients who have been affected by this problem I am sure it’s tipped hats all ‘round :-)</p> <p> </p> <h2></h2> <h2>The bad news</h2> <p>This is actually something that has been nagging me a bit for some time now, and has just been refreshed by this release.</p> <p> </p> <p>It is Shared Source.</p> <p> </p> <p>Don’t get me wrong. I think the <a href="http://sdn.sitecore.net/Resources/Shared%20Source.aspx">Sitecore Shared Source Library</a> is an excellent idea. I have code in there myself, and it’s a perfect place to go look for those “there has to be someone else who had this problem and solved it” code snippets and field types and whatever it may be.</p> <p> </p> <p>But it is also, for obvious reasons, unsupported by Sitecore themselves. I mean, how could they? Half the modules and code pieces in there come from independent sources like myself – and it wouldn’t be reasonable to expect Sitecore to support them.</p> <p> </p> <p>But what about the code Sitecore release to the library themselves?</p> <p> </p> <p>Would you not agree; “With Sitecore, you can have a team of editors publishing content to an enterprise level site, and performance will still rock” is a statement you’ll find (albeit probably not verbatim) in Sitecore marketing material? But it is unsupported?</p> <p> </p> <p>I feel somewhat the same for the <a href="http://trac.sitecore.net/MultipleSitesManager">Multiple Sites Manager</a>… shouldn’t a standard Sitecore have a solution for regular (advanced) editors to set up new websites without having to edit configuration files? Or is that limited to Foundry licenses only?</p> <p> </p> <h2>What I mean is</h2> <p>Sitecore marketing materials doesn’t exactly help anyone much, when they boast “Comes with Blog Modules, Wiki Modules and even <a href="http://www.sitecore.net/en/Products/Sitecore-CMS/Integration-Tools.aspx">Microsoft Dynamix and Microsoft BizTalk integration</a> (yes…)”; yet offers little or no actual support other than pointing the users, the licensees, the Sitecore implementors (like myself) in the direction of the Shared Souce Library, shrug and go… “From here, you’re on your own”.</p> <p> </p> <p>Kudos for making this module. I think this belongs in the core package however, fully supported and updated with every Sitecore release. That’s my 2 cents worth.</p> <p> </p> <p> </p> <p>.</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com2tag:blogger.com,1999:blog-20883010.post-17333317664524909122009-06-23T20:04:00.001+02:002009-06-23T20:04:16.020+02:00Code Monkeys?<p>Before I begin, I’m breaking routine here – this post is in no way Sitecore related.</p> <p> </p> <p>So here I was, reading through my blogroll, catching up on bits and bobs from around the world. And then one of the sites decides to confront me with the following ad:</p> <p> </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK96h17cfmaieHIFFp8tSY0Lq5iO4X_2q8e7_SMI-7jVPw2hMM9BDPFwQrpLo1ZHMk_BG_OnEaXx5qWBpPUt3VpyUSqT-OmoSE2TYN_WqSPIqQN4FPoSTmwJk25C9MhlshmUgC/s1600-h/chimps%5B2%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="chimps" border="0" alt="chimps" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqaIeaqr0MATPWosrFPdqAJCN6JeuLMG6h9UXcUR7_yWhocOonLGNn6fkdYIRWGRLBoWsOHdmcXDiD30fiQ7T-frg00pYxqEupq1Ze8OeIeEiuqJqanGJbJ-pCQ2bo9dP5KO8f/?imgmax=800" width="215" height="244"></a> </p> <p> </p> <p>I am, by own admission, quite possibly a bit over sensitive to the idea that developers are <a title="The Wetware Crisis" href="http://brucefwebster.com/2008/04/11/the-wetware-crisis-the-dead-sea-effect/">interchangeable code monkeys</a>. I’ve worked in the internet industry pretty much since the inception in the mid 90s, and I will never subscribe to the notion that one developer (me) can easily be replaced by a <a title="Rent a Coder" href="http://www.rentacoder.com/RentACoder/DotNet/default.aspx">developer</a> working offshore for as low as $ USD 7.00 a day.</p> <p> </p> <p>But then again, maybe that’s just me ;-)</p> <p> </p> <p>Am I missing a point here?</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com1tag:blogger.com,1999:blog-20883010.post-50449296714646182322009-06-05T14:38:00.001+02:002009-06-05T14:50:25.702+02:00Just because you can doesn’t mean you should<p>For those of you who don’t know this; I make my living as a Sitecore Professional Services Consultant. Understand this in the context of working with the Sitecore <em>product</em> as a consultant, I don’t actually work for Sitecore <em>the company</em>.</p> <p> </p> <p>My job, if you will, consists primarily of establishing contact with newly started Sitecore Partners or want-to-become Partners, and bringing to them whatever skills I can offer to help them take those first shaky steps when bringing their first Sitecore Project to life.</p> <p> </p> <p>Don’t worry, am not going to start any kind of sales pitch here, that’s not what <strike>blogs </strike>this blog <strike>are</strike> is for. I’m only telling, so you know what sort of context this post is in and where my experiences are coming from. </p> <p> </p> <p>As you can maybe imagine (or even remember still?); the myriad of questions in relation to Sitecore coming from developers, sales people, project managers and so on are many and not far between. Nothing wrong with that, obviously, we all have to learn. Mostly the questions start out around the capabilities of the product, “can” questions. After this, they move into “how”, and ultimately (in the cases where I’m actually lucky enough to be around for the entire project) “when”.</p> <p> </p> <h2>"Can” questions</h2> <p>Initially, when requirements are being gathered, it’s a heap of “Can Sitecore do this?” type of questions. To name just a very few examples:</p> <p> </p> <ul> <li>Can you integrate Sitecore with SharePoint? <li>Can you use the AJAX toolkit on a Sitecore site? <li>Can Sitecore deliver Flash content? <li>Can we build a database of our people and offices in Sitecore and have them shown on the site? <li>Can we have search options on our site? <li>Can Sitecore integrate with Google Analytics? <li>Can we implement breadcrumbs with Sitecore?</li></ul> <p> </p> <p>There are, of course, many more questions that are usually asked – as I’m sure any member of the Sitecore sales team will tell you as well. Incidentally, the answer to all of the above questions is “yes”.</p> <p> </p> <p>And now we’re getting to the point. Working with Sitecore – how often is it, that you actually have to sat “No”. “Sorry. Can’t do that with Sitecore”? While I have no statistics on it, I can still state without shaking my hand that this very rarely happens. Sitecore allows you to say “Yes. Can do” to almost any requirement, no matter how far fetched it may seem or even be.</p> <p> </p> <p>Let’s not get too carried away, however, this is not quite as amazing as it might appear on the surface. If we boil it down to the bone, Sitecore can be described as an “ASP.NET application that adds Content Management services to your ASP.NET websites”. Right – I’m sure there’s a hundred different marketing spins to be made here, but bear with me… ;-) </p> <p> </p> <p>Sitecore is an ASP.NET application. It sits, talks, walks and sounds like an ASP.NET application. And here’s the good bit – and the reason you can answer “yes” to almost any requirement – <em>anything you can do in ASP.NET, you can continue doing – Sitecore or no Sitecore involved</em>.</p> <p> </p> <p>Even the Sitecore product itself is flexible to the bone - (almost) everything is based on configuration files, and they can be tweaked and twisted or completely rewritten – in case there’s a particular feature you are missing, or if there’s a certain way Sitecore works that you want to change or even remove entirely. A blessing, right?</p> <p> </p> <p>Wrong! And on after-thought; “Wrong… mostly!”</p> <p> </p> <p>Don’t get me wrong. I totally get where Sitecore is coming from, in wanting to create as flexible a platform as possible. As any software vendor on the market; the more requirements you can say “yes” to, the more sales you are likely to get. This is as simple and obvious as can be, really.</p> <p> </p> <p>I’m just saying; maybe “Yes. But…” is a better answer. Let’s take a look at some more “can” questions. These are not fictional by the way, they are real questions asked by real people.</p> <p> </p> <h2>“Can” questions part 2</h2> <p> </p> <ul> <li>Can you rewrite how Sitecore creates new items, so that all language versions are created instead of just the current language you are editing? <li>Can you use ASP.NET Master Pages and Content Pages with Sitecore? Does Sitecore support nested Master Pages? <li>Can you work with Web Parts in Sitecore? <ul> <li>This one deserves a closer examination; and after doing that what is <em>usually</em> really asked here is, “Can I put my page into edit mode, and add/remove Web Parts on different placeholder areas of the page?”</li></ul> <li>Can I use Microsoft Word to edit my pages? <li>Can I use the Microsoft MVC Framework with Sitecore?</li></ul> <p> </p> <p>Do you see where this is going?</p> <p> </p> <p>What are these people really asking for? Features?</p> <p> </p> <p>Nope. In the vast majority of cases, these are not feature requests. These people are looking for <em><strong>familiarity</strong></em>.</p> <p> </p> <p>Yep. That’s right. Familiar ground to stand on. Something known, as opposed to something unknown. Can we blame them? Absolutely not. I think we all have a bit of <em>fear of the unknown</em>, to one degree or another.</p> <p> </p> <p>And of course, Sitecore is fully aware of this as well. They have released features specifically to address some of these very questions already, and there are more to come. That way, we can continue saying “yes” and everyone wins. Just keep this point in mind however – <em><strong>familiarity</strong></em>. Not features. More often than not.</p> <p> </p> <h2>The solution</h2> <p>I guess the word “solution” is not really appropriate, as it seems to indicate there was a problem to begin with. Look <em>behind</em> the question. Find out why it is being asked in the first place, and then work from there.</p> <p> </p> <p>Why does the person want to change how Sitecore creates language versions?</p> <p> </p> <p>Turns out, this is apparently how other major CMS vendors do it. Now I happen to think Sitecore is doing this the right way, but this person came from a different background with different experience. He was looking for a way to solve (amongst other things) content translation flows – and was maybe not aware of the various tools and gadgets that Sitecore provide for this purpose.</p> <p> </p> <p>Master Pages?</p> <p> </p> <p>Think “overworked .NET developer who really cannot be bothered to try and figure out how Sitecore constructs the pages it delivers”.</p> <p> </p> <p>I certainly get where he or she is coming from. But don’t go chasing down this road – sit down and show (don’t tell; show) how a Sitecore “Layout” and an ASP.NET “Master Page” is more or less essentially the same thing. (I know… but really – in most cases, this is just semantics). So “placeholders”, not “content placeholders”. “Layouts”, not “Master Pages”.</p> <p> </p> <p>Am sure you’re seeing the pattern here, by now :-)</p> <p> </p> <p>All I’m saying is; “Yes. You absolutely CAN reconfigure Sitecore, so that security is completely disabled, users and roles come off a scan of your table napkin, your coffee machine starts automatically when you publish AND it will even offer background music when content editing”. Ok I’m being ironic, obviously, but see the point is this.</p> <p> </p> <p><strong><u>Just because you can doesn’t mean you should</u></strong></p> <p> </p> <p>Don’t go mucking about with Sitecore, jumping through hoops to make it act a certain way. Chances are – and most times – you aren’t dealing with a real requirement at all. All too often have I arrived at a “young” Sitecore shop and seen a development team dive right into a complete reconfiguration of how Sitecore works. Pipelines altered and skewed and tonnes of bespoke code developed because “Otherwise we couldn’t meet our clients requirements”. Err… WHAT requirements were those exactly?</p> <p> </p> <p>I mean, come on. I’ve done lots and lots of Sitecore sites now – and I can count the number of times I’ve actually had to modify standard Sitecore behaviour on maybe one hand. This does not include adding new modules or field types, or anything like that – I’m talking about core changes such as altering the publishing pipeline, messing about with security resolvers and so on.</p> <p> </p> <p>Not pointing fingers at anyone in particular here. If I were, I would have to point to myself first – I’m as guilty of trying to change something from what it is into something that is familiar as I gather anyone else is.</p> <p> </p> <p>I just think it’s something we should all keep in mind. </p> <p> </p> <p>And oh… before I end.</p> <p> </p> <p>Sometimes the question DOES lead to a “legitimate” feature request ;-)</p> <p> </p> <p> </p> <p>-</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com6tag:blogger.com,1999:blog-20883010.post-14974907387418215022009-05-15T01:37:00.001+02:002009-05-15T01:37:43.747+02:00Listing “Related Articles” with Sitecore using the LinkDatabase<p>Seems I am on a writing streak this week. Am taking a week off, you see, from my normal everyday Sitecore Consulting, and seem to have a bit of time on my hands to catch up on some of all the posts I’ve been meaning to write for a while. Don’t worry; after this I will probably be way too busy again for a while to find time to post ;-)</p> <p> </p> <p>So I catching up on <a title="Link to StackOverflow.com" href="http://stackoverflow.com">StackOverflow</a> the other day, and an interesting question was posed; “<a title="How to find related items by tags in Lucene.NET" href="http://stackoverflow.com/questions/848229/how-to-find-related-items-by-tags-in-lucene-net">How to find related items by tags in Lucene.NET</a>”.</p> <p> </p> <p>And while there probably IS a way to actually do this with Lucene.NET; I remember my initial thought was “but why go through all the hassle of configuring and setting it up to do this?”. Not only would it matter things from an Operations point of view; it would require more code and more code that was completely dependant on specific configuration settings in the Lucene indexes.</p> <p> </p> <p>Now, let me be very clear, I am no big expert on Lucene. There are many of you out there who know it well, and would probably be able to cook up a solution to answer the guys question using it. As for myself, I try to keep as much arcane configuration out of any project I am involved in – especially to solve a problem such as this, where Sitecore pretty much gives you the tools you need to solve it straight out of the box.</p> <p> </p> <p>So anyway. Guy was asking in a Lucene context, but was looking for proposals. And I decided to give it a whirl, mocked up some pseudo code to solve the problem, and that was that. But see; everyone can write pseudo-code :P And it’s only fair I put my err… code where my mouth is, and write up a real example of how this can be achieved in a manner I explained. Here goes.</p> <p> </p> <h2>Setting it up in Sitecore</h2> <p>I start by making up two templates:</p> <p> </p> <p>1) “Simple Value”, which will be used to organise the meta tags I will be drawing upon.</p> <p>It has no fields.</p> <p> </p> <p>2) “Article”, which I will use to demonstrate how to implement “Related Articles” functionality.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1j1hzD6g1RpXnHoh7O_4SuQuJBSOt-mXh6nJq50OGsM09yTK9DXVpcZ28LweKm8vSlxFVKdvsIXTsqHu_OyNpEFBE17A7l2UIBk85iMahARy7EyQPvHbX1zJTq-02W0claAbo/s1600-h/image%5B2%5D.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="74" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzVaRPC007mb3vkUY-pyLWzFj-0H5TaIv9XtxLwonETqcqE-g1O8ABQ2xfycUI5mRbjzOlOnY4zM0OIeQiCyWe6hyphenhyphenaRup5leeiIEH73nXq7BXrQ8NP72iq9E5eJPknIQfR8RuN/?imgmax=800" width="244" border="0"></a> </p> <p> </p> <p>I then set up a meta-structure that I will be using to tag up my articles, and ultimately draw out related articles. I don’t fill out the entire structure, nor do I mean to imply this structure is perfect. But it is enough to demonstrate the point, and should be easy enough to follow. All the tags are based on the “Simple Value” template.</p> <p> </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTI2mVExbUbHkbx0G-tELfvxOILIDOtxPfYHBvnFNmbXw-TOXtifBmIj_-IzQYhAP9QK4wLAC9zYQuWA7_0mrA_CofoZ560Hu7C4qAjAe-JoCXB_uqYnnAiFunLrcXIc8jMoti/s1600-h/image%5B5%5D.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwym7ie51eQ7tPzwy34SM0zIlxuD3LBwh2EVqyC3QOY6sARhbTg-_4LvHqoQm4C8PCDrJ5VwlRI2p4bbeptfEOrYtBmZeaYgfVpTHcKWu_5JJB9nfnQ4JFwP3nvg0NkX0XYvog/?imgmax=800" width="101" border="0"></a></p> <p> </p> <p>After this, I go through the somewhat tedious task of setting up a number of articles that are tagged in different ways.</p> <p> </p> <p>For now, I type and tag in 7 articles; like this:</p> <p> </p> <p>Name: Ben Hur</p> <p>Tags: O2 Arena, Theatre</p> <p> </p> <p>Name: Britney Spears</p> <p>Tags: O2 Arena, Pop, Concert</p> <p> </p> <p>Name: Depeche Mode</p> <p>Tags: O2 Arena, Alternative, Concert</p> <p> </p> <p>Name: Michael Jackson</p> <p>Tags: O2 Arena, Pop, Concert</p> <p> </p> <p>Name: Nickelback</p> <p>Tags: O2 Arena, Rock, Concert</p> <p> </p> <p>Name: Pet Shop Boys</p> <p>Tags: O2 Arena, Pop, Concert</p> <p> </p> <p>Name: War of the Worlds</p> <p>Tags: O2 Arena, Theatre</p> <p> </p> <p>I should probably go on for a while longer if I really wanted to go all-out in demonstrating this. However, I do have enough now, and it’ll have to do. I hate typing in test data ;-)</p> <p> </p> <p>Before I go on, I should explain exactly how I intend to deduce what “related articles” should be. It can be done and determined in many ways – but I am proceeding exactly in the manner that was originally in question on StackOverflow. The rule can be described as two statements:</p> <p> </p> <p>1) An article is related if it shares one or more tags with the source article</p> <p>2) The more tags it shares, the more relevant it becomes (i.e. should appear higher on the list)</p> <p> </p> <p>Lastly, I set up a blank .ASPX page in my webroot named “TestRelated.aspx”, and I quickly mock up two <a title="Link to the Trac homespace for CorePoint.DomainObjects" href="http://trac.sitecore.net/DomainObjects">DomainObjects</a> that I will build upon for this functionality.</p> <p> </p> <p>SimpleValue.cs</p><pre class="code"><span style="color: blue">using </span>CorePoint.DomainObjects.SC;
<span style="color: blue">using </span>CorePoint.DomainObjects;
<span style="color: blue">namespace </span>Website.Related
{
[<span style="color: #2b91af">Template</span>(<span style="color: #a31515">"user defined/simple value"</span>)]
<span style="color: blue">public class </span><span style="color: #2b91af">SimpleValue </span>: <span style="color: #2b91af">StandardTemplate
</span>{
}
}
</pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>Article.cs</p><pre class="code"><span style="color: blue">using </span>System;
<span style="color: blue">using </span>System.Collections.Generic;
<span style="color: blue">using </span>CorePoint.DomainObjects.SC;
<span style="color: blue">using </span>CorePoint.DomainObjects;
<span style="color: blue">namespace </span>Website.Related
{
[<span style="color: #2b91af">Template</span>(<span style="color: #a31515">"user defined/article"</span>)]
<span style="color: blue">public class </span><span style="color: #2b91af">Article </span>: <span style="color: #2b91af">StandardTemplate
</span>{
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"title"</span>)]
<span style="color: blue">public string </span>Title { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"text"</span>)]
<span style="color: blue">public string </span>Text { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"tags"</span>)]
<span style="color: blue">public </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">Guid</span>> Tags { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
}
}
</pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>And finally, in my TestRelated.aspx.cs, I add a bit of code to test that everything is as expected.</p><pre class="code"><span style="color: blue">public partial class </span><span style="color: #2b91af">TestRelated </span>: System.Web.UI.<span style="color: #2b91af">Page
</span>{
<span style="color: blue">protected void </span>Page_Load( <span style="color: blue">object </span>sender, <span style="color: #2b91af">EventArgs </span>e )
{
<span style="color: blue">var </span>director = <span style="color: blue">new </span><span style="color: #2b91af">SCDirector</span>();
<span style="color: #2b91af">List</span><<span style="color: #2b91af">Article</span>> articles = director.GetChildObjects<<span style="color: #2b91af">Article</span>>( <span style="color: #a31515">"/sitecore/content/global/articles" </span>);
<span style="color: blue">foreach </span>( <span style="color: #2b91af">Article </span>article <span style="color: blue">in </span>articles )
{
<span style="color: green">// Get the SimpleValues (name) from the tag Guids
</span><span style="color: blue">var </span>simpleValues = article.Tags.ConvertAll<<span style="color: blue">string</span>>( a =>
{
<span style="color: blue">return </span>director.GetObjectByIdentifier<<span style="color: #2b91af">SimpleValue</span>>( a ).Name;
} );
<span style="color: #2b91af">StringBuilder </span>sb = <span style="color: blue">new </span><span style="color: #2b91af">StringBuilder</span>();
simpleValues.ForEach( sv => sb.Append( sv + <span style="color: #a31515">' ' </span>) );
Response.Write( <span style="color: blue">string</span>.Format(
<span style="color: #a31515">"Name: {0}<br />Tags: {1}<br /><br />"</span>,
article.Name,
sb.ToString() ) );
}
}
}
</pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>So far so good. I run the code, and I get a replica of the list I already showed:</p>
<p> <p>Name: Ben Hur<br>Tags: O2 Arena Theater <br>Name: Britney Spears<br>Tags: Pop Concert O2 Arena <br>Name: Depeche Mode<br>Tags: O2 Arena Concert Alternative <br>Name: Michael Jackson<br>Tags: Pop Concert O2 Arena <br>Name: Nickelback<br>Tags: Rock Concert O2 Arena <br>Name: Pet Shop Boys<br>Tags: O2 Arena Concert Pop <br>Name: War of the Worlds<br>Tags: O2 Arena Musical
<p> </p>
<p>Excellent. After all this, I am now ready to proceed to the good stuff ;-)</p>
<p> </p>
<h2>Finding Related Articles using the Sitecore LinkDatabase</h2>
<p>Having an Article entity in place, makes this an obvious place to add functionality such as Related Articles. I could either add it as a Lazy Load property named “Related Articles”, or I could write a method named “GetRelatedArticles()”. This is mostly down to aesthetics and practices; personally I prefer the first option.</p>
<p> </p>
<p>I expand the Article.cs with a little bit of code. The original pseudo-code I suggested, is entered in comments, for reference.</p><pre class="code"><span style="color: blue">private int </span>_referenceCount;
<span style="color: #2b91af">List</span><<span style="color: #2b91af">Article</span>> _RelatedArticles = <span style="color: blue">null</span>;
<span style="color: blue">public </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">Article</span>> RelatedArticles
{
<span style="color: blue">get
</span>{
<span style="color: blue">if </span>( _RelatedArticles == <span style="color: blue">null </span>)
{
_RelatedArticles = <span style="color: blue">new </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">Article</span>>();
<span style="color: blue">var </span>referenceCount = <span style="color: blue">new </span><span style="color: #2b91af">Dictionary</span><<span style="color: #2b91af">Guid</span>, <span style="color: blue">int</span>>();
<span style="color: green">// for each ID in tags
</span><span style="color: blue">foreach </span>( <span style="color: #2b91af">Guid </span>id <span style="color: blue">in </span>Tags )
{
<span style="color: blue">var </span>sv = Director.GetObjectByIdentifier<<span style="color: #2b91af">SimpleValue</span>>( id );
<span style="color: green">// Personal note: In this particular instance, performance
// could be gained here, but not loading up full articles
// via DomainObjects but hitting the LinkDatabase directly instead
// get all documents referencing this tag
</span><span style="color: #2b91af">List</span><<span style="color: #2b91af">Article</span>> articles = sv.GetReferrers<<span style="color: #2b91af">Article</span>>();
<span style="color: green">// for each document found
</span>articles.ForEach( a =>
{
<span style="color: blue">if </span>( a.Id != Id )
{
<span style="color: green">// if master-list contains document;
</span><span style="color: blue">if </span>( referenceCount.ContainsKey( a.Id ) )
referenceCount[ a.Id ]++; <span style="color: green">// increase usage-count
</span><span style="color: blue">else </span><span style="color: green">// else;
// add document to master list
</span>referenceCount[ a.Id ] = 1;
}
} );
}
<span style="color: green">// Now we have a list of all the relevant guids being referenced on all tags
// on this article. Load them up, and stamp them with the reference count
</span><span style="color: blue">foreach </span>( <span style="color: blue">var </span>key <span style="color: blue">in </span>referenceCount.Keys )
{
<span style="color: blue">var </span>relatedArticle = Director.GetObjectByIdentifier<<span style="color: #2b91af">Article</span>>( key );
relatedArticle._referenceCount = referenceCount[ key ];
_RelatedArticles.Add( relatedArticle );
}
<span style="color: green">// sort master-list by usage-count descending
</span>_RelatedArticles.Sort( ( a, b ) => b._referenceCount.CompareTo( a._referenceCount ) );
}
<span style="color: blue">return </span>_RelatedArticles;
}
}
</pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>And to test if what I’m getting from this is what I expect, I also add some code to my TestRelated.aspx so it becomes:</p><pre class="code"><span style="color: blue">protected void </span>Page_Load( <span style="color: blue">object </span>sender, <span style="color: #2b91af">EventArgs </span>e )
{
<span style="color: blue">var </span>director = <span style="color: blue">new </span><span style="color: #2b91af">SCDirector</span>();
<span style="color: #2b91af">List</span><<span style="color: #2b91af">Article</span>> articles = director.GetChildObjects<<span style="color: #2b91af">Article</span>>( <span style="color: #a31515">"/sitecore/content/global/articles" </span>);
<span style="color: blue">foreach </span>( <span style="color: #2b91af">Article </span>article <span style="color: blue">in </span>articles )
{
<span style="color: green">// Get the SimpleValues (name) from the tag Guids
</span><span style="color: blue">var </span>simpleValues = article.Tags.ConvertAll<<span style="color: blue">string</span>>( a =>
{
<span style="color: blue">return </span>director.GetObjectByIdentifier<<span style="color: #2b91af">SimpleValue</span>>( a ).Name;
} );
<span style="color: #2b91af">StringBuilder </span>sb = <span style="color: blue">new </span><span style="color: #2b91af">StringBuilder</span>();
simpleValues.ForEach( sv => sb.Append( sv + <span style="color: #a31515">", " </span>) );
Response.Write( <span style="color: blue">string</span>.Format(
<span style="color: #a31515">"Name: {0}<br />Tags: {1}<br />Related Articles: "</span>,
article.Name,
sb.ToString() ) );
article.RelatedArticles.ForEach( ra =>
Response.Write( <span style="color: blue">string</span>.Format( <span style="color: #a31515">"{0},"</span>, ra.Name ) ) );
Response.Write( <span style="color: #a31515">"<hr />" </span>);
}
}
</pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>And after all this, I am pleased to find a result looking like:</p>
<p> </p>
<p>Name: Ben Hur<br>Tags: O2 Arena, Theater, <br>Related Articles: Michael Jackson,Britney Spears,Depeche Mode,Nickelback,Pet Shop Boys,War of the Worlds,
<hr>
<strong>Name: Britney Spears<br>Tags: Pop, Concert, O2 Arena, <br>Related Articles: <u>Michael Jackson</u>,<u>Pet Shop Boys</u>,Depeche Mode,Nickelback,Ben Hur,War of the Worlds, </strong>
<hr>
Name: Depeche Mode<br>Tags: O2 Arena, Concert, Alternative, <br>Related Articles: Britney Spears,Michael Jackson,Nickelback,Pet Shop Boys,War of the Worlds,Ben Hur,
<hr>
<strong>Name: Michael Jackson<br>Tags: Pop, Concert, O2 Arena, <br>Related Articles: <u>Britney Spears</u>,<u>Pet Shop Boys</u>,Depeche Mode,Nickelback,Ben Hur,War of the Worlds, </strong>
<hr>
Name: Nickelback<br>Tags: Rock, Concert, O2 Arena, <br>Related Articles: Britney Spears,Depeche Mode,Pet Shop Boys,Michael Jackson,Ben Hur,War of the Worlds,
<hr>
<strong>Name: Pet Shop Boys<br>Tags: O2 Arena, Concert, Pop, <br>Related Articles: <u>Britney Spears</u>,<u>Michael Jackson</u>,Depeche Mode,Nickelback,War of the Worlds,Ben Hur, </strong>
<hr>
Name: War of the Worlds<br>Tags: O2 Arena, Musical, <br>Related Articles: Ben Hur,Britney Spears,Depeche Mode,Nickelback,Pet Shop Boys,Michael Jackson,
<hr>
<p> </p>
<p>The first thing that strikes me is; my meta data and test data probably aren’t extensive enough to really see this functionality in full effect. They all look almost the same.</p>
<p> </p>
<p>However, I can determine that it works as expected. “Britney Spears”, “Michael Jackson” and “Pet Shop Boys” all share the same 3 meta tags. They SHOULD in all instances suggest the “one left out” on top of the list as “Related Articles”. And they all do; I’ve marked them in bold and underline. Also note that the “Depeche Mode” concert in O2 Arena lists other concerts (although of different music genre) before it proceeds to list the musicals and theatre plays.</p>
<p> </p>
<p>It works :-)</p>
<p> </p>
<h2>A few notes on performance</h2>
<p>In this post, I’ve deliberately not focused excessively on performance implications. Don’t worry – it’s not at all bad. But in “real life”; there are still obvious places in this code where you could potentially gain a significant amount of performance. As everyone will know; I/O operations are by an order of magnitude some of the most expensive calls we can make, and there is definitely a few places you could set in here.</p>
<p> </p>
<p>A few suggestions I would look into if I were to take this code live:</p>
<p> </p>
<ul>
<li>Code up a TagController; that will eventually act as a cache for all the tags in your solution. Load up the tags only once, and don’t repeatedly re-load them in your loops.
<li>In this case, bypass the very convenient .GetReferrers() method provided by DomainObjects and go through the extra work of working with the LinkDatabase directly yourself. For this part of the algorithm (counting up how many times a given ID is referencing your tag), you don’t really need to load up the Sitecore Item – something .GetReferrers() will automatically do. I will put this on the TODO list for DomainObjects.
<li>And – as ALWAYS – don’t forget to configure caching for whatever sublayouts and/or user controls you are calling this functionality on.</li></ul>
<p> </p>
<p>That’s it for this time. I hope you found this useful :-)</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com0tag:blogger.com,1999:blog-20883010.post-80663992573209259102009-05-13T02:04:00.001+02:002009-05-13T02:04:35.981+02:00Working with web.config include files in Sitecore 6<p>In my previous post about <a title="Working with multiple content databases" href="http://www.cassidy.dk/blog/sitecore/2009/05/working-with-multiple-content-databases.html">Working with multiple content databases</a>; Lars Floe Nielsen made a comment about something I’ve been meaning to write about for a long time.</p> <p> </p> <p>Configuration files. Such a pain, aren’t they?</p> <p> </p> <p>Anyone who has ever stepped through 6 Sitecore upgrades and meticulously stepped through the web.config change instructions line by line will know what I mean. Would be so much easier to just <em>replace</em> your web.config with the one matching the latest Sitecore version you were upgrading to.</p> <p> </p> <p>Or what about your environments? Dev environment, Staging environment, Live environment, Slave server environment? All with different configuration settings. This has already <a title="Multi environment config" href="http://adeneys.wordpress.com/2009/04/17/multi-environment-config/">been blogged about</a>, and I am not going to dig particularly deep into this topic in this post.</p> <p> </p> <p>Starting from Sitecore 6 (actually, V5, but I’ve had a very hard time tracking more information down on it than can be found in <a title="Configuration Changes in 5.3" href="http://alexeyrusakov.com/sitecoreblog/2006/10/04/Configuration+Changes+In+53.aspx">Alexeys post on the matter</a>), Sitecore actually introduced a really neat new functionality. It’s called “Web Config Patching”, but to be honest I don’t personally like the term “patching” being used in this context, even if this IS technically what the functionality does.</p> <p> </p> <p>So far, I have not really been able to locate much in terms of official documentation on this subject (<a title="Search SDN for config include" href="http://sdn.sitecore.net/searchresult.aspx?q=config%20include">searching SDN</a> directly provides very few clues), so most of my knowledge on it comes from personal experience, chatting with other Sitecore consultants/investigators, studying other configuration include files and spiced with generous dosages of Reflector.</p> <p> </p> <p>In the “<a title="What's new in Sitecore 6" href="http://sdn5.sitecore.net/upload/sitecore6/whatsnew-a4.pdf">What’s new</a>” released for Sitecore 6, the functionality gets the following mention:</p> <p> </p> <p>“Previous versions of Sitecore CMS forced administrators to make direct changes to configuration settings in the web.config file manually. This led to challenges locating local configuration changes as opposed to modifications made by Sitecore when upgrading to a new version of Sitecore. Sitecore 6 offers a smart solution: web.config modifications can now be made in a separate XML file, stored under the /App_Config/Include folder, which Sitecore reads in at startup time after loading the web.config file. The folder contains several example files which illustrate how to use this feature. The Sitecore 6 configuration factory reads the include config files”</p> <p> </p> <p>The information appears out of date however, and no such “example files” can be found in any version of Sitecore 6 I have had my hands on.</p> <p> </p> <p>Anyway. On we go.</p> <p> </p> <h2>So how and where does it work?</h2> <p>To make good use of config includes, one must first understand how Sitecore implements it. And to get some idea of this, one must know a little bit about how a web.config file is organised.</p> <p> </p> <p>If you open up a standard Sitecore web.config and look near the top, the first thing you will see will be looking something like this:</p><pre class="code"><span style="color: blue"><?</span><span style="color: #a31515">xml </span><span style="color: red">version</span><span style="color: blue">=</span>"<span style="color: blue">1.0</span>" <span style="color: red">encoding</span><span style="color: blue">=</span>"<span style="color: blue">utf-8</span>"<span style="color: blue">?>
<</span><span style="color: #a31515">configuration</span><span style="color: blue">>
<</span><span style="color: #a31515">configSections</span><span style="color: blue">>
<</span><span style="color: #a31515">section </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">sitecore</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Configuration.ConfigReader, Sitecore.Kernel</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">section </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">log4net</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">log4net.Config.Log4NetConfigurationSectionHandler,
Sitecore.Logging</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">sectionGroup </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">system.web.extensions</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">System.Web.Configuration.SystemWebExtensionsSectionGroup,
System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35</span>"<span style="color: blue">>
<</span><span style="color: #a31515">sectionGroup </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">scripting</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">System.Web.Configuration.ScriptingSectionGroup,
System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35</span>"<span style="color: blue">>
<</span><span style="color: #a31515">section </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">scriptResourceHandler</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">System.Web.Configuration.ScriptingScriptResourceHandlerSection,
System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35</span>"
<span style="color: red">requirePermission</span><span style="color: blue">=</span>"<span style="color: blue">false</span>" <span style="color: red">allowDefinition</span><span style="color: blue">=</span>"<span style="color: blue">MachineToApplication</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">sectionGroup </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">webServices</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">System.Web.Configuration.ScriptingWebServicesSectionGroup,
System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35</span>"<span style="color: blue">>
<</span><span style="color: #a31515">section </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">jsonSerialization</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">System.Web.Configuration.ScriptingJsonSerializationSection,
System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35</span>"
<span style="color: red">requirePermission</span><span style="color: blue">=</span>"<span style="color: blue">false</span>" <span style="color: red">allowDefinition</span><span style="color: blue">=</span>"<span style="color: blue">Everywhere</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">section </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">profileService</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">System.Web.Configuration.ScriptingProfileServiceSection,
System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35</span>"
<span style="color: red">requirePermission</span><span style="color: blue">=</span>"<span style="color: blue">false</span>" <span style="color: red">allowDefinition</span><span style="color: blue">=</span>"<span style="color: blue">MachineToApplication</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">section </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">authenticationService</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">System.Web.Configuration.ScriptingAuthenticationServiceSection,
System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35</span>"
<span style="color: red">requirePermission</span><span style="color: blue">=</span>"<span style="color: blue">false</span>" <span style="color: red">allowDefinition</span><span style="color: blue">=</span>"<span style="color: blue">MachineToApplication</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">section </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">roleService</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">System.Web.Configuration.ScriptingRoleServiceSection,
System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35</span>"
<span style="color: red">requirePermission</span><span style="color: blue">=</span>"<span style="color: blue">false</span>" <span style="color: red">allowDefinition</span><span style="color: blue">=</span>"<span style="color: blue">MachineToApplication</span>" <span style="color: blue">/>
</</span><span style="color: #a31515">sectionGroup</span><span style="color: blue">>
</</span><span style="color: #a31515">sectionGroup</span><span style="color: blue">>
</</span><span style="color: #a31515">sectionGroup</span><span style="color: blue">>
</</span><span style="color: #a31515">configSections</span><span style="color: blue">>
</span></pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>What is declared here, are the different Configuration Sections that ASP.NET can expect to find in the configuration file. Some of them are there to support ASP.NET, and some of them are put in there by Sitecore. You can learn more about the <a title="Format of ASP.NET Configuration Files" href="http://msdn.microsoft.com/en-us/library/ackhksh7(vs.71).aspx">format of ASP.NET configuration files here</a>.</p>
<p> </p>
<p>Basically, what this then means is, that various “top level” configuration sections can be expected to appear in the web.config file we are looking at, and ASP.NET will (via the “type” attribute) know how to parse them. For normal every day use, most of us have probably been able to just use <appSettings> for whatever configuration we needed – but for configuring a complex application such as Sitecore, this just won’t be enough. Fortunately this is why ASP.NET allows us to create our <a title="Creating New Configuration Sections" href="http://msdn.microsoft.com/en-us/library/2tw134k3(VS.71).aspx">own configuration sections with our own configuration handlers</a>; and that is exactly what Sitecore has been doing for a very long time.</p>
<p> </p>
<p>Now. Keeping in mind what I wrote above; Sitecore came up with a system that allows the include of configuration files. Tying that into what we just learned; to find and use this functionality we must then look in the config section that Sitecore provides. Not surprisingly, this section is called <sitecore> and this is where you configure the vast majority of what you need to do, to get your Sitecore installation up and running the way you want it.</p>
<p> </p>
<p><strong><u>Config Include only works in this configuration section</u></strong></p>
<p> </p>
<p>First thing to keep in mind, when using this technology.</p>
<p> </p>
<p>This means it won’t work for <appSetting> configuration settings. Don’t worry about it – Sitecore has a perfectly good replacement for it; I’ll demonstrate in a bit.</p>
<p> </p>
<h2>How to set it up?</h2>
<p>Here’s a bit of good news. There’s nothing really to set up. Sitecore comes with this functionality enabled out of the box, and all you need to do is to tap into it and use it.</p>
<p> </p>
<p>If you open up Windows Explorer and navigate to /Website/App_Config/Include, you will (probably) find an empty folder. This is a directory that Sitecore is actively watching, for any additions <em>or changes</em> to it’s base web.config file. Remember I said how it was not fully correct to call this “config include”? This is because Sitecore actually offers more than just including more configuration files; it also allows you to <em>edit existing configuration data defined in web.config</em>. As long as it sits in the <sitecore> section :-)</p>
<p> </p>
<p>As so often before when I am testing something; I create a new .ASPX file (with codebehinds) in the root of my website; I name it “TestInclude.aspx”, and I type the following code into the class Visual Studio generates for me:</p><pre class="code"><span style="color: blue">public partial class </span><span style="color: #2b91af">TestInclude </span>: System.Web.UI.<span style="color: #2b91af">Page
</span>{
<span style="color: blue">protected void </span>Page_Load( <span style="color: blue">object </span>sender, <span style="color: #2b91af">EventArgs </span>e )
{
Response.Write( <span style="color: #a31515">"The value of setting 'TestInclude' is: " </span>+
Sitecore.Configuration.<span style="color: #2b91af">Settings</span>.GetSetting( <span style="color: #a31515">"TestInclude"</span>, <span style="color: #a31515">"Undefined" </span>) );
}
}
</pre><a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>At this point, the result I get when running the page is entirely as expected; “The value of setting 'TestInclude' is: Undefined”</p>
<p> </p>
<p>Notice how the Sitecore API equivalent is much more elegant than the ASP.NET standard handling which would achieve the above in the <appSettings> section.</p><pre class="code"><span style="color: blue">string </span>val;
<span style="color: blue">if </span>( System.Configuration.<span style="color: #2b91af">ConfigurationManager</span>.AppSettings[ <span style="color: #a31515">"TestInclude" </span>] != <span style="color: blue">null </span>)
val = System.Configuration.<span style="color: #2b91af">ConfigurationManager</span>.AppSettings[ <span style="color: #a31515">"TestInclude" </span>];
<span style="color: blue">else
</span>val = <span style="color: #a31515">"Undefined"</span>;
</pre>
<p><a href="http://11011.net/software/vspaste"></a> </p>
<p>But we’re not there yet. I then proceed to create a “New File” in the folder I mentioned above; /App_Config/Include and name it “TestInclude.config”</p><pre class="code"><span style="color: blue"><?</span><span style="color: #a31515">xml </span><span style="color: red">version</span><span style="color: blue">=</span>"<span style="color: blue">1.0</span>" <span style="color: red">encoding</span><span style="color: blue">=</span>"<span style="color: blue">utf-8</span>" <span style="color: blue">?>
<</span><span style="color: #a31515">configuration </span><span style="color: red">xmlns:patch</span><span style="color: blue">=</span>"<span style="color: blue">http://www.sitecore.net/xmlconfig/</span>"<span style="color: blue">>
<</span><span style="color: #a31515">sitecore</span><span style="color: blue">>
<</span><span style="color: #a31515">settings</span><span style="color: blue">>
<</span><span style="color: #a31515">setting </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">TestInclude</span>" <span style="color: red">value</span><span style="color: blue">=</span>"<span style="color: blue">This value comes from TestInclude.config</span>"<span style="color: blue">/>
</</span><span style="color: #a31515">settings</span><span style="color: blue">>
</</span><span style="color: #a31515">sitecore</span><span style="color: blue">>
</</span><span style="color: #a31515">configuration</span><span style="color: blue">></span></pre><a href="http://11011.net/software/vspaste"></a>
<p>I run my .ASPX page again; and this time I get the result I was hoping for. “The value of setting 'TestInclude' is: This value comes from TestInclude.config”.</p>
<p> </p>
<p>Great! :-) Things are working as expected. And I now have my own configuration files in a nice isolated area that can be easily packaged and deployed WITHOUT needing to worry (much) about the version of Sitecore that may be in place; and without needing to touch the original web.config in any way what so ever.</p>
<p> </p>
<p>There’s another benefit; or at least in a majority of cases this is a benefit. Making modifications to your config include files take effect (almost) instantly and <em>do not recycle your application pool</em>.</p>
<p> </p>
<p><strong><u>Updating your config files will not force your website to reset</u></strong></p>
<p> </p>
<p>Another important fact to keep in mind. For better and (sometimes) for worse.</p>
<p> </p>
<p>Notice how this is not limited to work with only <settings>. Anything in the Sitecore configuration structure can be added in your include file. If I wanted to add a new XSL helper, for instance, I would expand my file like this:</p><pre class="code"><span style="color: blue"><?</span><span style="color: #a31515">xml </span><span style="color: red">version</span><span style="color: blue">=</span>"<span style="color: blue">1.0</span>" <span style="color: red">encoding</span><span style="color: blue">=</span>"<span style="color: blue">utf-8</span>" <span style="color: blue">?>
<</span><span style="color: #a31515">configuration </span><span style="color: red">xmlns:patch</span><span style="color: blue">=</span>"<span style="color: blue">http://www.sitecore.net/xmlconfig/</span>"<span style="color: blue">>
<</span><span style="color: #a31515">sitecore</span><span style="color: blue">>
<</span><span style="color: #a31515">xslExtensions</span><span style="color: blue">>
<</span><span style="color: #a31515">extension </span><span style="color: red">mode</span><span style="color: blue">=</span>"<span style="color: blue">on</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">CorePoint.XslHelpers.XslHelper, CorePoint.Library</span>"
<span style="color: red">namespace</span><span style="color: blue">=</span>"<span style="color: blue">http://www.corepoint-it.com/library/xslhelper</span>" <span style="color: red">singleInstance</span><span style="color: blue">=</span>"<span style="color: blue">true</span>" <span style="color: blue">/>
</</span><span style="color: #a31515">xslExtensions</span><span style="color: blue">>
<</span><span style="color: #a31515">settings</span><span style="color: blue">>
<</span><span style="color: #a31515">setting </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">TestInclude</span>" <span style="color: red">value</span><span style="color: blue">=</span>"<span style="color: blue">This value comes from TestInclude.config</span>"<span style="color: blue">/>
</</span><span style="color: #a31515">settings</span><span style="color: blue">>
</</span><span style="color: #a31515">sitecore</span><span style="color: blue">>
</</span><span style="color: #a31515">configuration</span><span style="color: blue">></span></pre>
<p><span style="color: blue"></span></p>
<p></p>
<p>One last thing to mention about these include files before proceeding is; you can have as many of them as you like. They need to end in .config, but other than that there are no limitations. You can even create sub folders to your App_Config/Include directory and place your .config files there if you prefer; they too will be picked up by Sitecore’s configuration system.</p>
<p> </p>
<h2>More advanced work with your config include files</h2>
<p>In the example I just went through, I adeptly (or maybe not…) skipped explaining part of the reason the config include file I created looks the way it does. What I did was to work with the include system in it’s simplest form. If you picture in your mind your original web.config file, and then merge my XML on top of it; you have a pretty good idea of what I have just done.</p>
<p> </p>
<p>And this is fine; for settings. After all, a setting is a setting, and it matters not exactly WHERE in the configuration file the setting appears.</p>
<p> </p>
<p>But what about the times when it does matter? Like for Sitecore pipelines for instance; I can assure you the order of which these pipelines executes is NOT irrelevant. </p>
<p> </p>
<p>Positioning your configuration within the web.config is fortunately easily achieved. A few examples probably explain it better than I can type myself out of. So here goes.</p><pre class="code"><span style="color: blue"><?</span><span style="color: #a31515">xml </span><span style="color: red">version</span><span style="color: blue">=</span>"<span style="color: blue">1.0</span>" <span style="color: red">encoding</span><span style="color: blue">=</span>"<span style="color: blue">utf-8</span>" <span style="color: blue">?>
<</span><span style="color: #a31515">configuration </span><span style="color: red">xmlns:patch</span><span style="color: blue">=</span>"<span style="color: blue">http://www.sitecore.net/xmlconfig/</span>"<span style="color: blue">>
<</span><span style="color: #a31515">sitecore</span><span style="color: blue">>
<</span><span style="color: #a31515">pipelines</span><span style="color: blue">>
<</span><span style="color: #a31515">httpRequestBegin</span><span style="color: blue">>
<!-- </span><span style="color: green">Insert own pipeline processor as the first element of the pipeline </span><span style="color: blue">-->
<</span><span style="color: #a31515">processor </span><span style="color: red">patch:before</span><span style="color: blue">=</span>"<span style="color: blue">*[1]</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">CorePoint.Tracking.RequestTracker, CorePoint.Library</span>" <span style="color: blue">/>
<!-- </span><span style="color: green">Insert own pipeline processor right after the Language Resolver </span><span style="color: blue">-->
<</span><span style="color: #a31515">processor </span><span style="color: red">patch:after</span><span style="color: blue">=</span>"<span style="color: blue">*[@type='Sitecore.Pipelines.HttpRequest.LanguageResolver, Sitecore.Kernel']</span>"
<span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">CorePoint.Tracking.LanguageTracker, CorePoint.Library</span>" <span style="color: blue">/>
</span><span style="color: blue"> </</span><span style="color: #a31515">httpRequestBegin</span><span style="color: blue">>
</</span><span style="color: #a31515">pipelines</span><span style="color: blue">>
<</span><span style="color: #a31515">settings</span><span style="color: blue">>
<</span><span style="color: #a31515">setting </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">TestInclude</span>" <span style="color: red">value</span><span style="color: blue">=</span>"<span style="color: blue">This value comes from TestInclude.config</span>"<span style="color: blue">/>
</</span><span style="color: #a31515">settings</span><span style="color: blue">>
</</span><span style="color: #a31515">sitecore</span><span style="color: blue">>
</</span><span style="color: #a31515">configuration</span><span style="color: blue">>
</span></pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>As you can probably see, fairly advanced stuff can be done with configuration files. Most of this syntax and form I have exclusively from Reflector use, and <strong><em>I may not have it spot on correct</em></strong>. Finding official documentation on this topic has proven to be next to impossible. Except for lots of references on various comments around the web (recommended practise is to use config includes or “auto-includes” as they are also called) of course – but knowing HOW to use them is what this post is all about :-)</p>
<p> </p>
<p>I would love to know how one can:</p>
<ol>
<li><font color="#333333">Remove an existing configuration entirely</font></li>
<li><font color="#333333">Replace an existing functionality entirely</font></li></ol>
<p> </p>
<p>Both seem possible from digging around in Reflector – but given that this is actually a fairly involved process to test (at 2am in the morning), I chose to let the matter rest for this time. I will get back with an update if and when I learn more.</p>
<p> </p>
<h2>In summary</h2>
<ul>
<li>Configuration include files is probably one of the features I personally like very much from an operational perspective in Sitecore 6</li>
<li>The functionality is way under-documented; but hopefully now this post can help you get started</li>
<ul>
<li>So please; no more 3-page documents describing how to “merge” your configuration into web.config for <insert your module/functionality name here> :P</li></ul>
<li>You can modify config include files without resetting your website AppPool</li>
<li>And lastly, it only works in the <sitecore> configuration section. Don’t attempt it for <system.web> or <system.webserver> for instance, it won’t work.</li></ul> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com6tag:blogger.com,1999:blog-20883010.post-65277552522677930692009-05-08T18:41:00.001+02:002009-05-08T18:41:32.654+02:00Working with multiple content databases in Sitecore 6<p>One of the very neat things about Sitecore, is the way the architecture allows you to mould, shape, and work with the configuration files to come up with an implementation that suits your purpose.</p> <p> </p> <p>As the title of this post will suggest, I will be taking a look at Sitecore databases in this post; and how you are free to work with as many of them as you see fit in your projects.</p> <p> </p> <p>For sake of argument, let’s say that you were tasked with expanding an existing Sitecore website with a Products database. Potentially, this database would be holding tens-of-thousands of products – at least if you are to believe the PowerPoint slides of sales projections the CEO presented last week ;-)</p> <p> </p> <p>Now I KNOW what the first argument would be; “Don’t store in Sitecore. Sitecore is meant to build and store websites, and something as “businessey” as a Products Database has no place there”. I beg to differ however – as long as we’re not assuming there are ERP systems involved; we’re starting entirely from scratch.</p> <p> </p> <p>I find, that actually, Sitecore is perfect for the job. Just in short summary, by using Sitecore as our data platform, we get (at the very least) the following handed to us on a silver platter:</p> <p> </p> <ul> <li>Flexible hierarchical storage structure</li> <li>Multi-lingual meta data for product descriptions and so on</li> <li>Built-in advanced media library and media handling</li> <li>Easily modelled data templates</li> <li>Standard stuff, like workflows, security and so on</li> <li>Can be edited and maintained using familiar tools</li> <ul> <li>Don’t overlook this one. If you place the data in “traditional” SQL tables – YOU are going to need to write an interface that creates, edits and maintains your product data</li> <li>WHAT are you going to say, when the customer asks for “advanced” stuff such as Workflows, Automatic Image Scaling / Thumbnail creation, granular (field based) security, Publishing functionality, Spell checking… ? Just naming a few here, but let’s not be blind to what Sitecore is offering out of the box</li> <li>What will it cost?</li></ul></ul> <p> </p> <p>So just bear with me here. Am not saying that every case is a case for data going into Sitecore and “living” there. But what I am saying is, it’s not something that should be discarded as an option without further investigation. Like everything software, there are tradeoffs involved. Make sure you make the right trade.</p> <p> </p> <h2>Setting it up</h2> <p>Right. So let’s get started.</p> <p> </p> <p>In my example here, I downloaded a fresh copy of <a title="ZIP archive of the Sitecore CMS site root" href="http://sdn.sitecore.net/downloads/sitecore602rev090416.download">Sitecore 090416</a> (ZIP archive of the web root, we’re all developers here. The Installer is for marketers ;-))</p> <p> </p> <p>I’m going to be using SQL Server Express, so I get rid of the Oracle and SQL 2000 files. For my Products Database, I will be using the Sitecore “Master” database as a foundation, so I take a copy of the files and rename them like this:</p> <p> </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0uEkmzQliOhLMYg5GGeEN-KzQRyv2Mq5_HaIRm4m8ZW0tmZ40pGJwJpGaL6vDPw7SYsRP-NbOQHTR7ZzjibQka47ptY3NIu_26d2oXo_B7KiOC9Axnm7Zx7BDjU7J-Tfm14LU/s1600-h/copy%5B3%5D.png"><img title="copy" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="95" alt="copy" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRQDZtcPsUa5Ng2cONAmayE7-3Z65K9hp7OK7DRUIi2hxr6W0sARFXdeNgSR0GGY_4WPoeS0HdBDGEzJ9Eqek5CePLSHE7qU_lpdNwigoLmcO4zbEKRiC_37JG6CQO263vv1bX/?imgmax=800" width="244" border="0"></a> </p> <p> </p> <p>And then I proceed to attach them:</p> <p></p> <p> </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjH4pa_gt1uyLm_LDWrjvIytuJfuh_RkjBpnWfbPKXeR1HFisR3xMjcWiOPBIdULWYXjWyCz3PpUyEQ5O_aJBIWLOIBxQRaYl7Rsy71G5gXpErmWG7fNWOqSH9xLnIg6dCFlk6n/s1600-h/attach%5B3%5D.png"><img title="attach" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="219" alt="attach" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1gYsZsL6PXgD3SF19A5_8lder6q31mOouhYkPoPlq6iwbUE5x6ZfqTGk0nmEv16g3lYcmYXy47hQhiCM3Brrcd3M9x8i4g3UQMWQJGiaoBK-uSFkg_W6KsQGaZudVL-5Hgioi/?imgmax=800" width="244" border="0"></a> </p> <p> </p> <p>And eventually end up with 4 databases attached, like this:</p> <p> </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRVdDW9a-cv86UjXUFx0jjuYl8gbL8WssUtRBfhytR_usdnTmHp78fe24I9uy6mE8NBIgc5FL99x50_bdLeiH0KQJc9cBnnzB4BvKJY-1uRklVG3rvimhYdn9hKbVhAzW5-8i_/s1600-h/databases%5B3%5D.png"><img title="databases" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="79" alt="databases" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwS693ko5TKh8G-B_1mONo3mFE4cXPTQ1CF0pZq1qX-64RBedigvjA9_HTqJTyuIUJUTTqoKoKjMBICQd_LdxGVoBPzWpNdNBW8fWtt6NPLfCF_8ysOEfBKy1bjy8SsN_97VVw/?imgmax=800" width="206" border="0"></a> </p> <p> </p> <p>So far so good. I continue to set up an IIS site for this, and a local host header of “sc090416”. All of this you hopefully know all about, so I won’t go into detail with it here. The point of this post is not basic Sitecore installation – we’re all here to look at databases ;-)</p> <p> </p> <p>A few things that you need to do, which you wouldn’t normally, is to configure our new Products Database in Sitecore. First, open up /Website/App_Config/ConnectionStrings.config and configure the extra database. It could look like this:</p> <p><span style="color: blue"><br><?</span><span style="color: #a31515">xml </span><span style="color: red">version</span><span style="color: blue">=</span>"<span style="color: blue">1.0</span>" <span style="color: red">encoding</span><span style="color: blue">=</span>"<span style="color: blue">utf-8</span>"<span style="color: blue">?><br><</span><span style="color: #a31515">connectionStrings</span><span style="color: blue">><br> <!-- <br> </span><span style="color: green">Sitecore connection strings.<br> All database connections for Sitecore are configured here.<br> </span><span style="color: blue">--><br> <</span><span style="color: #a31515">add </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">core</span>" <span style="color: red">connectionString</span><span style="color: blue">=</span>"<span style="color: blue">user id=sa;password=removed;Data Source=.\SQLEXPRESS;Database=sc090416_Core</span>" <span style="color: blue">/><br> <</span><span style="color: #a31515">add </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">master</span>" <span style="color: red">connectionString</span><span style="color: blue">=</span>"<span style="color: blue">user id=sa;password=removed;Data Source=.\SQLEXPRESS;Database=sc090416_Master</span>" <span style="color: blue">/><br> <</span><span style="color: #a31515">add </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">web</span>" <span style="color: red">connectionString</span><span style="color: blue">=</span>"<span style="color: blue">user id=sa;password=removed;Data Source=.\SQLEXPRESS;Database=sc090416_Web</span>" <span style="color: blue">/><br><br> <</span><span style="color: #a31515">add </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">products</span>" <span style="color: red">connectionString</span><span style="color: blue">=</span>"<span style="color: blue">user id=sa;password=removed;Data Source=.\SQLEXPRESS;Database=sc090416_Products</span>" <span style="color: blue">/><br></</span><span style="color: #a31515">connectionStrings</span><span style="color: blue">><br></span></p><a href="http://11011.net/software/vspaste"></a> <p>Very straight forward, so far. But we’re not done yet. Open up Web.Config, look for the <databases> element, and find <!—master —>. For now, just copy the entire section – like this:</p><pre class="code">! <span style="color: blue"><!-- </span><span style="color: green"><strong>products</strong> –</span><span style="color: blue">>
! <</span><span style="color: #a31515">database </span><span style="color: red">id</span><span style="color: blue">=</span>"<span style="color: blue"><strong>products</strong></span>" <span style="color: red">singleInstance</span><span style="color: blue">=</span>"<span style="color: blue">true</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Data.Database, Sitecore.Kernel</span>"<span style="color: blue">>
<</span><span style="color: #a31515">param </span><span style="color: red">desc</span><span style="color: blue">=</span>"<span style="color: blue">name</span>"<span style="color: blue">></span>$(id)<span style="color: blue"></</span><span style="color: #a31515">param</span><span style="color: blue">>
<</span><span style="color: #a31515">icon</span><span style="color: blue">></span>People/16x16/cubes_blue.png<span style="color: blue"></</span><span style="color: #a31515">icon</span><span style="color: blue">>
<</span><span style="color: #a31515">dataProviders </span><span style="color: red">hint</span><span style="color: blue">=</span>"<span style="color: blue">list:AddDataProvider</span>"<span style="color: blue">>
<</span><span style="color: #a31515">dataProvider </span><span style="color: red">ref</span><span style="color: blue">=</span>"<span style="color: blue">dataProviders/main</span>" <span style="color: red">param1</span><span style="color: blue">=</span>"<span style="color: blue">$(id)</span>"<span style="color: blue">>
<</span><span style="color: #a31515">prefetch </span><span style="color: red">hint</span><span style="color: blue">=</span>"<span style="color: blue">raw:AddPrefetch</span>"<span style="color: blue">>
<</span><span style="color: #a31515">sc.include </span><span style="color: red">file</span><span style="color: blue">=</span>"<span style="color: blue">/App_Config/Prefetch/Common.config</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">sc.include </span><span style="color: red">file</span><span style="color: blue">=</span>"<span style="color: blue">/App_Config/Prefetch/Master.config</span>" <span style="color: blue">/>
</</span><span style="color: #a31515">prefetch</span><span style="color: blue">>
</</span><span style="color: #a31515">dataProvider</span><span style="color: blue">>
</</span><span style="color: #a31515">dataProviders</span><span style="color: blue">>
<</span><span style="color: #a31515">securityEnabled</span><span style="color: blue">></span>true<span style="color: blue"></</span><span style="color: #a31515">securityEnabled</span><span style="color: blue">>
<</span><span style="color: #a31515">proxiesEnabled</span><span style="color: blue">></span>false<span style="color: blue"></</span><span style="color: #a31515">proxiesEnabled</span><span style="color: blue">>
<</span><span style="color: #a31515">publishVirtualItems</span><span style="color: blue">></span>true<span style="color: blue"></</span><span style="color: #a31515">publishVirtualItems</span><span style="color: blue">>
<</span><span style="color: #a31515">proxyDataProvider </span><span style="color: red">ref</span><span style="color: blue">=</span>"<span style="color: blue">proxyDataProviders/main</span>" <span style="color: red">param1</span><span style="color: blue">=</span>"<span style="color: blue">$(id)</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">workflowProvider </span><span style="color: red">hint</span><span style="color: blue">=</span>"<span style="color: blue">defer</span>" <span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Workflows.Simple.WorkflowProvider, Sitecore.Kernel</span>"<span style="color: blue">>
<</span><span style="color: #a31515">param </span><span style="color: red">desc</span><span style="color: blue">=</span>"<span style="color: blue">database</span>"<span style="color: blue">></span>$(id)<span style="color: blue"></</span><span style="color: #a31515">param</span><span style="color: blue">>
<</span><span style="color: #a31515">param </span><span style="color: red">desc</span><span style="color: blue">=</span>"<span style="color: blue">history store</span>" <span style="color: red">ref</span><span style="color: blue">=</span>"<span style="color: blue">workflowHistoryStores/main</span>" <span style="color: red">param1</span><span style="color: blue">=</span>"<span style="color: blue">$(id)</span>" <span style="color: blue">/>
</</span><span style="color: #a31515">workflowProvider</span><span style="color: blue">>
<</span><span style="color: #a31515">indexes </span><span style="color: red">hint</span><span style="color: blue">=</span>"<span style="color: blue">list:AddIndex</span>"<span style="color: blue">>
<</span><span style="color: #a31515">index </span><span style="color: red">path</span><span style="color: blue">=</span>"<span style="color: blue">indexes/index[@id='system']</span>" <span style="color: blue">/>
</</span><span style="color: #a31515">indexes</span><span style="color: blue">>
<</span><span style="color: #a31515">archives </span><span style="color: red">hint</span><span style="color: blue">=</span>"<span style="color: blue">raw:AddArchive</span>"<span style="color: blue">>
<</span><span style="color: #a31515">archive </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">archive</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">archive </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">recyclebin</span>" <span style="color: blue">/>
</</span><span style="color: #a31515">archives</span><span style="color: blue">>
<</span><span style="color: #a31515">Engines.HistoryEngine.Storage</span><span style="color: blue">>
<</span><span style="color: #a31515">obj </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Data.$(database).$(database)HistoryStorage, Sitecore.Kernel</span>"<span style="color: blue">>
<</span><span style="color: #a31515">param </span><span style="color: red">connectionStringName</span><span style="color: blue">=</span>"<span style="color: blue">$(id)</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">EntryLifeTime</span><span style="color: blue">></span>30.00:00:00<span style="color: blue"></</span><span style="color: #a31515">EntryLifeTime</span><span style="color: blue">>
</</span><span style="color: #a31515">obj</span><span style="color: blue">>
</</span><span style="color: #a31515">Engines.HistoryEngine.Storage</span><span style="color: blue">>
<</span><span style="color: #a31515">Engines.HistoryEngine.SaveDotNetCallStack</span><span style="color: blue">></span>false<span style="color: blue"></</span><span style="color: #a31515">Engines.HistoryEngine.SaveDotNetCallStack</span><span style="color: blue">>
<</span><span style="color: #a31515">cacheSizes </span><span style="color: red">hint</span><span style="color: blue">=</span>"<span style="color: blue">setting</span>"<span style="color: blue">>
<</span><span style="color: #a31515">data</span><span style="color: blue">></span>20MB<span style="color: blue"></</span><span style="color: #a31515">data</span><span style="color: blue">>
<</span><span style="color: #a31515">items</span><span style="color: blue">></span>10MB<span style="color: blue"></</span><span style="color: #a31515">items</span><span style="color: blue">>
<</span><span style="color: #a31515">paths</span><span style="color: blue">></span>500KB<span style="color: blue"></</span><span style="color: #a31515">paths</span><span style="color: blue">>
<</span><span style="color: #a31515">standardValues</span><span style="color: blue">></span>500KB<span style="color: blue"></</span><span style="color: #a31515">standardValues</span><span style="color: blue">>
</</span><span style="color: #a31515">cacheSizes</span><span style="color: blue">>
</</span><span style="color: #a31515">database</span><span style="color: blue">>
</span></pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>Right. The only changes I made to this copy, are marked on the lines with !. Essentially the only thing changing are references to “master” which now become “products”.</p>
<p> </p>
<p>With this change, I am now ready to log into Sitecore for the first time and check that everything is in order.</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVFCpq4UqSUoUq_UrSUic_PNCvlNCHOluyn7yZd0TQHKqJl1Wz9SlKX1Zrm7p305z7XQczwTiUCUTpr4tgtt7tmy8mZv6C3qJuM-9TexTkbyzvlzMuBd0VJmkHbzDOzQLqJ3gm/s1600-h/products%5B2%5D.png"><img title="products" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="172" alt="products" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsFMU1QfaT-WY2DhbmN1VMSBttU_FF3HbnjMidyDypV7nGt3zxNyMVHwo57jqPaZYK5cehoVt0Poq3aku5xDqWoV6DlGscjWK9wd-LmJZQlfT6tYbbgDA-urGagwIVUMssAcNz/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>So far, everything is looking good. Sitecore has recognised my new database. I can switch to it – and you know… it looks just like the “master” database ;-) At this point, this should not really be a surprise.</p>
<p> </p>
<h2>Testing it</h2>
<p> </p>
<p>To further test things, I create a couple of content items. In the “master” database, I delete the /Home node, and create:</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1VikOVst67ImAjovTHDrLZix1xZZ0Y5Pr_2RsG8dOi_semYhbXatA9RagJ2N2hHKDVs4ffJCRcpBv23se6RSJxkNw5xzLGwjZj48ltZmNr2L_Oh3ycbFM8SPeZ6cF0lN63W67/s1600-h/master%5B3%5D.png"><img title="master" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="204" alt="master" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaAfW9z1idjAe8AC3GUDsWOnCL-mR8q4JVr1pexu_h8d_BBAEDvfifnBnwENvxfqSNxUNMYN_XrJoDoMqIMp9RSd2GO2q3pbsYi8IAK5QHUK1rbcyKsC_vlDaxSCeZI6_9rloT/?imgmax=800" width="221" border="0"></a> </p>
<p> </p>
<p>I then switch to the “products” database, and create a similar (yet different) folder.</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcxVPtOYqpTveofWaM7UEa9LyFTgUKiE7ku5sw1qwgjwuyw5W-oNKKyf3Hsi1TwbSMqK38DJK2eFz8X3Ez5NdgTrP-kl1PWZhXKi5OxBN7F2Vfza6inWRI8TYyMDJkJ_k47ofG/s1600-h/product2%5B3%5D.png"><img title="product2" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="170" alt="product2" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgw5oNgM-9yXssj7CXMICUsYMXTq0HlhtAJN7y6tinX1nbAW-urLnd-PsNn0ToHdfT2RHmRBWpFgS9lSMtPKym3YErnnVfkRyp5RcO7eRhrJenuOS5PiH_O5m-DgCcM7RUlC7Se/?imgmax=800" width="223" border="0"></a> </p>
<p> </p>
<p>Time to stop for a minute. Why did I delete /Home?</p>
<p> </p>
<p>Well here’s the thing. The Home node that “master” is "born” with, so to speak, is just a placeholder really. At least that’s how I see it. Right now, my concern is, that if we leave the /Home node in both databases – we will have two items in two different databases, but sharing the same ID. What happens if you edit it in one database – should it overwrite changes done in the other? While pursuing this question could be fun – I don’t really think this is a scenario Sitecore will support and I frankly don’t know what would happen. At this point I don’t much care to find out either :P</p>
<p> </p>
<p>So anyway.</p>
<p> </p>
<p>I have my two new folders, and I do a publish. Now at this point, there are a couple of things you would be expecting to see. Upon switching to the “web” database to have a look, I think I can pretty much guarantee that whatever you were expecting, it wasn’t this:</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNPiRfTg4zD9uXDHXTfDxSFLnXkFK4XYJcCccUZUitB8fEH5lN9xZwiu1UFnMff1BDVy7Z_4lLSzqMNVP1D5IaP2YbGbk84UbkTeMKrzXALIc_dxIRw0Gv3nB0YO5aoZ3aS2IV/s1600-h/web%5B2%5D.png"><img title="web" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="193" alt="web" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHzQd91M2kjqSi80J7o-ZxGNQYmlHG7oiquC-S2JjUWmPJYdE-aO7XcUBXUyp6xoCzAa6ykxdmxTy9T8r0ASeSRFGWWzxvFrV1A8-vL6Yf28DTctDiHA3Vk4uxMzKFolLgbVBl/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>Well ok. To be fair, maybe it was. But of all the things I personally expected when I first tried this, this was not the result I was hoping for and certainly not expecting ;-)</p>
<p> </p>
<p>So what is happening here?</p>
<p> </p>
<p>I guess, the most accurate answer would be, Sitecore isn’t really designed to work like this. While the concept of multiple databases IS certainly supported – you are supposed to use Proxy items to “merge” all of the data from “extra” databases (like our Products) into the main “master” database and then publish from there.</p>
<p> </p>
<p>This doesn’t answer the question however, what IS happening?</p>
<p> </p>
<p>Well I started investigating, and the first thing I looked into was the publishItem pipeline. Out of the box, it looks like this:</p><pre class="code"><span style="color: blue"><</span><span style="color: #a31515">publishItem </span><span style="color: red">help</span><span style="color: blue">=</span>"<span style="color: blue">Processors should derive from Sitecore.Publishing.Pipelines.PublishItem.PublishItemProcessor</span>"<span style="color: blue">>
<</span><span style="color: #a31515">processor </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Publishing.Pipelines.PublishItem.RaiseProcessingEvent, Sitecore.Kernel</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">processor </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Publishing.Pipelines.PublishItem.CheckVirtualItem, Sitecore.Kernel</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">processor </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Publishing.Pipelines.PublishItem.CheckSecurity, Sitecore.Kernel</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">processor </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Publishing.Pipelines.PublishItem.DetermineAction, Sitecore.Kernel</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">processor </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Publishing.Pipelines.PublishItem.PerformAction, Sitecore.Kernel</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">processor </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Publishing.Pipelines.PublishItem.RemoveUnknownChildren, Sitecore.Kernel</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">processor </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Publishing.Pipelines.PublishItem.MoveItems, Sitecore.Kernel</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">processor </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Publishing.Pipelines.PublishItem.RaiseProcessedEvent, Sitecore.Kernel</span>" <span style="color: red">runIfAborted</span><span style="color: blue">=</span>"<span style="color: blue">true</span>" <span style="color: blue">/>
<</span><span style="color: #a31515">processor </span><span style="color: red">type</span><span style="color: blue">=</span>"<span style="color: blue">Sitecore.Publishing.Pipelines.PublishItem.UpdateStatistics, Sitecore.Kernel</span>" <span style="color: red">runIfAborted</span><span style="color: blue">=</span>"<span style="color: blue">true</span>"<span style="color: blue">>
<</span><span style="color: #a31515">traceToLog</span><span style="color: blue">></span>false<span style="color: blue"></</span><span style="color: #a31515">traceToLog</span><span style="color: blue">>
</</span><span style="color: #a31515">processor</span><span style="color: blue">>
</</span><span style="color: #a31515">publishItem</span><span style="color: blue">>
</span></pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>And if going by names is enough (and it is), my suspicion instantly fell on RemoveUnknownChildren. A little work with Reflector quickly reveals what one of the main purposes of this item processor is.</p>
<p> </p>
<p>It essentially gets a list of child item IDs in the “source” database and removes them if they are not present in the “destination” database. </p>
<p> </p>
<p>This can be tested quickly enough. Switch to “master” – run a publish and check the result. Sure enough, our “Master Database” folder is now there, alone. Swithing to “products” and running a publish gives us a new result; now the “Master Database” folder is gone, but the “Products Database” folder is present.</p>
<p> </p>
<p>Curious as I am, I proceeded to disable this processor, to see what happened.</p><pre class="code"><span style="color: blue"><!--</span><span style="color: green"><processor type="Sitecore.Publishing.Pipelines.PublishItem.RemoveUnknownChildren, Sitecore.Kernel" /></span><span style="color: blue">--></span></pre><a href="http://11011.net/software/vspaste"></a>
<p>Result:</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUI5hUQLy_dG-MB1OGVL-i_5hXMCLsn5ntPZ3GmLb_7G41Ug-MeDFSFFyTPsxDnKIIIRH0E97CfTnJttLA-DwoucFlhkmb5pUMWUFeXLer4cr6-N7hM3qJTxLVHMl0SSJ9MydG/s1600-h/disabled%5B2%5D.png"><img title="disabled" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="200" alt="disabled" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWqFrVplm3sqBS4XOrzWY7sl9stLYMu33OjayM77I1UtEWXUigMoTSr98VExk8TINmSAeDYQk2eV7meb7qSB2OI-b30GPIBPg3Tomnp01AuxXrnFwUY7xfhSsOn9HTNdAAX8JV/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>Voila. It looks good. At least on paper ;-)</p>
<p> </p>
<p>While I am not completely comfortable with an intrusion such as this, disabling a system processor in the publishing pipeline, it at least allows me to move a bit forward on what I was aiming to achieve. If I dare to, that is.</p>
<p> </p>
<p>In “master” I mock up a new template, and an item named Home, based on it:</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2l4-5CVwQCtN6qGp91avR7Ge-nsdV2wBPGM0cJSDwQIfTd4f2_Q0y2AM90PXAJp5REqKu1WlaWY-Ai_BmqsRAaxji5_ML8MHBqCbAIrVHHUwv0k-d4RSMUDyd7UZrx4qM8bFW/s1600-h/master%5B6%5D.png"><img title="master" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="196" alt="master" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglbwW-RvpB9z6RzEtR2fmgpfpFmu3N_sqN5kNaASQwGLeAa9dAJ87tmqxK-W7oDIWLIrG-rbJt4HSaHpbXj16P9HBL6meBAvHoFYobaPhMsc9KRBgeHLatq5fQgdtXRxMq_xFv/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>And in Products, something similar.</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU9f9xXnVRldVg4O7jvbDJEbfJ-BgeVuWjurbQzWVcT7QzxtVl_-GLJnaMJ1EjJhwPG3f1kvoFAc0B_Z_FZL1CntKReXqeQqfoqaO25telYPjybT2nxJbhQil5iun490hlpOil/s1600-h/products2%5B2%5D.png"><img title="products2" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="195" alt="products2" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKWJycn2wHKAOKAwIBMVGxzU7ld_enlZ29PKqQebJqmeVu5wgs8q27CGjuZtgwkRDczP74n4cb5xEXQBifIaZ1-eKeFd2MDnYNpC5ilFkPB7_9Y9kgTECRn5iqzI2m04z7SzUu/?imgmax=800" width="244" border="0"></a> </p>
<p></p>
<p> </p>
<p>And after publishing the respective databases, I get the (now) expected end result.</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjCDq2xwg-WwtEId8qKibhddR37yyxrXjYAIpssAzBwHvRrw127Gdcvle6rB09w-2WYmVr4XM7DI1lgAfIIhP0-AwqHUaXqhGqI2rm-LDBsXb-PQQQNbQwfnqADWmb8ox2jKrD/s1600-h/web2%5B2%5D.png"><img title="web2" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="195" alt="web2" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVlZ1-rpXsJkEDXspq5ymv7usARbrnhfKJg0zRxNuF3oUcorbAoaim1nCFwXC3wOCPxv7vgORrb0CyEkML3IEAKYMErJjHGvP1EqSClvt0CqzE-REqEC6JAHALde_Go3wU1ZB2/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>Pretty neat :-)</p>
<p> </p>
<p>Alas however, as I mentioned above, having to modify web.config to achieve this kind of behaviour worries me. I can certainly see some advantages to this model, and I hope that at some point in the future, this will be an officially supported way to work with multiple databases. For now, the route we have to go, is via Proxy Items. They are not entirely bad either – that’s not it at all – but they seem (to me) a little less intuitive to use. Worst of all, however, they don’t hide from view the potentially thousands (see CEO presentation above) of content items being proxied in from the “products” database – I would personally prefer to be able to work like I just described here.</p>
<p> </p>
<p>(In reality, there are lots of potential issues involved in this approach, and I can sort of see why Sitecore wouldn’t immediately support it)</p>
<p> </p>
<p>But let’s proceed.</p>
<p> </p>
<h2>Configuring multiple databases using Sitecore Proxy Items</h2>
<p>First thing I do is enable the RemoveUnknownChildren processor again. Now I’m back to a normal (and therefore supported) configuration.</p>
<p> </p>
<p>First thing that needs to be done, is enabling proxies on the “master” database. Find it in web.config, and toggle the setting.</p><pre class="code"><span style="color: blue"><</span><span style="color: #a31515">proxiesEnabled</span><span style="color: blue">></span>true<span style="color: blue"></</span><span style="color: #a31515">proxiesEnabled</span><span style="color: blue">></span></pre><a href="http://11011.net/software/vspaste"></a>
<p>Then, in the Content Editor (“master” database), navigate to /sitecore/system/proxies – and add a new Proxy.</p>
<p> </p>
<p>Most of the settings on the Proxy Item are fairly straight forward.</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicxc5OLLjEmLSK7DjxE2YQ0pGb9KlGDdApCS0dSoHZgVl26oUFzFPfR7QRje9xexXFzIxtLKs-jCCPQZ1nbHPK8K_vekhxBtXRxHHv0YQud3FQdARaXqHHU8ahBq3n-1gCGYFL/s1600-h/proxy%5B2%5D.png"><img title="proxy" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="181" alt="proxy" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_Kz02L8yq1ge_gyNYhsQ0z9mSXCQ_e9XcuDLMNYVtkeeYklQt-K8q3zBbpZExNKyuz42ISEWGd6aucP5V-gCwn-MOGKXVqO3naXXyctxKv7DsN8JrCwIeAHdhaduB6VC4rmaA/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>The “Source Item” field is a little bit tricky however. If you click, you get a navigation tree from your… “master” database. Not products, as one would hope. This is not news, I <a title="Proxy template curiosity when proxying across databases" href="http://www.cassidy.dk/blog/sitecore/2006/01/proxy-template-curiosity-when-proxying.html">blogged about this in January 2006</a> – and the workaround is fortunately even simpler today than it was back then. I open up the View tab, switch on “Raw Values” and quickly paste my ID of the “Products Database” root folder into the field. After saving my Products Proxy, I can safely disable “Raw Values” again, and now I have:</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1E2EF_Ayej3xHIPP640-qHvIeLHpoESnQctvMQTcUVJMRxmg6eM488g5fTSNgBBgTXUXwz_1i_zBeBUMJLn4H7JOUetLtRHxSKehGFkQL_mdaq4FXIxzPRYuVOyllfiBKbPbb/s1600-h/proxy2%5B2%5D.png"><img title="proxy2" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="182" alt="proxy2" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSN4U7aJ2SG0orVS44l8JdycVbr_mjEl-Iu7Poarxe75Vu4TgJXZCFP6BbU6S0cmgyfx8bN42Q_EKVD1l1wZ3ndTt93JORyDv3aM2xClsSa1pREThP32ZYuH_AutYlT3phIIek/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>Because of what appears to be a slight quirk in the Sitecore Content Editor interface, I disable and then re-enable proxies using the new option that has now appeared in my database selector.</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaqbbMBzHom_61cJK1MEboWlE8SLh8QGqqQp2iTJ__WxigN19Vp7GFEeK1o5bKZMkymFYKxgWyoyt5eUStHRXRyZe3QMuhwrG0xOoSl1-BkggisUAuWaaq1DNZG8WcvxtBvJqG/s1600-h/selector%5B2%5D.png"><img title="selector" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="179" alt="selector" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgovoutJqKr7IkGY3mLNlVNxItAAWlEvaMz80siNeOuKBS7J4mQeiLXtZ94bl9NinJXjHLcXahofv91opkBsiZkEn1WDckS6Gq5pzwFZ_gsUcQgBG_tjKnKvy86SKMVI9GqKOiG/?imgmax=800" width="147" border="0"></a> </p>
<p> </p>
<p>Once done, my content tree looks like I expect:</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzIHA9PfUspg_t-edRCyipRTff_3sgpruIJhp07dLfpUKAJKM2T39oVLX8MJQF4nRvFMlUKXn4UnhzEcN9O10KAx-P1y7FWkN2jJBdTpGzyYQ8BgP9fWu9ZzrEfe18kMcsRO6U/s1600-h/proxied%5B2%5D.png"><img title="proxied" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="156" alt="proxied" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTtbG8WcVJMmKPLl2XeLbZ883CKloDE85Gt6MjfMZDayixhPC68jaj_ab8ZSCoK8JJbX_XhMw4uVjKaEBLpqAm6Yv9U9qvzujlLqNBV3iYL4uvnb3HagPsKaFhyy9cZ9KJDt6v/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>Notice how the items coming in from the “products” database are shown in grey. This is a visual cue to the editor, that these items are “different” – in effect in this case not coming from the same database at all.</p>
<p> </p>
<p>Running a publish also yields the same results – we are now back to where we were using my first approach.</p>
<p> </p>
<h2>Setting up a shortcut to Products</h2>
<p>One of the last things you would probably want to do, is set up an application shortcut to your “products” database.</p>
<p> </p>
<p>Fortunately, this is very easily achieved. Switch to the “core” database, and find /sitecore/content/documents and settings/all users/start menu/left/content editor – make a duplicate of it, and name your new item Product Editor.</p>
<p> </p>
<p>Configure parameters like this:</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicYcPZ7iuk0Amkmzheie8vw2BR0E3xu7WC8hdFj3ijsFF20NEn1vtZII_r7xZltbo4EyPR6f0xXfHThVJFWhdqSJ1RSbqYS1E4VKQ3fDEgd6lMd0yUYOmHxnXi6ZJvKxU7nJ-c/s1600-h/producteditor%5B8%5D.png"><img title="producteditor" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="182" alt="producteditor" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha2NJQNCbYjuOTIsb2ewMimKdWdSuIaa4TReawJNeebenujEGT3yYPZpKI9aVyW5l_gjXGOWkvTTdnaMYm0T1quu9QmKZt0K3f3qOFqQDDgEXhe4tcRScr7CP4yb5szV-52foI/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>Especially make note of the “Parameters” field; where I am instructing the Content Editor application to use the “products” database instead of the default database.</p>
<p> </p>
<p>Switch back to “master”, and you now have an extra option available on your Start Menu.</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPhlK1PiEMSXoe_V98yHZCC7LssoK2xiqAEwKlfqfXSrBrpnsVSDT2VTMQnly4sMB6dx1h5-O8BfQ5FLDoDFH3VdSRIBRDUpWEAQgy_CByuYLlEFfdx_k6vjcWvpY-pMy-sWXx/s1600-h/startmenu%5B2%5D.png"><img title="startmenu" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="startmenu" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHwQKB1peQYOdyjbzda8Qvww47VoPMyux2JqZIdpOZfeRblychJk8bAbDUxH3AzrcgjEkDJpXm0YWJUeW9hqyOF8wOdSBZgOSpZTwEnf4u1FGdOXZOvz96oA6ENpDbh7y69D0P/?imgmax=800" width="195" border="0"></a> </p>
<p> </p>
<p>And clicking the new “Product Editor” now take you directly to the “products” database, ready to edit. Since this application shortcut can be configured with security just like you would expect, you can therefore configure users who can ONLY work with the “products” and not mess around with the rest of your site. </p>
<p> </p>
<h2>In summary</h2>
<p>When I set out writing this article, I had a few ideas in mind. I thought I had a “new creative” approach to handling multiple databases in Sitecore – but it turned out to be perhaps a little TOO creative ;-) The recommended approach is going via Proxy Items, and it seems like the safer way to go.</p>
<p> </p>
<p></p>
<p></p>
<p>Regardless of method used, I still feel that Sitecore offers plenty of options of partitioning your data if the need arises. Performance-wise… well sure – I have no doubt you could produce a QUICKER (as in; performing faster) Product Catalogue working directly with SQL Server and Products/Categories and whatnot tables.</p>
<p> </p>
<p>Just like you could absolutely create a QUICKER website, using only flat .html files ;-)</p>
<p> </p>
<p>But there is a LOT to be gained by utilising the tools Sitecore makes available to us. Many of them were mentioned in the beginning of this article.</p>
<p> </p>
<p>I, for one, do NOT relish the idea of having to create a full blown web based product administrative interface. Especially not late Friday afternoon. Anyone else? ;-)</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com8tag:blogger.com,1999:blog-20883010.post-35725238968686769672009-04-17T18:05:00.001+02:002009-04-17T18:05:08.440+02:00Sitecore Wildcard Nodes<p>So here I was, doing a bit of late Friday afternoon catching up on the Sitecore Forums. I come across <a href="http://sdn.sitecore.net/SDN5/Forum/ShowPost.aspx?PostID=15042">this post</a>, in which a fairly common question is being asked, for which there are quite a number of possible solutions.</p> <p> </p> <p>What got my attention was the reply by Kern, who mentions something called a “wildcard item”.</p> <p> </p> <p>This is one of those humbling experiences. Having worked with Sitecore for over 3 years now, been involved directly and indirectly with a very large number of Sitecore Solution Partners, being certified, for a while had an office space right next to where certification training is being conducted in the UK; heck even going out for Friday night beers with one of the most experienced Sitecore Trainers in the UK….</p> <p> </p> <p>And I’ve never ever heard of “wildcard items” before.</p> <p> </p> <p>I asked around between everyone I could find online on MSN (with Sitecore experience), and although they all did offer various guesses – none knew the answer.</p> <p> </p> <p>Best… Kept… Secret… Ever… LOL ;-) Or maybe it just never came up.</p> <p> </p> <p>But I find it so very useful, that I’m now going to do my bit to spreading the word. Once again, it is proof that the really true gems in the Sitecore blogosphere tend to come from Lars Nielsen’s blog.</p> <p> </p> <p><a title="Sitecore - Avoiding query string in dynamic URL" href="http://larsnielsen.blogspirit.com/archive/2007/01/09/sitecore-avoiding-query-string-in-dynamic-url.html">Sitecore – Avoiding query string in dynamic URL</a></p> <p> </p> <p>I’ll just bring a quick snippet from the full article here, to give you some sort of idea of what it’s all about. Lars talks at length about how you can implement and use wildcard items in XSLT, and I find it in a way “drowns” the underlying real message – how you can use wildcard items in your day-to-day solutions. </p> <p> </p> <p>Anyway, give it a look. And have a good weekend :-)</p> <p> </p> <p>Snippet:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtyZSJ9yyP0b-9IhGQWee9zwn-O3YMMhy1fV6vCi16jON92egt0jQfejhvbvug_QFOuE-jv5DCAMY6qWhxePQSDthZfnEnuIPVO5LMzLjYSCcLkoWXqA9R7ea6jgvceCpy1ua5/s1600-h/wildcard%5B2%5D.png"><img title="wildcard" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="wildcard" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXykTO078lwZYLqlArV1H-0EdJdnE8TEKdWTH7ElZKU5Wr488SRJKfxWAHDWWi0rM8Ra9RPeTWadxEnKF9OuXw4-l2O8G5ZTljMZPIS-sCEKR8LKO59qcDFCuj1LbPIelVFAjf/?imgmax=800" width="162" border="0"></a></p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com0tag:blogger.com,1999:blog-20883010.post-23818657198622750472009-04-13T20:26:00.001+02:002009-04-13T20:26:07.749+02:00Migrating data into Sitecore<p>One question I’ve noticed gets posed ever so often on the Sitecore Forums is; “How do I migrate data from my clients previous CMS into Sitecore?” Or similar type questions, to more or less the same effect. Also commonly asked on the same occasion is; “Does Sitecore provide a tool for this?”</p> <p> </p> <p>Just to make sure we’re all on the same page here; I’m referring to the act of importing structured data (i.e. the clients existing website, sitting in any structured repository – CMS or otherwise – and getting that migrated into Sitecore for use in the Next Generation CMS system being developed by you).</p> <p> </p> <p>This process is also known as <a href="http://en.wikipedia.org/wiki/Data_migration">Data Migration</a>, and WikiPedia defines it as follows:</p> <p> </p> <p>“<b>Data migration</b> is the process of transferring <a href="http://en.wikipedia.org/wiki/Data">data</a> between <a href="http://en.wikipedia.org/wiki/Computer_storage">storage</a> types, <a href="http://en.wikipedia.org/wiki/Formats">formats</a>, or <a href="http://en.wikipedia.org/wiki/Computer_system">computer systems</a>. Data migration is usually performed programmatically to achieve an <i>automated migration</i>, freeing up human resources from tedious tasks. It is required when organizations or individuals change computer systems or upgrade to new systems, or when systems merge (such as when the organizations that use them undergo a merger/takeover).”</p> <p> </p> <p>It further states:</p> <p> </p> <p>“To achieve an effective data migration procedure, data on the old system is mapped to the new system providing a design for <a href="http://en.wikipedia.org/wiki/Data_extraction">data extraction</a> and <a href="http://en.wikipedia.org/wiki/Data_loading">data loading</a>. The design relates old data formats to the new system's formats and requirements. Programmatic data migration may involve many phases but it minimally includes <i>data extraction</i> where data is read from the old system and <i>data loading</i> where data is written to the new system.”</p> <p> </p> <p>And lastly:</p> <p> </p> <p>“Changing application vendor - for instance a new <a href="http://en.wikipedia.org/wiki/CRM">CRM</a> or <a href="http://en.wikipedia.org/wiki/ERP">ERP</a> platform will inevitably involve substantial transformation as almost every application or suite operates on its own specific data model.”</p> <p> </p> <p>If we boil all of this information down, we get something like “Data Migration is usually performed programmatically. To do it, a design for extracting data and loading data needs to be developed.”</p> <p> </p> <p>And that’s where you come in.</p> <p> </p> <p>We already have at least a partial answer to one of the above questions. Sitecore couldn’t really provide a tool that could achieve this. Data extraction would always be bespoke to the specific system being migrated from – so while you might be lucky enough to find something that would extract data from say a specific version EpiServer to a specific version of Sitecore; chances are you would still end up having to do quite a bit of the data design yourself to satisfy your business requirements.</p> <p> </p> <p>That being said, I do believe Sitecore offers a few features to help you out.</p> <p> </p> <p><font color="#333333">For instance, there’s the <a href="http://trac.sitecore.net/XmlImporter">Xml Importer</a>.</font></p> <ul> <ul> <li>It essentially works as a Transformation Engine. Given an XML input file (presumably produced by the system you intend to migrate from), you then write an XSLT file to transform the XML input file to a pre-defined format, which the Xml Importer will then use to create Sitecore Content Items.</li> <li>This is probably only fine for the simplest of cases. In most migrations I have done, there has almost always been business requirements to perform more “advanced” features such as UrlMapping (setting up Url Redirection so that the existing links will work on the target Sitecore installation), Media Importing, category mapping to name a few. Not that you couldn’t achieve most of this using XSLT. I just think it will be very hard and unworkable.</li></ul></ul> <p> </p> <p>There may or may not be similar tools available. I’ve never used them myself simply because in the vast majority of cases, a pre-built tool would not be able to achieve the requirements I was tasked to fulfil. The process of actually creating Sitecore Content Items is the least of my worries when it comes to migration.</p> <p> </p> <p>While I can’t provide a specific solution in this post to whatever migration problem you may be facing, I will take you through a fictional scenario to help explain some of the points that needs to be addressed, and provide a few techniques that you may find useful.</p> <p> </p> <p>For demonstration purposes, I will be importing the good old trusted Northwind database. Now before you start, I’m not saying this is neither a very good idea or anything similar – Northwind doesn’t hold web content and is therefore a very unlikely candidate for this type of migration. I have chosen it simply because many of you are already familiar with the structure of it, and it is <a title="Link to download Northwind sample databases for SQL 2000" href="http://www.microsoft.com/downloads/details.aspx?familyid=06616212-0356-46a0-8da2-eebc53a68034&displaylang=en">freely available for anyone to download</a> and experiment with.</p> <p> </p> <h4>Designing the model</h4> <p>To begin the process of migration, you first need to analyse the data model, and come up with an implementation that will work in your Sitecore solution.</p> <p> </p> <p>In the example I’m going to cover, I’ve been tasked with the following:</p> <p> </p> <p>“Migrate the <em>products</em> into a relevant <em>categorised</em> Sitecore Content Structure, making sure you preserve information about each <em>supplier</em> for the migrated products”.</p> <p> </p> <p>After looking a bit at the products table definition, I conclude the following:</p> <p> </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0d09Q7d_Bz2yox5hZmvchDCErfEXn1IKalEnXRHgJsArZAYgpHFKtyVrXhGymmYXWE0aSLMOJIzZNcx8HqLu_BHoYz8j9h-mvadgne49YCdmnls1SoR1J9zq2CdXUQe32MgN7/s1600-h/products%5B3%5D.png"><img title="products" style="border-right: 0px; border-top: 0px; display: inline; margin-left: 0px; border-left: 0px; margin-right: 0px; border-bottom: 0px" height="177" alt="products" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge0SPbvKYc_whp-UmA1ET-Bnp6mIm8yZshdnTqVJv4RmjpfOvgV8MgobppXIr49yiXk9Qm52TOP-vsxBBfelQOOKSNtj00Jb_A8u1ZhxYEvGxkESlhOocFn2jGMgqC7oYMVaSi/?imgmax=800" width="244" align="right" border="0"></a> </p> <ul> <li>Each product has only 1 supplier</li> <li>Each product is only defined in 1 category</li></ul> <p> </p> <p>This is fortunate, as it will make the migration a fairly simple task. More often than not, things will not be this well defined and simple.</p> <p> </p> <p>So in terms of a Sitecore Content Structure, this could look like this:</p> <p> </p> <ul> <li>/sitecore/content/Home</li> <ul> <li>/sitecore/content/Home/Products</li> <ul> <li>/sitecore/content/Home/Products/CategoryA</li> <li>/sitecore/content/Home/Products/CategoryB</li> <li>… and so on</li></ul> <li>/sitecore/content/Home/Suppliers</li> <ul> <li>/sitecore/content/Home/Suppliers/SupplierA</li> <li>/sitecore/content/Home/Suppliers/SupplierB</li> <li>… and so on</li></ul></ul></ul> <p> </p> <p>You could of course map this in many different ways. A few of the things you should be considering when designing your structure are:</p> <p> </p> <ul> <li>How many Sitecore Items will be created on each branch? </li> <ul> <li>Sitecore recommends that no more than 100 Items are created on any one branch level. </li></ul> <li>How will the existing <em>meta-structure</em> be preserved? </li> <ul> <li>In this case, a products category is implicitly derived from the Category node it sits under. Alternatively, as I will need to do with suppliers, you could create references from your data to point to the relevant meta data.</li></ul> <li>Will any filtering or data transformation need to take place?</li> <ul> <li>Often, there are old data in the source system that you might as well filter out when doing the migration. This could be data prior to a certain date (i.e. only migrate the last 3 years worth of data) or certain categories that will not be migrated to the new system.</li> <li>Are there inline links that you will need to handle? As Northwind doesn’t hold web content, this is not relevant to this example. But in most cases, you will need to <a title="Link to WikiPedia explaining the basics of Regular Expressions" href="http://en.wikipedia.org/wiki/Regular_expression">RegEx</a> through content fields and rewrite Internal Links to point to the new locations of your content within Sitecore. This topic is complex enough to deserve an entire post of it’s own, so I won’t be going into detail with this here.</li></ul></ul> <p> </p> <p>Having defined this structure, I now explore the relevant Database Schemas, and come up with the following 3 Sitecore Templates that will eventually hold my migrated data. In this case I started on a blank Sitecore 6 installation (090212, but that isn’t really relevant – this migration process would not differ significantly between Sitecore 5 and Sitecore 6).</p> <p> </p> <p>Product:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigL6ra-eWm6vkI8S1YXQ3fxEDG5UnZWiK1S5cJInsP667r0LBQ_wabkXrCLZPccdzISbbjMegACj0mXCCzWSKmRgaFc4rPjdTOoZMo4rnakuTSBeqyxfzCVQxwEpnMAS9AgoLO/s1600-h/product%5B2%5D.png"><img title="product" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="109" alt="product" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9Hxq5KmxciYJwaL2CyuU77tStrb__TH0B6S6C3eqspbphu3O4GPLKYdz29qv5NMEQ5m5ksNP2eDNYidlM1OjXLJ581IYZMA12iFlAcZ-RHak_jc_g0im7b8ErCGQC8upahm5N/?imgmax=800" width="244" border="0"></a> </p> <p> </p> <p>Category:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIDhDAJXa3lydMPk0Wbem9CLaXnU7OA_KnSCIubKds_7FaDxd84CgYPPQ17LPTpK726Yd5vK5PfoSwQwOHD8u3VibnGE0RQL4BQPot7WdJRSAX7ELjqisGL2EL1PcYvLf5-K9L/s1600-h/category%5B2%5D.png"><img title="category" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="46" alt="category" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEBmkvnc2dYA12XQEk8qEosvZoZSYe6gApzH0yOGbLPmhpJqRF3-ZYfyS4Rl7hyphenhyphenUVZ0AfuLHOyVGnDaX0O9i8_cgNEjI1CEt_AdPVJ-_-rr5Z4iRaauKFTjPm9Zfmmr9rJNse9/?imgmax=800" width="244" border="0"></a> </p> <p></p> <p> </p> <p>Supplier:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixwD66AC5EGvUbkH7wc7xanJN6fPxLYX4z8PX14ZFPcuKSYZD98IT52FP2fK_EPOZpaWht-2dMe_Bq45ZXmU5fhKTctbU54R4E6Y0oAy3bxBfdgJ38_zaKycFXgs69UBG7sdZo/s1600-h/supplier%5B2%5D.png"><img title="supplier" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="145" alt="supplier" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijqfrT6Gq1yK1rWvbEalQWG0sTc8miAUuBQ5eYg2zyEZIGLa9VA0T4Eve5BCEjdopae_wcFtw6fBA-ku60yEtsC1kiolpTtgwKK_EcumvpAJnWO8B54Lkr3gQ1H9IptiTGXh1H/?imgmax=800" width="244" border="0"></a> </p> <p> </p> <p>I also create the basic skeleton structure I defined above, and run a Smart Publish. I’ll explain why, in just a bit.</p> <p> </p> <p>Skeleton structure:</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgabv0WRU7sbA90eJ-faEEDbXIOp2Nenr_IHsqbM4jb9lAtijg0YrwfRjRaEvTR9CaNff6CxxkWQ6Dp04MRl96b1eYOu1YJpAdhmh7yjb2SpyD5-7Z5qIeX7-jT9aZgBWIzrH1Z/s1600-h/structure%5B2%5D.png"><img title="structure" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="98" alt="structure" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdJ07mH3OOgHdud4IvhRd1cIZ6E943BydDG8kP3SEJQMtqU3h3rrAZupCTBGpoW4icRto9K_Y6tvh7dEZQKwv5RhzER9lpXCOt1J7_aSOokC0jhWeC60TMkyzrjKqfERnK02H1/?imgmax=800" width="135" border="0"></a> </p> <p> </p> <p>And I am now ready to proceed to the next stage.</p> <h4>Extracting the data</h4> <p>When it comes to extracting the Northwind Data, fortunately this is almost a textbook example. The data is nice and clean, referential integrity is in place, and .NET makes it very straightforward to connect to my underlying MS SQL Server to reach the relevant tables. I know there are many boilerplate examples out there of how to easily access Northwind using LINQ and similar technologies, but since the data extraction will be so simple in this case I don’t really see a need for invoking any of them.</p> <p> </p> <p>One toolkit I will use however, is my <a title="Link to the Sitecore Trac space for CorePoint.DomainObjects" href="http://trac.sitecore.net/DomainObjects">CorePoint.DomainObjects</a>. Don’t see this as a shameless plug; one of the main reasons I developed the toolkit was to help in my many migration projects and it just makes the task ahead so much easier.</p> <p> </p> <p>I set up a standard Sitecore project, and quickly mock up a few entities:</p> <p> </p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0vDgvM0wOcEZpmBLPPYhDgmbAeQWUMqmq5G9ke3AuK940_-aMotnPKWrGCO80QoUa5UC0KBR3vVuYwK_gqUkKAOwhO0ZsQfa0EKD9cO7a6KumYK_7oOHMRPpJpcWfHBH0iEJH/s1600-h/project%5B2%5D.png"><img title="project" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="project" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCizaL2lfWC7LwxvXI8d1hMzEj6o3wO2qUQmf7r2H9sfHedrJdRHjVGW0rvyTp4WfDQolkKsbP5S9Lp8FJdUD-X_kZ5LaxI5f8ATQSSbJonS3by7lSk2LMtpmxTGEYYMmiwu8l/?imgmax=800" width="213" border="0"></a> </p> <p> </p> <p>Product.cs:</p><pre class="code"><span style="color: blue">using </span>System;
<span style="color: blue">using </span>System.Data;
<span style="color: blue">using </span>CorePoint.DomainObjects.SC;
<span style="color: blue">using </span>CorePoint.DomainObjects;
<span style="color: blue">namespace </span>Website.Migration
{
[<span style="color: #2b91af">Template</span>(<span style="color: #a31515">"user defined/product"</span>)]
<span style="color: blue">public class </span><span style="color: #2b91af">Product </span>: <span style="color: #2b91af">StandardTemplate
</span>{
<span style="color: green">// A few properties that will be needed to map out the Sitecore hierarchy
</span><span style="color: blue">public int </span>ProductId { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: blue">public int </span>SupplierId { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: blue">public int </span>CategoryId { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"product name"</span>)] <span style="color: blue">public string </span>ProductName { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"quantity per unit"</span>)] <span style="color: blue">public string </span>QuantityPerUnit { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"unit price"</span>)] <span style="color: blue">public double </span>UnitPrice { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"units in stock"</span>)] <span style="color: blue">public int </span>UnitsInStock { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"units on order"</span>)] <span style="color: blue">public int </span>UnitsOnOrder { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"reorder level"</span>)] <span style="color: blue">public int </span>ReorderLevel { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"discontinued"</span>)] <span style="color: blue">public bool </span>Discontinued { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"supplier"</span>)] <span style="color: blue">public </span><span style="color: #2b91af">Guid </span>Supplier { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: blue">public </span>Product()
{
}
<span style="color: blue">public </span>Product( <span style="color: #2b91af">DataRow </span>row )
{
<span style="color: green">// For brewity, null values are not checked for. There are none in the
// Northwind products table
</span>ProductName = <span style="color: #2b91af">Convert</span>.ToString( row[ <span style="color: #a31515">"ProductName" </span>] );
SupplierId = <span style="color: #2b91af">Convert</span>.ToInt32( row[ <span style="color: #a31515">"SupplierID" </span>] );
CategoryId = <span style="color: #2b91af">Convert</span>.ToInt32( row[ <span style="color: #a31515">"CategoryID" </span>] );
QuantityPerUnit = <span style="color: #2b91af">Convert</span>.ToString( row[ <span style="color: #a31515">"QuantityPerUnit" </span>] );
UnitPrice = <span style="color: #2b91af">Convert</span>.ToDouble( row[ <span style="color: #a31515">"UnitPrice" </span>] );
UnitsInStock = <span style="color: #2b91af">Convert</span>.ToInt32( row[ <span style="color: #a31515">"UnitsInStock" </span>] );
UnitsOnOrder = <span style="color: #2b91af">Convert</span>.ToInt32( row[ <span style="color: #a31515">"UnitsOnOrder" </span>] );
ReorderLevel = <span style="color: #2b91af">Convert</span>.ToInt32( row[ <span style="color: #a31515">"ReorderLevel" </span>] );
Discontinued = <span style="color: #2b91af">Convert</span>.ToBoolean( row[ <span style="color: #a31515">"Discontinued" </span>] );
}
}
}</pre>
<p>Category.cs:</p><pre class="code"><span style="color: blue">using </span>System;
<span style="color: blue">using </span>System.Data;
<span style="color: blue">using </span>CorePoint.DomainObjects.SC;
<span style="color: blue">using </span>CorePoint.DomainObjects;
<span style="color: blue">namespace </span>Website.Migration
{
[<span style="color: #2b91af">Template</span>(<span style="color: #a31515">"user defined/category"</span>)]
<span style="color: blue">public class </span><span style="color: #2b91af">Category </span>: <span style="color: #2b91af">StandardTemplate
</span>{
<span style="color: blue">public int </span>CategoryId { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"category name"</span>)] <span style="color: blue">public string </span>CategoryName { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"description"</span>)] <span style="color: blue">public string </span>Description { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: blue">public </span>Category()
{
}
<span style="color: blue">public </span>Category( <span style="color: #2b91af">DataRow </span>row )
{
CategoryId = <span style="color: #2b91af">Convert</span>.ToInt32( row[ <span style="color: #a31515">"CategoryID" </span>] );
CategoryName = <span style="color: #2b91af">Convert</span>.ToString( row[ <span style="color: #a31515">"CategoryName" </span>] );
Description = <span style="color: #2b91af">Convert</span>.ToString( row[ <span style="color: #a31515">"Description" </span>] );
}
}
}</pre><a href="http://11011.net/software/vspaste"></a>
<p>Supplier.cs:</p><pre class="code"><span style="color: blue">using </span>System;
<span style="color: blue">using </span>System.Data;
<span style="color: blue">using </span>CorePoint.DomainObjects.SC;
<span style="color: blue">using </span>CorePoint.DomainObjects;
<span style="color: blue">namespace </span>Website.Migration
{
[<span style="color: #2b91af">Template</span>(<span style="color: #a31515">"user defined/supplier"</span>)]
<span style="color: blue">public class </span><span style="color: #2b91af">Supplier </span>: <span style="color: #2b91af">StandardTemplate
</span>{
<span style="color: blue">public int </span>SupplierId { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
[<span style="color: #2b91af">Field</span>(<span style="color: #a31515">"company name"</span>)] <span style="color: blue">public string </span>CompanyName { <span style="color: blue">get</span>; <span style="color: blue">set</span>; }
<span style="color: green">// For brewity, the rest of the fields are omitted, as
// they're not really needed to demonstrate the migration
</span><span style="color: blue">public </span>Supplier()
{
}
<span style="color: blue">public </span>Supplier( <span style="color: #2b91af">DataRow </span>row )
{
SupplierId = <span style="color: #2b91af">Convert</span>.ToInt32( row[ <span style="color: #a31515">"SupplierID" </span>] );
CompanyName = <span style="color: #2b91af">Convert</span>.ToString( row[ <span style="color: #a31515">"CompanyName" </span>] );
}
}
}
</pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>And now we’re ready to get going. As you can see, I cheated a bit on Supplier.cs. This article is long enough, without me listing out 15 or so similar fields that don’t really add value to the demonstration / example itself.</p>
<p> </p>
<p>I also created a completely standard .ASPX page under the root of the site, DoMigrate.aspx. It is in this page I will be executing the migration itself.</p>
<p> </p>
<p>I start by adding some code that will load up all of the required source data into memory. When developing a migration, there are a few things to keep in mind:</p>
<p> </p>
<ul>
<li>Your code is more or less <em>fire and forget</em>. It will not run in a live environment, it will not need to be maintained by yourself or your peers for an extended period of time (normally) and therefore many of the rules and guidelines that apply to developing web applications don’t really apply here.</li>
<li>For performance reasons, load up as much as you possibly can into RAM. Run 64 bit if you can, and don’t worry if your migration script is eating up gigabytes of memory. If your migration mapping is complex (this example isn’t), you will appreciate the performance gain from having in-memory tables and lookups, and can in many cases mean a difference of 4 hours versus 4 minutes of execution time. </li>
<li>Don’t expect to get it spot-on the first time. You will be running the script many times over.</li></ul>
<p> </p>
<p>I add a bit of code to the Page_Load method:</p><pre class="code"><span style="color: blue">protected void </span>Page_Load( <span style="color: blue">object </span>sender, <span style="color: #2b91af">EventArgs </span>e )
{
<span style="color: green">// First we start by loading all source data into memory.
</span><span style="color: blue">string </span>connectionString =
<span style="color: #a31515">@"user id=sa;password=<strong>removed</strong>;Data Source=.\SQLEXPRESS;Database=Northwind"</span>;
<span style="color: #2b91af">DataTable </span>products = <span style="color: blue">new </span><span style="color: #2b91af">DataTable</span>();
<span style="color: #2b91af">DataTable </span>suppliers = <span style="color: blue">new </span><span style="color: #2b91af">DataTable</span>();
<span style="color: #2b91af">DataTable </span>categories = <span style="color: blue">new </span><span style="color: #2b91af">DataTable</span>();
<span style="color: blue">using </span>( <span style="color: blue">var </span>conn = <span style="color: blue">new </span><span style="color: #2b91af">SqlConnection</span>( connectionString ) )
{
<span style="color: blue">using </span>( <span style="color: blue">var </span>da = <span style="color: blue">new </span><span style="color: #2b91af">SqlDataAdapter</span>( <span style="color: #a31515">"SELECT * FROM Products"</span>, conn ) )
{
da.Fill( products );
}
<span style="color: blue">using </span>( <span style="color: blue">var </span>da = <span style="color: blue">new </span><span style="color: #2b91af">SqlDataAdapter</span>( <span style="color: #a31515">"SELECT * FROM Categories"</span>, conn ) )
{
da.Fill( categories );
}
<span style="color: blue">using </span>( <span style="color: blue">var </span>da = <span style="color: blue">new </span><span style="color: #2b91af">SqlDataAdapter</span>( <span style="color: #a31515">"SELECT * FROM Suppliers"</span>, conn ) )
{
da.Fill( suppliers );
}
}
}
</pre><a href="http://11011.net/software/vspaste"></a>
<p><a href="http://11011.net/software/vspaste"></a> </p>
<p>As you can see, no surprises there. I am basically filling up 3 Datatables with information from the Northwind database.</p>
<p> </p>
<p>I add a little code to load up all the data in the entities I created above, like this:</p><pre class="code"><span style="color: #2b91af">List</span><<span style="color: #2b91af">Product</span>> ProductList = <span style="color: blue">new </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">Product</span>>();
<span style="color: #2b91af">List</span><<span style="color: #2b91af">Category</span>> CategoryList = <span style="color: blue">new </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">Category</span>>();
<span style="color: #2b91af">List</span><<span style="color: #2b91af">Supplier</span>> SupplierList = <span style="color: blue">new </span><span style="color: #2b91af">List</span><<span style="color: #2b91af">Supplier</span>>();
<span style="color: blue">foreach</span>( <span style="color: #2b91af">DataRow </span>row <span style="color: blue">in </span>products.Rows )
ProductList.Add( <span style="color: blue">new </span><span style="color: #2b91af">Product</span>( row ) );
<span style="color: blue">foreach </span>( <span style="color: #2b91af">DataRow </span>row <span style="color: blue">in </span>categories.Rows )
CategoryList.Add( <span style="color: blue">new </span><span style="color: #2b91af">Category</span>( row ) );
<span style="color: blue">foreach </span>( <span style="color: #2b91af">DataRow </span>row <span style="color: blue">in </span>suppliers.Rows )
SupplierList.Add( <span style="color: blue">new </span><span style="color: #2b91af">Supplier</span>( row ) );
Response.Write( <span style="color: blue">string</span>.Format( <span style="color: #a31515">"{0} products, {1} categories, {2} suppliers<br />"</span>,
ProductList.Count,
CategoryList.Count,
SupplierList.Count ) );
</pre>
<p><a href="http://11011.net/software/vspaste"></a> </p>
<p>And for the first time, I run my migration script-to-be.</p>
<p> </p>
<p>“77 products, 8 categories, 29 suppliers”</p>
<p> </p>
<p>This is a good time to sanity check your code and make sure the numbers you are getting are more or less what you expect. Comparing with the rowcounts of the source database, I conclude that everything appears to be in order, and continue. It is now time to create some Sitecore Items based on this data.</p>
<p> </p>
<p>Before I do that, there are a few things to point out:</p>
<p> </p>
<ul>
<li>The order which you create the Sitecore items in, is not irrelevant. In our case here, Products refer Suppliers and sit beneath Categories. Therefore the first Sitecore Items you create must be Suppliers (or Categories, in this case that won’t matter) followed by Categories and finally followed by Products.</li>
<li>I always run test migrations against the “web” database. This has a few advantages:</li>
<ul>
<li>It runs faster (I think… it should, since there is no publishing queue handling)</li>
<li>It allows you to easily undo a failed migration. Just publish your skeleton again, and the whole thing resets back to start</li></ul></ul>
<p> </p>
<p>I add some code to create Sitecore Content. Don’t worry, we’re almost done now :-)</p><pre class="code"><span style="color: green">// Set up a director for "web", standard language and security disabled
</span><span style="color: #2b91af">SCDirector </span>director = <span style="color: blue">new </span><span style="color: #2b91af">SCDirector</span>( <span style="color: #a31515">"web"</span>, <span style="color: #a31515">"en"</span>, <span style="color: blue">true </span>);
<span style="color: blue">var </span>suppliersRoot = director.GetObjectByIdentifier( <span style="color: #a31515">"/sitecore/content/home/suppliers" </span>);
<span style="color: blue">var </span>productsRoot = director.GetObjectByIdentifier( <span style="color: #a31515">"/sitecore/content/home/products" </span>);
<span style="color: green">// And with these references in place, it's time to create our Sitecore Content Items
</span>SupplierList.ForEach( <span style="color: blue">delegate</span>( <span style="color: #2b91af">Supplier </span>s )
{
s.Name = s.CompanyName; <span style="color: green">// A name for the Sitecore Item (duh.. :P)
</span>s.ParentId = suppliersRoot.Id;
s.Director = director;
s.Store();
} );
CategoryList.ForEach( <span style="color: blue">delegate</span>( <span style="color: #2b91af">Category </span>c )
{
<span style="color: green">// First create the category
</span>c.Name = c.CategoryName;
c.ParentId = productsRoot.Id;
c.Director = director;
c.Store();
<span style="color: green">// And now create all products under the category
</span>ProductList.FindAll( p => p.CategoryId == c.CategoryId ).ForEach( <span style="color: blue">delegate</span>( <span style="color: #2b91af">Product </span>p )
{
p.Name = p.ProductName;
p.ParentId = c.Id; <span style="color: green">// .Id was assigned when the category was stored
</span>p.Director = director;
<span style="color: green">// Pay attention to this one. I now map the Supplier Id field with the
// Sitecore ID of the related supplier (created above)
</span>p.Supplier = SupplierList.Find( s => s.SupplierId == p.SupplierId ).Id;
p.Store();
} );
} );
Response.Write( <span style="color: #a31515">"All done!<br />" </span>);
</pre><a href="http://11011.net/software/vspaste"></a>
<p> </p>
<p>And here we go. The data is now migrated. On my overworked laptop, this code executes in just a couple of seconds. The results look like this:</p>
<p> </p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDyHp_8Lqj4FXW1i8ZYZHoRB6g5ve0zB6cyC7MHg0eTOh2Jus_VuaeXR926xNnlUXAhRtbyN9H4vSh3QOMcNEzffc-o6dXyR6tOmizYGLWH0YiSV2E_fZ3Q-8T0c7wLpcM1h64/s1600-h/migration%5B2%5D.png"><img title="migration" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="170" alt="migration" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9eNm-ZcvATYv1BDiq4HMBNrX3m2ro2sFPofBr81Iv2Qyx0YqmT7WO8Du_03W7S7S8dqPb1iAE8VUgpdeL9nmxiamvOgYFcNsE9AydrLw50EOq5_ZoRn31FrQ25sExW6eBfC_5/?imgmax=800" width="244" border="0"></a> </p>
<p> </p>
<p>And you’re all done.</p>
<p> </p>
<p>A few last things to consider are this:</p>
<p> </p>
<ul>
<li>If migrating large quantities of data, try and disable as many Sitecore event handlers and whatever else you can get away with. </li>
<li>Use BulkUpdateContext()</li>
<li>Don’t forget your target language</li>
<li>If you can, make the fields shared and unversioned. This should help migration execution speed.</li>
<li>And probably most importantly: Most of your work will almost always be during analysis. Figuring out</li>
<ul>
<li>How to get to the source data</li>
<li>Determining what data needs to be migrated</li>
<li>Designing a reasonable way to store the data in Sitecore</li></ul></ul>
<p> </p>
<p>I don’t know of many shortcuts. This is very rarely as easy a task as this one was. I know there are standards in the making, for how CMS systems could store (or at least interchange data), but I don’t know if those are realistic and (more importantly) will be widely adopted by the various CMS vendors.</p>
<p> </p>
<p>I hope you found this post useful. Probably my longest post ever. Comments and feedback welcomed :-)</p> Mark Cassidyhttp://www.blogger.com/profile/07054254475245848945noreply@blogger.com8