Thursday 29 October 2015

Setting up Mongo DB for developer use with Sitecore

Here are some simple steps to set up Mongo for use with Sitecore.  I used these for a development Sitecore 8 installation:

  1. Download and install mongodb from https://www.mongodb.org/downloads?_ga=1.194443182.2054203937.1446139202#production
  2. Create a bat file to initialize the mongo db, something like:

    "C:\Program Files\MongoDB\Server\3.0\bin\mongod.exe" -dbpath "C:\inetpub\wwwroot\[Sitecore Project]\Databases\Mongo"

    N.B. I always have the database created in the Sitecore website's databases folder.
  3. Double click the bat file - you can use software like Always Up (http://www.coretechnologies.com/products/AlwaysUp/) to turn the bat file into a pseudo windows service, which means you don't have to kick it off every time you start windows.
  4. The analytics part of your connections strings config should look something:
    <add name="analytics" connectionString="mongodb://localhost/analytics"/>
    <add name="tracking.live" connectionString="mongodb://localhost/tracking_live"/>
    <add name="tracking.history" connectionString="mongodb://localhost/tracking_history"/>
    <add name="tracking.contact" connectionString="mongodb://localhost/tracking_contact"/>
  5. Login to Sitecore and you should now see the graph (which defaults to Interactions by visits and value per visits) appear at the bottom of the screen:

    Sitecore 8

Moving SQL DB Files

Here's a good link showing how to move SQL DB files:

http://sqlserverzest.com/2013/08/10/sql-server-how-to-move-database-files-data-file-and-log-file-from-one-drive-location-to-the-other/

Friday 16 October 2015

Sitecore Glass doesn't work with MediaRequestProtection

There's a new feature in Sitecore 7.5, called Media Request Protection.  This is where Sitecore adds a hash to the media URL, to stop it being tampered with.  You can manage it in the Sitecore.Media.RequestProtection.config file.  

In fact if you need to add to the parameters in the Media URL, perhaps for a custom image handler; you will need to ensure that your new parameters are registered in this configuration file, otherwise they won't come through to your handler.
 
We just went live with new Sitecore 8.0 site and noticed lots of errors in the log something like: 

948 20:44:25 ERROR MediaRequestProtection: An invalid/missing hash value was encountered. The expected hash value: E43FE65CB6A819B4976A19BEFF730F204C7386DB. Media URL: /~/media/Images/Heroes/card-bikes.ashx?h=279&w=576, Referring URL: 7084 20:44:25 ERROR MediaRequestProtection: An invalid/missing hash value was encountered. The expected hash value: 098D0163B651C8F5944A0183BB29012295FA8B54. Media URL: /~/media/Images/Heroes/product-card.ashx?h=279&w=279, Referring URL: 

The errors seemed to be so frequent that it was causing memory to spike, which was impacting the site.

Also (and more importantly) what this meant was that Sitecore wasn't resizing/caching any of the images before sending them to the client.  Sitecore's own image handlers were not working.

On investigation we realized that Sitecore Glass's Mvc Helper methods like @Editable and @RenderImage were not rendering the new Media Request security hash. Doing a little digging I found: https://github.com/mikeedwards83/Glass.Mapper/issues/93 The article mentions that it is a known issue with Sitecore Glass and is fixed in Sitecore Glass version 4.0.

The options we came up with were 
  • Turn it off
  • Upgrade Sitecore Glass
  • Write our own helper method or extend the Sitecore Glass ones
In the end we opted to upgrade.

ReSharper and Complex Content Searching

On a recent project, I had to create a more complicated content search that involved a number of parameters, some of which had to be grouped with ands and ors.  In doing this I encountered some issues with Sitecore's implementation of Linq and the use of tools like ReSharper.

I have a number of different filters passed into my search results method, with each search parameter being a list of GUIDs

public static IList<ProductSearchResultItem> GetAllProductsByFilter(
       List<Guid> categories,
       List<Guid> subCategories, List<Guid> brands,
       List<Guid> agesAndStages, List<Guid> features,
       List<Guid> searchTerms, List<Guid> brandCollections,
       List<Guid> products, List<Guid> colors,
       List<Guid> fashions, List<Guid> subBrands,
       bool includeProducts, ProductFilterSortOrder sortOrder,
       int pageNumber, int pageSize, out int count)       
I then need to start building out my predicates, that will enable me to build up my search expression. The issue arises when you do something like:
var query = GetBaseQuery(searchContext);

var predicate = PredicateBuilder.True<productsearchresultitem>();
var predicateCategory = PredicateBuilder.False<ProductSearchResultItem>();

foreach (var category in categories)
{
   var localCategory = category;
   predicateCategory = predicateCategory.Or(p => p.Categories.Contains(localCategory));
}

ReSharper will try to refactor the loop to something like:
 predicateCategory = categories.Aggregate(predicateCategory, (current, localCategory) => current.Or(p => p.Categories.Contains(localCategory)));
The Aggregate extension method is not supported by Linq to Sitecore and will throw an exception when the query is evaluated (ToList()).

To stop this happening you can instruct ReSharper to ignore this by doing the following:
                 
// ReSharper disable LoopCanBeConvertedToQuery
foreach (var category in categories)
{
   var localCategory = category;
   predicateCategory = predicateCategory.Or(p => p.Categories.Contains(localCategory));
}
This will stop ReSharper from suggesting the refactoring from this point in the code.

Another Gotcha I found was when I came to evaluate the expression using:
 query = Queryable.Where(query, predicate);
ReSharper will suggest changing this to use the extension method:
  query = query.Where(predicate);
Which will break Linq to Sitecore.

You can stop this from happening once:
                
// ReSharper disable once InvokeAsExtensionMethod
query = Queryable.Where(query, predicate);
// ReSharper restore LoopCanBeConvertedToQuery

How to install Mongo Db in a dev environment

Here's a good article: https://briancaos.wordpress.com/2014/10/01/sitecore-and-xdb-setting-up-mongodb-on-your-developer-machine/

Make sure you have the following in your ConnectionStrings.config :
  <add name="analytics" connectionString="mongodb://localhost/analytics" />
  <add name="tracking.live" connectionString="mongodb://localhost/tracking_live" />
  <add name="tracking.history" connectionString="mongodb://localhost/tracking_history" />
  <add name="tracking.contact" connectionString="mongodb://localhost/tracking_contact" />

Thursday 15 October 2015

Sitecore indexed results and paging

In the past I often used a query like this to bring back paged results from a query on a Sitecore index:
query.Skip(page*pageSize).Take(pageSize);
However, after some research I realized that this is inefficient and actually causes some issues because the query is performed in memory.

N.B. Sitecore uses a reduced set of Linq operators, be careful when refactoring Sitecore Linq statements as tools like reSharper will break Sitecore Linq statements

Instead the Sitecore suggested approach to do this is to use the Page extension method, that is part of the QueryableExtension class in Sitecore.Contentsearch.Linq.

The above would become:
query.Page(page, pageSize);
Much neater!

Extending TDS glass entity generation

One of the really cool features of Hedeghog's TDS (Team Developer for Sitecore - https://www.hhogdev.com/products/team-development-for-sitecore/overview.aspx) is that it can create Sitecore Glass entities automatically, when you synchronize specific portions of the Sitecore tree.  

One of the issues I came across was that when it creates properties for Sitecore link and list fields like DropLink and TreeListEx; it uses GUIDs rather than the entity in question.  So how do you get the code generation templates to link to the entity rather than to a GUID?

There is a really simple fix that involves 4 steps.


Let's assume that you have an entity called Product Filter which has a TreeListEx field, which references a list of Brand items.
  1. In the Sitecore core database navigate to the Field Types folder under system and create a copy of the TreeListEx field naming it something like Brand TreeListEx

    N.B. I also create a new folder to hold all my Entity Types so that they are easy to find when editing a template:


  2. In the template that uses the field; change the type to be Brand TreeListEx (or whatever you called it).

  3. In Visual Studio navigate to the TDS project that contains the code generation templates. 

    N.B. This is probably your Sitecore templates project. 

    Open up the code generation template for the glass items - in my code this was called glassv3item.tt.  Scroll down the script until you find the following method:

    public static string GetGlassFieldType(SitecoreField field)
    
    Locate the switch statement add the following on a new line:
    case "brand treelistex":
         return "IEnumerable<Brand>";
    N.B. The text in the case statement must be lowercase and must exactly match the name of the newly created field in the core database.

    N.B Always use IEnumerable for collections.

    N.B. For a single item reference field like a DropLink; change the return part to be something like:

    return "Brand";
  4. Now synchronize the Sitecore items project so that the Sitecore Glass code generation is redone, and you should then be good to go.