This is part 1 of a short series on developing service applications in SharePoint 2010:
- Part 1 (This Post): Getting Started with SAF + Starter Solution (with Logging / Configuration / Service Locator)
- Part 2: Custom Service Application – Logical Components
- Part 3: Custom Service Application – Base Solution
- Part 4: Custom Service Application – Full Infrastructure (with Admin UI and PowerShell)
- Part 5 (Final): Custom Service Application – Integrating features and capabilities into your service application (sample MailChimp integration)
I’ve built a couple service applications over the last 3 years or so, and I thought it might be fun to write a couple blog posts on the subject, especially given that SharePoint 2013 is just around the corner, and that the Service Application Framework (SAF) will continue to be a big part of SharePoint going forward. My perspective on service applications is that companies (especially large companies that have invested in SharePoint) can really take advantage of this framework to expose functionality throughout their SharePoint farm(s) in a way that allows them to centrally manage what and how things are deployed and distributed across SharePoint sites, departments, and the user-base.
Why develop a custom service application?
In this MSDN article, Microsoft points out that service applications offer 6 major things: application pool provisioning, database provisioning, claims-based security support, backup/restore/upgrade support, out-of-the-box load balancing, and custom permission rights. These are all really great things that companies I think can get excited about. The article then describes the types of business requirements that lend to investing in the development of a custom service application. I think this is where the article falls short. These business requirements don’t really sell SAF adequately, in fact they don’t really “jive” with the real-world type of requirements that I see companies having. Because these business requirements don’t share commonalities with what business and program managers actually face, these managers may not think of SharePoint SAF as a worthwhile investment, and in my mind are missing out on some real potential benefits in fully utilizing their SharePoint investment.
To me, the most attractive reason for building a service application is to logically bundle a set of services and/or capabilities provided by in-house applications and systems, as well as 3rd party (i.e.: NOT SharePoint) systems and expose those within the SharePoint infrastructure. Assuming SharePoint is being used heavily within an organization, these services and/or capabilities are easily distributed to users, and centrally managed by IT.
Here are some examples of services and/or capabilities that I’ve worked with that could be made available to SharePoint users in a centrally managed way:
- Exposing Bills of Materials (EBOMs or MBOMs) from an ERP system like SAP to SharePoint sites, and linking BOM parts and manufacturing routings to any significant project documentation, etc.
- Making customer information available from a CRM system like SalesForce.com available within a SharePoint portal
- Exposing engineering information from a PLM system like Enovia, or Windchill within SharePoint, and perhaps allowing users to visualize engineering processes and approve tasks managed within those PLM systems directly from within SharePoint.
- Accessing documents from any number of other internal CMS and collaboration systems in the enterprise, like eRoom, or FileNet.
- Accessing reports from enterprise reporting systems like Cognos, or SAS.
- … building integrations that merge SharePoint usefulness with any number of 3rd party systems used within an organization
Not only are service applications useful for integrating and extending the surface area of the tools and capabilities already in use within an organization, service applications are also useful for enabling new capabilities, for example:
- Creating a payment gateway integration application or set of services to easily process credit cards and track orders in a secure fashion (easily consumed by 3rd party applications, SharePoint web parts, SharePoint 2013 apps)
- Creating an email newsletter engine infrastructure that allows site/web/app owners to create and customize the look & feel of emails and notifications using templates
- And most generally, providing a singular infrastructure within an application to add capabilities to the SharePoint investment and manage new code in a central way by exposing new functionality using web services and custom UIs all-the-while storing data in a repository of choice that is managed apart of, or within the SharePoint database infrastructure (i.e.: data is NOT stored necessarily in SharePoint lists).
Some background on SAF development
When I built my first service application in 2009, there was very little information available and I had to use .NET Reflector to analyze the existing SharePoint service application code to figure out how SAF actually worked. I ended up discovering a maze of classes and methods, some real head-scratching as I recall.
If you were to google “sharepoint 2010 service application framework” now, you would find lots of information on the subject (much of it dated back to 2010 and 2011), including a training video by Andrew Connell (Creating Custom Service Applications), an overview slide-deck by Andy Nogueira (Service Applications in SharePoint 2010) and also some useful samples to examine, such as: autocompletedemo, Wingtip Calculator, SridharServiceApplication, CSSharePointCallClaimsAwareWCF, SuperCoolServiceApplication.
If you’re interested in SAF, there’s some interesting stuff out there. Some have highlighted the complexity of building a service application:
- From Andrew Connell’s video talk: this is “not for the faint of heart”
- From Sridhar’s blog: “This was not an easy joke at all and is pretty involved process.”
- From Todd Bleeker: “However, with over a dozen moving parts, Service Applications can be quite overwhelming to create.”
I think it’s well worth taking note of these warnings; however, the good news is that there is LOTS of information out there to help you build a custom service application infrastructure within your organization, and the task should not be as daunting now as it was at first.
Getting Started
In these few blog posts, I’ll layout a typical Visual Studio solution format I’ve used in the past for building and deploying a service application, something that’s generic enough to support most requirements an organization may have.
I’ll use a fictitious organization called “MyCorp” as an alias for a real corporation/organization. In this first post, I’ll do the following:
- Create the visual studio solution
- Create the base infrastructure needed to support typical SharePoint development within an organization, including:
- Setting up the service locator infrastructure
- Setting up the logging infrastructure
- Layout the Visual Studio projects to adequately decouple responsibility areas
[box style=”info”]NOTE: While this is just my way of laying out a solution, it is in NO WAY the definitive way. Your organization’s SharePoint Architect or Lead Developer should be able to do this using his/her own favorite strategy.[/box]
The Visual Studio Solution
The solution (which can be downloaded at the bottom of this post) looks as follows:
Couple comments on the above:
- MyCorp.SP.Core is a class library project that contains generic core code used across ALL SharePoint projects within an organization. It is meant to provide a single place for general stuff like custom extension methods, utility and helper classes, and core interfaces and means of accessing shared infrastructure by way of the service locator (more later on this …)
- MyCorp.SP.ServiceApplication.Common is a SharePoint project that we’ll use to deploy common SharePoint infrastructure artifacts (my preference is to minimize the amount of C# code in SharePoint projects unless the code is specifically related to feature installation/activation/deactivation, or tied to SharePoint modules, web parts, layout pages, etc…). As a general best practice, it’s my preference to keep business logic, service code in separate class library projects.
- MyCorp.SP.ServiceApplication.Lib is a class library project which will contain implementations of service interfaces as well as the ServiceApplication classes (which I’ll build in the next blog post).
Service Locator Implementation
To use the service locator, I create a generic static Locator class I can easily reference across my SharePoint projects.
using System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Practices.SharePoint.Common.ServiceLocation; using Microsoft.SharePoint; using MyCorp.SP.Infrastructure.ServiceLocator; namespace MyCorp.SP { public static class Locator { [DebuggerStepThrough] public static object Resolve(Type type) { ArgumentValidator.IsNotNull(type, "type"); return ServiceLocatorWrapper.GetInstance(type); } [DebuggerStepThrough] public static object Resolve(SPSite site, Type type) { ArgumentValidator.IsNotNull(type, "type"); return ServiceLocatorWrapper.GetInstance(site, type); } [DebuggerStepThrough] public static object Resolve(Type type, string name) { ArgumentValidator.IsNotNull(type, "type"); ArgumentValidator.IsNotEmpty(name, "name"); return ServiceLocatorWrapper.GetInstance(type, name); } [DebuggerStepThrough] public static object Resolve(SPSite site, Type type, string name) { ArgumentValidator.IsNotNull(type, "type"); ArgumentValidator.IsNotEmpty(name, "name"); return ServiceLocatorWrapper.GetInstance(site, type, name); } [DebuggerStepThrough] public static T Resolve<T>() { return Resolve<T>(null, null); } [DebuggerStepThrough] public static T Resolve<T>(SPSite site) { return Resolve<T>(site, null); } [DebuggerStepThrough] public static T Resolve<T>(string name) { return Resolve<T>(null, name); } [DebuggerStepThrough] public static T Resolve<T>(SPSite site, string name) { if (name.IsNullOrEmpty()) return ServiceLocatorWrapper.GetInstance<T>(site); else return ServiceLocatorWrapper.GetInstance<T>(site, name); } [DebuggerStepThrough] public static IEnumerable<T> ResolveAll<T>() { return ServiceLocatorWrapper.GetAllInstances<T>(); } [DebuggerStepThrough] public static IEnumerable<T> ResolveAll<T>(SPSite site) { return ServiceLocatorWrapper.GetAllInstances<T>(site); } [DebuggerStepThrough] public static IEnumerable<object> ResolveAll(Type type) { return ServiceLocatorWrapper.GetAllInstances(type); } [DebuggerStepThrough] public static IEnumerable<object> ResolveAll(SPSite site, Type type) { return ServiceLocatorWrapper.GetAllInstances(site, type); } [DebuggerStepThrough] public static void Reset() { SharePointServiceLocator.Reset(); } } }
The ServiceLocatorWrapper class is a wrapper around the SharePoint Service Locator available on codeplex.
I also created a LocatorRegistrar class that allows SharePoint projects to easily register interface implementations as well. The register/unregister methods look like the following.
[DebuggerStepThrough] public static void Register<TInterface, TService>(SPSite site, string name) where TService : TInterface, new() { var typeMappings = ServiceLocatorWrapper.GetInstance<IServiceLocatorConfig>(site); if (name.IsNullOrEmpty()) typeMappings.RegisterTypeMapping<TInterface, TService>(); else typeMappings.RegisterTypeMapping<TInterface, TService>(name); } [DebuggerStepThrough] public static void RegisterSingleton<TInterface, TService>(SPSite site, string name) where TService : TInterface, new() { var typeMappings = ServiceLocatorWrapper.GetInstance<IServiceLocatorConfig>(site); var singletonTypeMapper = typeMappings as ServiceLocatorConfig; if (singletonTypeMapper == null) return; if (name.IsNullOrEmpty()) singletonTypeMapper.RegisterTypeMapping<TInterface, TService>(null, InstantiationType.AsSingleton); else singletonTypeMapper.RegisterTypeMapping<TInterface, TService>(name, InstantiationType.AsSingleton); } [DebuggerStepThrough] public static void Unregister<TInterface>(SPSite site, string name) { var typeMappings = ServiceLocatorWrapper.GetInstance<IServiceLocatorConfig>(site); if (name.IsNullOrEmpty()) typeMappings.RemoveTypeMapping<TInterface>(null); else typeMappings.RemoveTypeMapping<TInterface>(name); } [DebuggerStepThrough] public static void UnregisterSingleton<TInterface>(SPSite site, string name) { var typeMappings = ServiceLocatorWrapper.GetInstance<IServiceLocatorConfig>(site); var singletonTypeMapper = typeMappings as ServiceLocatorConfig; if (singletonTypeMapper == null) return; if (name.IsNullOrEmpty()) singletonTypeMapper.RemoveTypeMapping<TInterface>(null); else singletonTypeMapper.RemoveTypeMapping<TInterface>(name); }
Finally, I use the FeatureInstalled and FeatureUninstalling methods of the Service Application Infrastructure feature receiver to install and uninstall the service locator mappings.
Within these methods, I call a ServiceLocatorInstaller class which does a couple things, registers diagnostic areas (see Logging section below) for logging in ULS, and registers service mappings using a ServiceLocatorRegistrar class.
using Microsoft.Practices.SharePoint.Common.ServiceLocation; using Microsoft.SharePoint; namespace MyCorp.SP.ServiceApplication.Common.ServiceLocator { internal static class ServiceLocatorInstaller { internal static void Install(SPFeatureReceiverProperties properties) { DiagnosticsAreaRegistrar.RegisterDiagnosticAreas(); var serviceLocator = SharePointServiceLocator.GetCurrent(); var typeMappings = serviceLocator.GetInstance(); var typeMapper = typeMappings as ServiceLocatorConfig; if (typeMapper == null) return; ServiceLocatorRegistrar.RegisterMappings(typeMapper); SharePointServiceLocator.Reset(); } internal static void Uninstall(SPFeatureReceiverProperties properties) { var serviceLocator = SharePointServiceLocator.GetCurrent(); var typeMappings = serviceLocator.GetInstance(); var typeMapper = typeMappings as ServiceLocatorConfig; if (typeMapper == null) return; ServiceLocatorRegistrar.RemoveMappings(typeMapper); SharePointServiceLocator.Reset(); DiagnosticsAreaRegistrar.UnregisterDiagnosticAreas(); } } }
The ServiceLocatorRegistrar class simply maps interface to implementation.
using Microsoft.Practices.SharePoint.Common.ServiceLocation; using MyCorp.SP.Infrastructure.Config; using MyCorp.SP.Infrastructure.Logging; namespace MyCorp.SP.ServiceApplication.Common.ServiceLocator { public class ServiceLocatorRegistrar { internal static void RegisterMappings(ServiceLocatorConfig typeMapper) { typeMapper.RegisterTypeMapping<IConfig, GeneralConfig>(null, InstantiationType.AsSingleton); typeMapper.RegisterTypeMapping<ILogger, Logger>(null, InstantiationType.AsSingleton); } internal static void RemoveMappings(ServiceLocatorConfig typeMapper) { typeMapper.RemoveTypeMapping(null); typeMapper.RemoveTypeMapping(null); } } }
Logging Implementation
For logging, I create a generic logging interface that looks like this:
using System; using Microsoft.SharePoint; namespace MyCorp.SP.Infrastructure.Logging { /// /// A logging interface for SharePoint /// public interface ILogger { /// /// Debug method interface /// ///The SPSite from which logging is being done. Pass null if not applicable. ///The category to which the message applies. Pass null if not applicable. ///The message to log. void Debug(SPSite site, string category, string message); /// /// Info method interface /// ///The SPSite from which logging is being done. Pass null if not applicable. ///The category to which the message applies. Pass null if not applicable. ///The message to log. void Info(SPSite site, string category, string message); /// /// Warn method interface /// ///The SPSite from which logging is being done. Pass null if not applicable. ///The category to which the message applies. Pass null if not applicable. ///The message to log. void Warn(SPSite site, string category, string message); /// /// Error method interface /// ///The SPSite from which logging is being done. Pass null if not applicable. ///The category to which the message applies. Pass null if not applicable. ///The message to log. void Error(SPSite site, string category, string message); /// /// Exception method interface /// ///The SPSite from which logging is being done. Pass null if not applicable. ///The category to which the message applies. Pass null if not applicable. ///The exception to log. void Exception(SPSite site, string category, Exception exception); } }
I then create a static Log class that can be easily used across SharePoint projects to access the ILogger implementation using the service locator.
using System; using Microsoft.SharePoint; using MyCorp.SP.Infrastructure.Config; using MyCorp.SP.Infrastructure.Logging; namespace MyCorp.SP { public static class Log { private static ILogger Logger { get { try { var logger = Locator.Resolve(); if (logger != null) return logger; } catch // we don't want logging to throw ANY exceptions { } return new NullLogger(); } } private static ILoggingConfig Config { get { try { var config = Locator.Resolve(); if (config != null && config.Logging != null) return config.Logging; } catch // we don't want logging to throw ANY exceptions { } return new DefaultConfig(); } } #region Debug public static void Debug(string message) { Debug(null, Config.DefaultCategory, message); } public static void Debug(string category, string message) { Debug(null, category, message); } public static void Debug(SPSite site, string message) { Debug(site, Config.DefaultCategory, message); } public static void Debug(SPSite site, string category, string message) { Logger.Debug(site, category, message); } public static void DebugFormat(string format, params object[] args) { Debug(null, Config.DefaultCategory, string.Format(format, args)); } public static void DebugFormat(SPSite site, string format, params object[] args) { Debug(site, Config.DefaultCategory, string.Format(format, args)); } public static void DebugFormat(SPSite site, string category, string format, params object[] args) { Logger.Debug(site, category, string.Format(format, args)); } #endregion // etc...
The Logger is a wrapper implementation around the Patterns & Practices Logging implementation which allows logging to both ULS and the Event log. It looks like the following:
using System; using Microsoft.Practices.SharePoint.Common.ServiceLocation; using Microsoft.SharePoint; using Microsoft.SharePoint.Administration; namespace MyCorp.SP.Infrastructure.Logging { public class Logger : ILogger { public const string DiagnosticAreaName = "MyCorp"; #region ILogger Members public void Debug(SPSite site, string category, string message) { WriteTrace(site, category, TraceSeverity.Verbose, message); } public void Info(SPSite site, string category, string message) { WriteTrace(site, category, TraceSeverity.Medium, message); } public void Warn(SPSite site, string category, string message) { WriteTrace(site, category, TraceSeverity.High, message); } public void Error(SPSite site, string category, string message) { WriteTrace(site, category, TraceSeverity.Unexpected, message); } public void Exception(SPSite site, string category, Exception exception) { WriteTrace(site, category, TraceSeverity.Unexpected, exception.ToString()); } #endregion private static void WriteTrace(SPSite site, string category, TraceSeverity severity, string message) { if (severity == TraceSeverity.None) return; try { var locator = (site == null ? SharePointServiceLocator.GetCurrent() : SharePointServiceLocator.GetCurrent(site)); if (locator == null) return; var logger = locator.GetInstance(); if (logger == null) return; var areaCategory = string.Concat(DiagnosticAreaName, "/", category); // USE ULS ONLY logger.TraceToDeveloper(message, 777, severity, areaCategory); ////USE EVENT LOG ////Run the EnsureDiagnosticArea.ps1 scripts on each server in the farm ////with a user that has Administrative rights on the server //EventSeverity eventSeverity; //switch (severity) //{ // case TraceSeverity.VerboseEx: // case TraceSeverity.Verbose: // eventSeverity = EventSeverity.Verbose; // break; // case TraceSeverity.Medium: // eventSeverity = EventSeverity.Information; // break; // case TraceSeverity.High: // eventSeverity = EventSeverity.Warning; // break; // case TraceSeverity.Monitorable: // eventSeverity = EventSeverity.Warning; // break; // case TraceSeverity.Unexpected: // eventSeverity = EventSeverity.ErrorCritical; // break; // default: // return; //} //logger.LogToOperations(message, 777, eventSeverity, areaCategory); } catch { // should not happen, but we don't want logging to stop anything } } } }
I commented out the Event Log logging, feel free to uncomment it if you prefer to use event log logging instead. In order to log to the event log, you’ll need to enable the custom diagnostic area on each server in the farm. I created a PowerShell script you can execute on each WFE (this is a one-time thing after the diagnostic areas have been registered in central admin) to enable this. The script is in the download (see: EnsureDiagnosticArea.ps1) and is adapted from Patrick Boom’s post here.
Now, in order to actually get the logging to work, we need to register the diagnostic areas. I do this using a DiagnosticsAreaRegistrar class which is called from the ServiceLocatorInstaller class above. Here’s the code for that.
using Microsoft.Practices.SharePoint.Common.Configuration; using Microsoft.Practices.SharePoint.Common.Logging; using Microsoft.Practices.SharePoint.Common.ServiceLocation; using Microsoft.SharePoint.Administration; using MyCorp.SP.Infrastructure.Logging; namespace MyCorp.SP.ServiceApplication.Common.ServiceLocator { internal static class DiagnosticsAreaRegistrar { #region Diagnostics public static void RegisterDiagnosticAreas() { DiagnosticsAreaCollection configuredAreas; var appAreas = GetDiagnosticAreas(out configuredAreas); foreach (var area in appAreas) { var existingArea = configuredAreas[area.Name]; if (existingArea == null) { configuredAreas.Add(area); } else { foreach (var c in area.DiagnosticsCategories) { var existingCategory = existingArea.DiagnosticsCategories; if (existingCategory == null) { existingArea.DiagnosticsCategories.Add(c); } else { if (existingCategory.EventSeverity != c.EventSeverity || existingCategory.TraceSeverity != c.TraceSeverity) { existingCategory.EventSeverity = c.EventSeverity; existingCategory.TraceSeverity = c.TraceSeverity; } } } } } configuredAreas.SaveConfiguration(); } public static void UnregisterDiagnosticAreas() { DiagnosticsAreaCollection configuredAreas; var appAreas = GetDiagnosticAreas(out configuredAreas); foreach (var area in appAreas) { var areaToRemove = configuredAreas[area.Name]; if (areaToRemove != null) { foreach (var c in area.DiagnosticsCategories) { var existingCat = areaToRemove.DiagnosticsCategories; if (existingCat != null) { areaToRemove.DiagnosticsCategories.Remove(existingCat); } } if (areaToRemove.DiagnosticsCategories.Count == 0) { configuredAreas.Remove(areaToRemove); } } } configuredAreas.SaveConfiguration(); } private static DiagnosticsAreaCollection GetDiagnosticAreas(out DiagnosticsAreaCollection configuredAreas) { var configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>(); configuredAreas = new DiagnosticsAreaCollection(configMgr); var appAreas = new DiagnosticsAreaCollection(); var newArea = new DiagnosticsArea(Logger.DiagnosticAreaName); newArea.DiagnosticsCategories.Add(new DiagnosticsCategory(LogCategory.General, EventSeverity.Verbose, TraceSeverity.Verbose)); newArea.DiagnosticsCategories.Add(new DiagnosticsCategory(LogCategory.ServiceApplication, EventSeverity.Verbose, TraceSeverity.Verbose)); appAreas.Add(newArea); return appAreas; } #endregion } }
Visualizing the ServiceLocator and Logging Implementation
Let’s now visualize the results of what we’ve just done, and make sure it all works.
Note: If you are doing any kind of SharePoint 2010 work, I highly recommend you use the Community Kit for SharePoint available on CodePlex to easily deploy/undeploy projects from visual studio, start/restart sharepoint processes and copy things to the GAC as needed. Another extremely useful tool is the VSCommands for Visual Studio 2010 extension, which allows you among other things to quickly debug from Visual Studio any application pool running within IIS (which is going to be imperative as we move further along in debugging our service application across the WCF service tiers).
First, we want to make sure the deployment process works. Here’s the output window after a clean deployments (see download).
After a clean deployments, let’s make sure the diagnostic areas were created in central admin.
Hmm! I may want to add a space in ServiceApplication for my next post . Also, I’ll probably want to default the EventLevel and TraceLevel to something a little higher than Verbose!
So everything is good so far, let’s make sure logging works. I’ll open the ULS Viewer or use the SharePoint LogViewer, and filter for the MyCorp product.
Looks like Logging is working as expected!
When we uncomment the Event logging, we’ll also see the logs show up in the event log!
So we now have our basic infrastructure ready for SharePoint development, and we are ready to build a service application for our organization.
We will start building our service application with all the bells and whistles in our next post. That’s gonna be a biggy!
[button link=”https://skydrive.live.com/redir?resid=30FD0C7F694C1B3F!293&authkey=!AJ9mKTemY0vGR_4″ color=”primary” target=”_blank” size=”large” title=”Code Download” icon_before=”download”]Code Download[/button]
Feel free to download, use and abuse the code as you see fit, and as always, use at your own risk , I mean pleasure. Have fun!
Hello Matt,
Thanks for such a wonderful article.
I am working on multiple Web Front End Environment and in my Farm have-
1 WFE
1 Application Server
1 Database Server
I have installed Server Solution and Client Solution on my Farm. When I access Mycorp Service Application using Application service URL it works perfectly fine but when i call the Url for MyCorp Service Application using WFE URL it gives me error as Failed to load type IServiceClientFactory, flushing service locator and retrying ExceptionType: ‘ActivationException’ ExceptionMessage: ‘Activation error occured while trying to get instance of type IServiceClientFactory, key “”‘ StackTrace: ‘ at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key) at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance[TService]() at MyCorp.SP.Infrastructure.ServiceLocator.ServiceLocatorWrapper.GetInstance[TService](SPSite site)’ Source: ‘Microsoft.Practices.ServiceLocation’ TargetSite: ‘System.Object GetInstance(System.Type, System.String)’ ———————————————————— Inner exception: ——————————————-…
Microsoft.Practices.ServiceLocation.ActivationException: No type mapping was registered for type ‘IServiceClientFactory’ and key ”. at Microsoft.Practices.SharePoint.Common.ServiceLocation.ActivatingServiceLocator.DoGetInstance(Type serviceType, String key) at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key)
Can you please help me.
Thanks,
Piyush
My first thought is to ensure your service locator cache is refreshed. The mappings are stored as farm configuration so should technically behave the same regardless of being on the app server / wfe server. First test would be to recycle IIS on the WFE(s), see if that does anything (it may not have an effect since service locator mappings are stored as config properties / property bag settings in the db, but it will rule it out). Another test would be to reset the locator, possibly run this on the WFE (I use something like LinqPad).
SharePointServiceLocator.Reset();
Hello Matt,
Worked Perfectly well… Thanks once again!!!
I have changed my Custom Service Application as per your thoughts. So now my Farm is as follows –
1 Web Front End
1 Application Server
1 Database Server
Services enabled on Web Front End –
1. Central Administration
2. Microsoft SharePoint Foundation Incoming E-Mail
3. Microsoft SharePoint Foundation Web Application
4. Microsoft SharePoint Foundation Workflow Timer Service
Services enabled on Application Server –
1. All Service Application
2. My Custom Service Application
Now, I have Custom Solutions (wsp) which gets deployed at Web Application Level and Custom Service Solution (wsp) gets deployed at Farm Level. in one of my solution (wsp) I have custom web templates, Master Pages, Page Layouts and that solution gets deployed at Web Application Level
In my Custom Service Application which gets executed on Application Server I have a logic which creates Sub Site. But while creating Sub Site it says Web Template not available and this is because Web Application Service is not enabled on Application Server. If I execute same code via PowerShell on WFE machine it works.
Could you guide me in a manner that, My Custom Service Application on my Application Server will create Sub Site using custom Web Template.
Thanks in advance.
Regards,
Piyush
Great work, Matt. A lot more detailed than Andrew’s wingtip calculator.
One questtion if you can help me:
If a NONE-SharePoint consumer, like javascript or non-.NET desktop app, how can it access this service, like any regular WCF service other than using the ServiceClient in the Core project?
thanks a lot in advance.
Hey SP13,
Interesting question. In my implementations of custom service applications, I don’t let javascript and .NET desktops apps talk directly to the service application services (for various reasons: domain/trust issues, WCF protocols and bindings, ports and firewall issues, unexpected problems that I would endlessly have to troubleshoot because they are out of my control, etc…). Also, because the services are on app servers, and not necessarily on the web front-ends, it doesn’t always make sense for external applications to access these service directly. I also like to use TCP as the protocol between the WFEs and the app services, and I prefer to have external apps integrate over HTTP(s) which is simple to monitor, control, load-balance, and secure.
Most importantly, my services don’t always have the type of interface that I would want external apps to talk to. It’s much easier, IMO and for me, to just package up another set of services (WCF services that go into the _vti_bin configured for HTTPS, or config free ServiceStack services), and have external apps talk to these, and that way I can use XML or JSON. DTOs are simpler to work with and the client api is simple (jquery, a json parser and/or System.Net is all that’s needed).
It does seem that this creates a double hop from client to WFE to service app, but I’ve personally not run into any performance issues, especially since I cache things where needed. Hope that helps and answers your question in a round-a-bout way.
That said, I still use the ServiceClient directly in powershell and my LinqPad scripts to do stuff quickly as needed on the servers.
Hi Matt
I have a question about IOC:
Why have you chosen to use the Service Locator Pattern, instead of a DI-Container (like Autofac you have already used in the SharPoint context)?
In SharePoint, the service locator has some advantages I’ve found. The P&P implementation stores it’s mappings in the SharePoint database, which means that you can use the service locator to easily retrieve a service within any web application in your SharePoint farm without any web.config and global.asax changes. Also, the service locator can be used within PowerShell commandlets and Linqpad (which I use a ton), so you don’t need to have access to an HttpContext or SPContext to retrieve service instances. That way, I can write scripts all day that work outside of SharePoint that run on the SharePoint server and call my service application services using a generic client api (with internal calls to the service locator). Then there is the specific scenario of a service application. In this service application which I think is the standard practice, the web front-end does not do much besides call WCF services and work with DTOs and the service client instances are pretty stateless in our case (not a requirement) and I instantiate them using a factory which is registered as a singleton (i.e.: the client factory) so we’re not really needing to manage the lifetime of a service within an HttpRequest on the web front-ends a.l.a. Autofac ILifetimeScope. Autofac DI is nice to me when you need to have something managed (like a database connection or unit of work implementation) because it makes sure that service is disposed at the end of the request, but the components that implement IDisposable are on the app server here in my service application and they are all instantiated and disposed of within each service call, so the web front ends don’t need that extra touch imo, and the stuff that runs on the app server is more unit based then request based. You could use Autofac if you wanted the WFEs to talk directly to your mongodb, or datasource for example. You also have to consider the extra complexity of integrating Autofac, when you have something like the service locator at your disposal that can do probably most of what you need (you just have to factor the lack of DI into your code base). Anyway, I hope that helps.
Good explanation on SharePoint Service by developing SharePoint i definitely see your post at that time your post might be very useful to get new thing in application.