How to use custom SolrNet calls with Sitecore

Director, Sitecore Practice
  • Twitter
  • LinkedIn

How to use custom SolrNet calls with Sitecore


You have created custom SolrNet calls, but may have encountered the following exception:

Exception has been thrown by the target of an invocation.
---> Microsoft.Practices.ServiceLocation.ActivationException: 
Activation error occured while trying to get instance of type ISolrOperations1, key "Some_index"
---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.

The issue is both Sitecore and SolrNet relies on a ServiceLocator to get Solr operations. When using SolrNet you initialize it like so:

Startup.Init(_url);
_solr = ServiceLocator.Current.GetInstance>();

Sitecore also initializes a custom locator provider.

ServiceLocator.SetLocatorProvider((ServiceLocatorProvider) (() => (IServiceLocator) new DefaultServiceLocatorstring, object>>(this.Operations)));

Depending on which gets executed last, ServiceLocator.Current.GetInstance either resolves to Sitecore s locator or SolrNet s locator.

Solution

The solution is to create a service locator wrapper which switches between SolrNet s service locator and Sitecore s locator. We will then replace Sitecore s DefaultSolrStartUp that sets the locator provider to our wrapper. Lastly, we will replace the InitializeSolrProvider which calls the DefaultSolrStartUp to our own SharedSolrStartUp.

Service Locator wrapper

public class SharedServiceLocator : IServiceLocator
{
   private const string SitecoreSolrDll = "Sitecore.ContentSearch.SolrProvider";
   IServiceLocator _sitecoreLocator;
   IServiceLocator _solrLocator;

   public SharedServiceLocator(IServiceLocator solrLocator)
   {
        _solrLocator = solrLocator;
   }

   public void SetSitecoreServiceLocator(IServiceLocator sitecoreLocator)
   {
       _sitecoreLocator = sitecoreLocator;
   }

   [MethodImplAttribute(MethodImplOptions.NoInlining)]
   IEnumerable<object> IServiceLocator.GetAllInstances(Type serviceType)
   {
       return (Assembly.GetCallingAssembly().FullName.StartsWith(SitecoreSolrDll) ? _sitecoreLocator : _solrLocator).GetAllInstances(serviceType);
   }

   [MethodImplAttribute(MethodImplOptions.NoInlining)]
   IEnumerable IServiceLocator.GetAllInstances()
   {
       return (Assembly.GetCallingAssembly().FullName.StartsWith(SitecoreSolrDll) ? _sitecoreLocator : _solrLocator).GetAllInstances();
   }

    [MethodImplAttribute(MethodImplOptions.NoInlining)]
    object IServiceLocator.GetInstance(Type serviceType)
    {
        return (Assembly.GetCallingAssembly().FullName.StartsWith(SitecoreSolrDll) ? _sitecoreLocator : _solrLocator).GetInstance(serviceType);
    }

    [MethodImplAttribute(MethodImplOptions.NoInlining)]
    object IServiceLocator.GetInstance(Type serviceType, string key)
    {
         return (Assembly.GetCallingAssembly().FullName.StartsWith(SitecoreSolrDll) ? _sitecoreLocator : _solrLocator).GetInstance(serviceType, key);
    }
     [MethodImplAttribute(MethodImplOptions.NoInlining)]     TService IServiceLocator.GetInstance()     {         return (Assembly.GetCallingAssembly().FullName.StartsWith(SitecoreSolrDll) ? _sitecoreLocator : _solrLocator).GetInstance();     }
     [MethodImplAttribute(MethodImplOptions.NoInlining)]     TService IServiceLocator.GetInstance(string key)     {         return (Assembly.GetCallingAssembly().FullName.StartsWith(SitecoreSolrDll) ? _sitecoreLocator : _solrLocator).GetInstance(key);     }
     [MethodImplAttribute(MethodImplOptions.NoInlining)]     object IServiceProvider.GetService(Type serviceType)     {         return (Assembly.GetCallingAssembly().FullName.StartsWith(SitecoreSolrDll) ? _sitecoreLocator : _solrLocator).GetService(serviceType);     }
}

Shared Solr StartUp

Let's replace Sitecore s DefaultSolrStartUp and set the locator provider to our wrapper. The majority of this code is borrowed from Sitecore's DefaultSolrStartUp.

public class SharedSolrStartUp : ISolrStartUp, IProviderStartUp
{
     private readonly DefaultSolrLocatorstring, object>> operations;
     private readonly ISolrCache solrCache;
     private SharedServiceLocator _sharedServiceLocator;

     protected internal virtual DefaultSolrLocatorstring, object>> Operations
     {
         get
         {
             return this.operations;
         }
     }

