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']"/>