Sunday, August 8, 2010

SharePoint Service Locator

Microsoft has released SharePoint Guidance 2010. One of the important part of the guidance is SharePoint Service Locator. With this SharePoint Service Locator, you can easily decouple interface consumers from its implementation. You can get more details on SharePoint Service Locator from MSDN.

How to use Service Locator?

  1. First of all download the SharePoint guidance from this MSDN link.
  2. Now add reference to the following two dlls in your project (SharePoint Project) to use SharePoint Service Locator. You may need to compile the source code after downloading from MSDN link as the guidance may not have dlls compiled.
    • Microsoft.Practices.SharePoint.Common.dll
    • Microsoft.Practices.ServiceLocation.dll
  3. Write a bootstrapper class which will register your type mappings. Let’s consider the following example, where I have an interface ILogger and it’s implementation EventLogger.
    public interface ILogger
    {
        void WriteLog(string message);
        void WriteException(Exception exception);
    }
    
    
    
    public class EventLogger:ILogger
    {
        public void WriteLog(string message)
        {
                
        }
    
        public void WriteException(Exception exception)
        {
                
        }
    }

    Now to map the ILogger to EventLogger using SharePoint Service Locator you’ll use the type mapping code block shown below:

    var serviceLocator = SharePointServiceLocator.GetCurrent();
    var typeMapper = serviceLocator.GetInstance<IServiceLocatorConfig>();
    typeMapper.Site = SPContext.Current.Site;
    typeMapper.RegisterTypeMapping<ILogger, EventLogger>();

    So the above code block shows how to register type mappings using SharePoint Service Locator.

  4. Finally you need to put the type mapping code (shown in step 3) in some startup place so that before any type is asked from the type mapping the type is registered. In desktop application, you could put the type mapping code during project startup and in web application we usually put the type mapping in Application_Start event. However in SharePoint we can do this type mappings using Feature Event Receiver.
    public class ServiceLocatorFeatureEventReceiver : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            var currentSite=properties.Feature.Parent as SPSite;
            var serviceLocator = SharePointServiceLocator.GetCurrent();
            var typeMapper = serviceLocator.GetInstance<IServiceLocatorConfig>();
            typeMapper.Site = currentSite;
            typeMapper.RegisterTypeMapping<ILogger, EventLogger>();
            SharePointServiceLocator.Reset();
        }
    
    
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            var currentSite=properties.Feature.Parent as SPSite;
            var serviceLocator = SharePointServiceLocator.GetCurrent();
            var typeMapper = serviceLocator.GetInstance<IServiceLocatorConfig>();
            typeMapper.Site = currentSite;
            typeMapper.RemoveTypeMapping<ILogger>();
            SharePointServiceLocator.Reset();
        }
    }

    As shown in the above code snippet, activating/deactivating the feature will register/unregister type. The SharePointServiceLocator.Reset() method will reset the type mappings in current Appdomain.

  5. Now you can use the registered types in your code as shown below:
var logger  = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();

 

FAQ

  • Can I control the scope of type mappings available?

Yes, you can. However, there’s only two scopes you can define your type mappings: Farm and Site Collection. By default type mapping are kept in Farm Configuration settings. However, if you set the ServiceLocatorConfig.Site property (as I have done in step 4) then the type mapping is kept in site collection Configuration Settings. For site collection scope, your feature event receiver (for type mapping) will have Site scope. Similarly if you want your type mappings will be available at Farm level, then you need to use a feature receiver with Farm level scope. If you try to put your mappings in Farm level by using an feature event receiver from site or web level you’ll get access denied error

  • How to refresh type mappings?

Service locators for farm and site level are cached and refresh periodically. By default the time is 60sec. You can change the refresh interval time by calling  SetSiteCacheInterval method in IServiceLocatorConfig interface. However, if you need to refresh typemappings immediately, then you can deactivate/activate the feature of which event receiver is used for type mappings.

  • If I reset IIs will my mappings go away?

No, the type mappings not kept in w3p (IIS process) rather in site configuration settings (or property bags) which does not reset with IIS restart.

Access Deined Error:

During development you may get the following error:

Configsetting with key 'Microsoft.Practices.SharePoint.Common.TypeMappings' could not be set 'Microsoft.Practices.SharePoint.Common.ServiceLocation.ServiceLocationConfigData' with type 'Microsoft.Practices.SharePoint.Common.ServiceLocation.ServiceLocationConfigData'. The technical exception was: System.Security.SecurityException: Access denied.

You may get the error when you’ll try to use SharePoint Service locator to register types. The problem might be in this case, you have a feature with site or web level scope and you are trying to register types for firm level. You may even get the error if you try to register types for site scope from a feature with web scope. If you want to use Site scope for type mapping set the Site property of IServiceLocatorConfig interface before registering any type as shown below:

var serviceLocator = SharePointServiceLocator.GetCurrent();
var typeMapper = serviceLocator.GetInstance<IServiceLocatorConfig>();
typeMapper.Site = currentSite;
typeMapper.RegisterTypeMapping<ILogger, EventLogger>();

Conclusion

Service Locator usage will make the code loosely coupled. It’ll also make room for putting unit test in code as using Mock implementation of a class and Service Locator we can easily bypass the SharePoint Dependency in unit testing. I’ll try to explain the unit testing stuffs using Service Locator in another post hopefully. For more details on SharePoint Guidance 2010 you can download the whole guidance stuffs from this MSDN link which is surely very useful resource for SharePoint Developers.

9 comments:

  1. Do you hook the mapping feature receiver to a separate feature than the feature that contains the element that will locate the service?

    ReplyDelete
  2. the mapping feature receiver will add the mapping and you can then use the mapping anywhere in your code: in feature receiver, webpart etc. So if you want to access the type mapping in a feature other than the feature that added the mapping, will work.

    ReplyDelete
  3. How would you go about using the Service Locator to consume a WCF Service? Would you generate a proxy for the service (interface and implementation), and then add the mapping at the Farm level?

    ReplyDelete
  4. Yes you need to register the type mappings of WCF interface and implementation. However, If your service needs to be accessible from the farm level then u can add at the firm level. Otherwise, if you have different scope (like site collection) then you need to add the mappings in the corresponding scope.

    ReplyDelete
  5. Isn't the whole idea behind the ServiceLocator to hide away the actual ioc container being used and provide a common interface for this?

    When we call SharePointServiceLocator.GetCurrent(); haven't we tightly coupled our application to the specific SharePointServiceLocator implementation or have I misunderstood something?

    ReplyDelete
  6. Rasmus, when you use SharePointServiceLocator, you can hide your IoC Container. You can use Unity or Spring.net or anything else. SharePointServiceLocator just hides the Ioc Container whether it's SharePoint Service Locator or any other.

    ReplyDelete
  7. where is the implementation of this class?

    public class EventLogger:ILogger
    {
    public void WriteLog(string message)
    {

    }

    public void WriteException(Exception exception)
    {

    }
    }

    ReplyDelete
  8. @Rod, I tried to explain how the service locator can be used for type mapping. And that's why I just skipped the logger details.

    ReplyDelete

Note: Only a member of this blog may post a comment.