     public SharedSolrStartUp(SharedServiceLocator sharedServiceLocator)
     {
         if (SolrContentSearchManager.EnableHttpCache)
            this.solrCache = (ISolrCache)new HttpRuntimeCache();

         this.operations = new DefaultSolrLocatorstring, object>>();
         _sharedServiceLocator = sharedServiceLocator;
     }

     public virtual void AddCore(string coreId, Type documentType, string coreUrl)
     {
         ISolrConnection connection = this.CreateConnection(coreUrl);
         this.Operations.AddCore(coreId, documentType, coreUrl, connection);
     }

     public virtual void Initialize()
     {
         if (!SolrContentSearchManager.IsEnabled)
             throw new InvalidOperationException("Solr configuration is not enabled. Please check your settings and include files.");

         this.Operations.DocumentSerializer = (ISolrDocumentSerializerstring, object>>)new SolrFieldBoostingDictionarySerializer(this.Operations.FieldSerializer);
         this.Operations.HttpWebRequestFactory = SolrContentSearchManager.HttpWebRequestFactory;
         this.Operations.SchemaParser = (ISolrSchemaParser)new Sitecore.ContentSearch.SolrProvider.Parsers.SolrSchemaParser();

        foreach (string core in SolrContentSearchManager.Cores)
         {
            this.AddCore(core, typeof(Dictionary<string, object>), string.Format("{0}/{1}", (object)SolrContentSearchManager.ServiceAddress, (object)core));

            if (SolrContentSearchManager.EnableHttpCache)
                this.Operations.HttpCache = this.solrCache;

            this.Operations.CoreAdmin = this.BuildCoreAdmin();

            _sharedServiceLocator.SetSitecoreServiceLocator(new DefaultServiceLocatorstring, object>>(this.Operations));
            ServiceLocator.SetLocatorProvider(() => _sharedServiceLocator);

            SolrContentSearchManager.SolrAdmin = (ISolrCoreAdmin)this.Operations.CoreAdmin;
            SolrContentSearchManager.Initialize();
        }

        public virtual bool IsSetupValid()
        {
            if (!SolrContentSearchManager.IsEnabled)
                return false;

            ISolrCoreAdminEx admin = this.BuildCoreAdmin();

            return SolrContentSearchManager.Cores.Select<string, CoreResult>((Func<string, CoreResult>)(defaultIndex => admin.Status(defaultIndex).First())).All
                  ((Funcbool>)(status => status.Name != null));
        }

        protected virtual ISolrCoreAdminEx BuildCoreAdmin()
        {
            return this.Operations.BuildCoreAdmin(this.CreateConnection(SolrContentSearchManager.ServiceAddress));
        }

        protected virtual ISolrConnection CreateConnection(string serverUrl)
        {
            SolrConnection solrConnection = new SolrConnection(serverUrl);

            solrConnection.Timeout = SolrContentSearchManager.ConnectionTimeout;

            if (this.solrCache != null)
                solrConnection.Cache = this.solrCache;

            return (ISolrConnection)solrConnection;
        }
    }
}

Initialize pipeline

Here we will initialize SolrNet as well as Sitecore's.

using Microsoft.Practices.ServiceLocation;
using Sitecore.ContentSearch.SolrProvider;
using Sitecore.ContentSearch.SolrProvider.SolrNetIntegration;
using Sitecore.Pipelines;
using SolrNet;

namespace YourNameSpace
{
   public class InitializeSharedSolrProvider
   {
       public void Process(PipelineArgs args)
       {
            if (!SolrContentSearchManager.IsEnabled) return;

            if (IntegrationHelper.IsSolrConfigured())
            {
                IntegrationHelper.ReportDoubleSolrConfigurationAttempt(this.GetType());
            }
            else
            {
                Startup.Init("your solr url");
                Startup.Init("your solr url");

                //Place any custom SolrNet init prior to this line
                var sharedSolrStartUp = new SharedSolrStartUp(new SharedServiceLocator(ServiceLocator.Current));
                //init Sitecore's solr
                sharedSolrStartUp.Initialize();
            }
       }
   }
}

Sitecore Configuration

Lastly, we'll patch Sitecore to use your InitializeSharedSolrProvider pipeline.


  
    
      
        "YourNameSpace.InitializeSharedSolrProvider, Assembly"
               patch:instead="*[@type='Sitecore.ContentSearch.SolrProvider.Pipelines.Loader.InitializeSolrProvider, Sitecore.ContentSearch.SolrProvider']"/>
      
    
  


Related Blogs

Latest Blogs