2

I have a custom index which has additional teaser title, teaser description, main title and main content fields and the index is resolved based on Sitecore.Context.Database. Eg. master database context will resolve it to myproject_page_master_index while web database will resolve it to myproject_page_web_index.

I'm using Sitecore 8.2 latest version

I have following Repository:

public class PageSearchRepository : IPageSearchRepository
{
    private readonly ISearchIndex _index;

    public PageSearchRepository(ISearchIndex index)
    {
        _index = index;
    }

    public PageSearchResults FindByKeywords(string[] keywords, int take, int skip)
    {
        using (IProviderSearchContext context = this._index.CreateSearchContext())
        {
            var baseQuery = PredicateBuilder.True<PageSearchResultItem>()
                .And(item => item.LatestVersion)
                .And(item => item.Language == Sitecore.Context.Language.Name);
            foreach (string keyword in keywords)
            {
                var filterQuery = PredicateBuilder.False<PageSearchResultItem>()
                    .Or(item => item.Content.Like(keyword))
                    .Or(item => item.TeaserTitle.Like(keyword))
                    .Or(item => item.TeaserContent.Like(keyword));
                baseQuery = baseQuery.And(filterQuery);
            }

            var searchResults = context.GetQueryable<PageSearchResultItem>().Filter(baseQuery).GetResults();
            int count = searchResults.Hits.Count();
            var results = searchResults.Hits.Select(i => i.Document).Skip(skip).Take(take).ToList();

            return new PageSearchResults()
            {
                Results = results,
                TotalResults = count
            };
        }
    }
}

I have following service that uses the repository:

public class PageSearchService : IPageSearchService
{
    private IPageSearchRepository _pageSearchRepository;

    public PageSearchService(IPageSearchRepository pageSearchRepository)
    {
        this._pageSearchRepository = pageSearchRepository;
    }

    public PageSearchResults GetPagesBySearchQuery(string searchQuery, int page, int perpage)
    {
        int take = perpage;
        int skip = (page - 1) * perpage;
        string[] keywords = searchQuery.Split(' ');
        return this._pageSearchRepository.FindByKeywords(keywords, perpage, skip);
    }
}

I have following controller that uses the service:

public class SearchPageController : Controller
{
    private IPageSearchService _pageSearchService;

    public SearchPageController(IPageSearchService pageSearchService)
    {
        this._pageSearchService = pageSearchService;
    }

    [HttpGet]
    public ActionResult Index(string query, int page = 1, int perpage = 10)
    {
        var pages = this._pageSearchService.GetPagesBySearchQuery(query, page, perpage);
        var model = new SearchPageViewModel()
        {
            Pages = pages.Results,
            PageNumber = page,
            NumPages = (pages.TotalResults + perpage -1) / perpage ,
            SearchQuery = query, 
            PerPage = perpage,
            BaseUrl = Sitecore.Context.Item.Url()
        };
        return View(model);
    }
}

My Dependencies are registered as follows:

    public class DependenciesRegistration : IServicesConfigurator
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public void Configure(IServiceCollection serviceCollection)
    {
        serviceCollection
            .AddScoped<ISitecoreContext, SitecoreContext>()
            .AddScoped<IPageSearchRepository, PageSearchRepository>()
            .AddScoped<IPageSearchService, PageSearchService>();
        serviceCollection.AddMvcControllersInCurrentAssembly();

        var service = new ServiceDescriptor(typeof(BaseLinkManager), typeof(SwitchingLinkManager), ServiceLifetime.Singleton);
        serviceCollection.Replace(service);
    }
}

How I can inject the correct context to the repository so that my repository has correct ISearchProviderContext without writing the code to do that in repository class itself?

  • Are you asking on how to best use DI to get Sitecore.Context in your repository? – George Chang Nov 26 '18 at 19:54
1

Instead of injecting it I would use the ContentSearchManager to grab the desired index.

Example:

ContentSearchManager.GetIndex($"myproject_page_{Context.Database.Name}_index")
1

Other DI framework such as StructureMap allows you to create a conditional resolver during registration which allows you to configure different object to be passed to the concrete class constructor, for example

For<ISearchIndex>().Use("some description", c => {
  return ContentSearchManager.GetIndex($"myproject_page_{Sitecore.Context.Database.Name}_index")
});

Obviously that's not available in the Sitecore's default DI framework, which is the Microsoft DI framework. Not that there aren't different ways to achieve the same thing.

Instead of passing the ISearchIndex to your repository class constructor, you can instead pass a IPageIndexResolver to your repository class to move the logic to resolve the correct search index during runtime.

public interface IPageSearchIndexResolver{
  ISearchIndex GetPageSearchIndex();
}

public class PageSearchIndexResolver{
  public ISearchIndex GetPageSearchIndex(){
    return ContentSearchManager.GetIndex($"myproject_page_{Sitecore.Context.Database.Name}_index")
  }
}

public class PageSearchRepository{
  public PageSearchRepository(IPageSearchIndexResolver pageSearchIndexResolver){
    _index = pageSearchIndexResolver.GetPageSearchIndex();
  }
}

I prefer to use this approach instead of the conditional object registration which StructureMap provides, because it allows you to encapsulate the resolver logic into it's own class, instead of littering the dependency container registration with conditional logic.

0

I would add one thing here, be careful where your Application Dependency Graph is defined. It should be as close to the application entry point as possible. You should avoid this type of logic bleeding into your application code as its strictly not part of your application but part of bootstrapping IMO.

A solution with lots of these types resolvers may become a maintenance issue as the composition roots footprint can start to spread. The MS container defines a common abstraction where you can easily replace it with another feature-rich IOC container where you can use a factory to return a type.

I disagree with the common abstraction and usually champion the idea of a second container that contains your application registrations with focused adaptors back to the Sitecore container for Sitecore dependencies. If you want to find out more of my motivation behind this have a read of a blog article I published on the topic.

Whatever approach you choose to take think about maintenance as at the end of the day this is the biggest cost in Software Development.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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