How can I stop using ServiceLocator?

Vote:
 

I am using ServiceLocator when I for example need to get hold of the IContentRepository

var contentRepository = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance();

I have understand that ServiceLocator works but it is not good coding to do it like this.

Does anyone have any examples to give me that shows how to do it in a better way?

#115827
Jan 20, 2015 7:40
Vote:
 

Best practice, as least that I'm aware of, is to take in dependencies in the constructor of your object where possible and to set this as a variable that can then be used by the class. This way, it becomes very clear what dependencies the class has (try to avoid hidden depdencies where possible, for instance usages of static classes or extension methods). For instance:

protected DepencendyClass _yourDependency;

public YourClass(DepencendyClass yourDependency)
{
    _yourDependency = yourDependency;
}
#115828
Jan 20, 2015 7:57
Vote:
 

Thanks Linus.

Ok, that was what I thought on how to do it, doing like that will make StructureMap handle it.

Many times it is use is in static helper classes. For example in the alloy site it is used like this:

using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using EPiServer.Core;
using EPiServer.Globalization;
using EPiServer.ServiceLocation;
using EPiServer.Web.Routing;
using EPiServer;

namespace EPiServerFindTestSite.Helpers
{
    public static class UrlHelpers
    {
        /// <summary>
        /// Returns the target URL for a ContentReference. Respects the page's shortcut setting
        /// so if the page is set as a shortcut to another page or an external URL that URL
        /// will be returned.
        /// </summary>
        public static IHtmlString PageLinkUrl(this UrlHelper urlHelper, ContentReference contentLink)
        {
            if(ContentReference.IsNullOrEmpty(contentLink))
            {
                return MvcHtmlString.Empty;
            }

            var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
            var page = contentLoader.Get<PageData>(contentLink);

            return PageLinkUrl(urlHelper, page);
        }

        /// <summary>
        /// Returns the target URL for a page. Respects the page's shortcut setting
        /// so if the page is set as a shortcut to another page or an external URL that URL
        /// will be returned.
        /// </summary>
        public static IHtmlString PageLinkUrl(this UrlHelper urlHelper, PageData page)
        {
            var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
            switch (page.LinkType)
            {
                case PageShortcutType.Normal:
                case PageShortcutType.FetchData:
                    return new MvcHtmlString(urlResolver.GetUrl(page.ContentLink));

                case PageShortcutType.Shortcut:
                    var shortcutProperty = page.Property["PageShortcutLink"] as PropertyPageReference;
                    if (shortcutProperty != null && !ContentReference.IsNullOrEmpty(shortcutProperty.ContentLink))
                    {
                        return urlHelper.PageLinkUrl(shortcutProperty.ContentLink);
                    }
                    break;

                case PageShortcutType.External:
                    return new MvcHtmlString(page.LinkURL);
            }
            return MvcHtmlString.Empty;
        }

        public static RouteValueDictionary GetPageRoute(this RequestContext requestContext, ContentReference contentLink)
        {
            var values = new RouteValueDictionary();
            values[RoutingConstants.NodeKey] = contentLink;
            values[RoutingConstants.LanguageKey] = ContentLanguage.PreferredCulture.Name;
            return values;
        }
    }
}

As I understand, setting the DI the same way as in a createable class does not work with StructureMap. Do you know the best way to rewrite this class without any ServiceLocator?

#115835
Jan 20, 2015 8:12
Vote:
 

Our approach has been to create two overloads, for instance for extension methods. Then we make sure that the second overload takes all depencencies as parameters and test this method. This way, the only thing that the "simpler" overload does is to fetch some dependencies from the IOC container which is nothing that you want to test anyway.

#115836
Jan 20, 2015 8:25
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.