Friday, May 3, 2013

Adding custom WCF services to a SharePoint 2013 farm solution using Visual Studio 2012

In this blog post I will explain how to add and deploy a custom WCF service as a part of a SharePoint 2013 farm solution using Visual Studio 2012.

Back in the SharePoint 2010 days we all learned to love and use the Community Kit for SharePoint Development Tools (CKSDev), the king of Visual Studio add ons for SharePoint developers which extended the Visual Studio 2010 with numerous SharePoint related options and project item templates, making SharePoint development so much easier.

One of these templates was the Custom WCF Service template which made it easy to add a custom WCF service to your SharePoint 2010 solution. At the time of this writing, the CKSDev Tools were upgraded to the version 1.1 which made them compatible with Visual Studio 2012. However, many of the features from the 2010 version - most notably the SharePoint related project item templates such as Custom WCF Service - are missing from this release. The missing features will most certailnly be re-added in the coming versions, but if you’re in a hurry, this article will show you how to add a custom WCF service to your SharePoint 2013 project in Visual Studio 2012 per hand, without any add ons at all.

Before you begin, make sure to update your version od the Microsoft Office Developer Tools for Visual Studio 2012 the Visual Studio 2012 itself to the latest versions. This is especially important if you are still using the pre-RTM version of the Office Developer Tools, since this version has a many problems when developing against the RTM version of SharePoint 2013.

Begin by creating a new SharePoint 2013 empty project. In the Visual Studio 2012 menu select File>New>Project and select the “SharePoint 2013 – Empty Project” template from the SharePoint Solutions template group:

01_CreateNewProject

I named the project VS2012WcfService but you can name it what you want.

The SharePoint Customization Wizard will display asking you to select the deployment options for your new solution. Select a valid SharePoint URL to be used for deployment and F5 debugging from Visual Studio 2012 and select the “Deploy as a farm solution” option:

02_ProjectOptions

Please note that the URL you select here must point to the local SharePoint installation. If you are using Host Names Site Collections in your dev environment, it is likely that Visual Studio 2012 will not recognize the site URL as a local URL and throw the following error:

image

If that happens, simply open your hosts file under C:\Windows\System32\drivers\etc, add the following line to it and save it:

127.0.0.1   team.contoso.com

Replace team.contoso.com with the URL of the site you want to use for the debugging. After that the above error should disappear and you should be able to use your Host Named site for debugging.

First add the necessary references to yourt project. You need to add the reference to the following two dlls:

  • Microsoft.SharePoint.Client.ServerRuntime 15.0.0.0
  • System.ServiceModel 4.0.0.0

image

The next step is to add the ISAPI SharePoint mapped folder to the project. The ISAPI folder is the special folder within the SharePoint hive where all WCF services should be deployed. This folder is mapped to the “_vti_bin” alias in the IIS, so that services deployed to this folder can be accessed through http://{SiteUrl}/_vti_bin/{ServiceName}.svc.

Right-click on the project node in the solution explorer and select Add>SharePoint Mapped Folder… menu option:

03_AddMappedFolderMenu

On the next dialog select the ISAPI folder and click on OK:

04_ISAPIFolder

Once you did that, right-click on the ISAPI folder in the Solution Explorer and add a subfolder to store files for your custom WCF service. This is technically not necessary, but as a best practice I suggest to always separate all your custom files being deployed in the SharePoint hive in subfolders named after your solution and/or company. In this example I added the “MyService” subfolder:

05_AddMyServiceFolder

Keep in mind that the name of the subfolder will also impact the URL of your custom service after it is deployed to SharePoint. In this case, my service(s) deployed in the ISAPI\MyService folder will be accessible under http://{SIteUrl}/_vti_bin/MyService/{ServiceName}.svc URL.

Next, we’ll add the custom service svc file to the ISAPI\MyService folder. Since we don’t have the project item template to add a WCF service directly, we’ll add a simple text file and change its extension to svc. Right-click on the MyService subfolder, select Add>New Item… menu:

06_AddNewItemMenu

On the next dialog, select the Text File template under the General template group and name the file MyService.svc (or anything you want but make sure it has the .svc extension):

07_AddMyServiceSvc

Next, add the Interface for our new service. Right-click again on the MyService folder in the solution exporer, select Add>New Item and select the Interface template under the Code template group:

08_AddIMyService

Name the new interface I{ServiceName}.cs, in my case it is IMyService.cs. Later we will add code to this interface that will describe the Operation Contract of our new service, but let’s first add the last file we need, the {ServiceName}.svc.cs – in my case MyService.svc.cs - which will contain the implementation of the service interface we just added.

Right-click on the MyService folder in the solution explorer, select Add>New Item… menu option and add a new class making sure to name it like {ServiceName}.svc.cs:

08_AddMyServiceSvcCs

Time for a quick check. If you did everything right, your project structure in the solution explorer should look something like this now:

08_ProjectStructure

So far we added:

  • MyService.svc – the service file itself that will be deployed to SharePoint hive and served by IIS
  • IMyService.cs – the interface file that will contain the service operation contract
  • MyService.svc.cs – the code file that will contain the service interface implementation

Let’s now fill the files with content.

First open the IMyService.cs file and define your Operation Contract. For the purpose of this example I will use the following simple contract:

using System.ServiceModel;
 
namespace VS2012WcfService
{
    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        string HelloWorld();
    }
}

As you can see, we only have one service operation called HelloWorld returning a string. Let’s implement this interface. Open the MyService.svc.cs file and paste the following code:





using Microsoft.SharePoint.Client.Services;
using System.ServiceModel.Activation;
 
namespace VS2012WcfService
{
    [BasicHttpBindingServiceMetadataExchangeEndpoint]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class MyService : IMyService
    {
        public string HelloWorld()
        {
            return "Hello World from WCF and SharePoint 2013";
        }
    }
}


The next step ist to connect everything together in the MyService.svc file. Open MyService.svc and paste the following XML into it:




<%@ ServiceHost Language="C#" Debug="true"
    Service="VS2012WcfService.MyService, $SharePoint.Project.AssemblyFullName$"  
    CodeBehind="MyService.svc.cs"
    Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory, 
    Microsoft.SharePoint.Client.ServerRuntime, 
    Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

Make sure that the namespace and the class name of your service (in my case VS2012WcfService.MyService) and the CodeBehind file name (in my case MyService.svc.cs) entered above match those in your project.


Pay attention to the following line in the above XML:


Service="VS2012WcfService.MyService, $SharePoint.Project.AssemblyFullName$"


See the $SharePoint.Project.AssemblyFullName$ token? Visual Studio should replace this token the actual assembly name and version during the packaging of the solution. However, per default Visual Studio does that only for some file types. If you check the Microsoft.VisualStudio.SharePoint.targets MSBuild target file under C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\SharePointTools (which is used by Visual Studio to build SharePoint projects) you will find the following line:


<TokenReplacementFileExtensions>$(TokenReplacementFileExtensions);xml;aspx;ascx;webpart;dwp;bdcm</TokenReplacementFileExtensions>


This line basically tells Visual Studio to replace tokens such as $SharePoint.Project.AssemblyFullName$ in .XML, .ASPX, .ASCX, .WEBPART, .DWP and .BDCM files. In addition, there is an extension point $(TokenReplacementFileExtension) that makes it possible to register additional file types either by passing them to MSBuild command line or by modifying the Project (.proj) file.


So this is what we need to do in order to add the .SVC files to the list of file types whose tokens will be replaced by Visual Studio during pacaking. Save your project and then right-click on the project name in the Solution Explorer and select Unload Project option:


image


After the project was unloaded, right click again on the project name and select the “Edit VS2010EcfService.csproj” option:


image


This will open up the .csproj XML file in the editor. In the first <PropertyGroup> section on the top of the file, right below the line where it says


<SandboxedSolution>False</SandboxedSolution>


add the following line, save the file and close the editor:


<TokenReplacementFileExtensions>svc</TokenReplacementFileExtensions>


This will effectively tell Visual Studio to include .svc files to the list of files for token replacement.


The last step is to reload the project, compile and deploy it.


Right-click on the project name in the solution explorer and select the Reload Project option:


image


That’s it! Compile and deploy your project. If you did everything correctly, you should be able to access your custom WCF service under http://{SiteUrl}/_vti_bin/MyService/MyService.svc (replace the {SiteUrl} with the URL of the site you selected for the debugging when you started this tutorial):


image


Checking in the 15 hive, you should see your MyService.svc file deployed in 15\ISAPI\MyService folder:


image




If you open the MyService.svc file with the notepad, you will see that the $SharePoint.Project.AssemblyFullName$ token was replaced with the correct assembly name and version:


image


The dll itself was, of course, deployed to GAC. Because this is a .NET 4.5 dll compiled for Any CPU, it is not stored in the “old” .NET 2.0/3.5 GAC under C:\Windows\assembly, but under the new .NET 4.x GAC found at C:\Windows\Microsoft.NET\assembly\GAC_MSIL:


image

59 comments:

  1. Hey Robert, this is what I've been looking for for the last week.
    I already have a WCF service that I created in VS2012 with the CKS template. But I needed to migrate the SP solution to SP2013/VS2012. I did it with no problem, but the project won't compile because it can't find the [BasicHttpBindingServiceMetadataExchangeEndpoint]

    I guess it's inside Microsoft.SharePoint.Client.ServerRuntime but I can't find that assembly in the "add assembly reference" dialog.
    http://i.imgur.com/BR8wCIN.png
    It seems a lot of assemblies are missing. Any ideas?

    Thank you again for this great article.

    ReplyDelete
  2. Hi emzero, thanks for the comment.

    You should be able to find the Microsoft.SharePoint.Client.ServerRuntime.dll if you click on the "Extensions" tab on the lefthand side of the Add References dialog, the "Framework" tab only shows the .NET framework dlls.

    In any case, this dll is physically located under C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\ so you can reference it from there as well.

    Regards, Robert

    ReplyDelete
  3. Great blog, thanks for saving my time! I followed and double check everything but I am getting this error in the browser:
    "The type 'IntranetWcfService.IntranetService, IntranetWcfService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1f18b9d9e773b71f', provided as the Service attribute value in the ServiceHost directive, or provided in the configuration element system.serviceModel/serviceHostingEnvironment/serviceActivations could not be found. "

    This blog: http://www.laschinger.net/?p=160 suggests a solution. Your very clear article made no mention of web.config which makes me wonder why not? Is this a difference between Sp2010 & SP2013 perhaps?

    ReplyDelete
  4. Hi Russell, thanks for the comment.

    The WCF services hosted in SharePoint are normally configured dynamically using one of the three factory classes in the Microsoft.SharePoint.Client.Services Namespace (in the Microsoft.SharePoint.Client.ServerRuntime.dll) and do not require the web.config file (see http://msdn.microsoft.com/en-us/library/ff521586.aspx for more info). In this case I used the Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory class as you can see in the MyService.svc file, that's why I don't need any extra web.config for it to work. This factory class automatically configures a SOAP endpoint for my web service without me having to do anything in web.config.

    You might need to create your own web.config file in case you need additional or different configuration options than those provided by one of the three built-in factories. In this case you should always place your .svc and the web.config file in a subfolder under the 15\ISAPI\ (e.g. 15\ISAPI\{YourFolder}), because there is a built-in SharePoint web.config file directly under 15\ISAPI which is required for the built-in services to function properly and should not be changed or deleted.

    If you use both a factory class and the web.config file, than the configuration will equal to the sum of both, with the code configuration in the Factory class overriding the declarative settings in the web.config file (if there are any overlaps).

    However, I suggest to keep the services simple and just use one of the factory classes that come with SharePoint.

    Regards, Robert

    ReplyDelete
  5. Thanks for your reply, got it working.

    ReplyDelete
  6. Thanks you Robert. I have been looking for this article from past one week. It's really save my time nice article.

    ReplyDelete
  7. Hi Robert, it's me again, still dealing with this.

    I've done everything as you explained, except that this was not a new project, but a SP2010 one migrated to SP2013.

    When I access the service url I get

    ---
    Sorry, something went wrong
    The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
    ---

    No further detail. Any ideas?

    Thanks

    ReplyDelete
  8. Hi emzero,

    difficult to say without the complete stack trace, but it looks like the .NET runtime has problems loading some of the assemblies that your project is using. First try the following:

    1) make sure your project is set to use .NET 4.5, not .NET 3.5
    2) make sure your target CPU is set to Any CPU or x64, not x86
    3) Are you referencing any external 3rd party dlls? How are they referenced?


    If none of those helped, please have a look in the ULS log and the Windows event log, in one of those you should be able to find the complete stack trace of this exception, with a bit of luck we might see the name of the assembly that causes the problem there. In addition you could try to use the Assembly Binding Log Viewer (Fuslogvw.exe, see http://msdn.microsoft.com/en-us/library/e74a18c4(v=vs.110).aspx) to log and troubleshoot the assembly loading for your project.

    You can also send me the project or upload it somewhere and I'll give it a look.

    Regards, Robert

    ReplyDelete
  9. Thank you so much for your help Robert, really.

    1) All projects inside the solution are set to use .NET 4.5
    2) All projects are set to x64
    3) In one project I reference a 3rd party dll. I've dropped the dll in a folder inside the project and add the reference via the add reference dialog. I don't think that's the problem. As I mentioned it works well in SP2010.

    Have looked at the Event Log and that's what it says about the error:

    WebHost failed to process a request.
    Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/46351451
    Exception: System.ServiceModel.ServiceActivationException: The service '/_vti_bin/MyService/MyService.svc' cannot be activated due to an exception during compilation. The exception message is: The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047). ---> System.IO.FileLoadException: The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
    at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMarkHandle stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName, ObjectHandleOnStack type)
    at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName)
    at System.Type.GetType(String typeName, Boolean throwOnError)
    at System.ServiceModel.Activation.ServiceParser.GetType(String typeName)
    at System.ServiceModel.Activation.ServiceParser.GetCompiledType(Assembly compiledAssembly)
    at System.ServiceModel.Activation.ServiceParser.CreateParseString(Assembly compiledAssembly)
    at System.Web.Compilation.BuildProvider.CreateBuildResult(CompilerResults results)
    at System.Web.Compilation.BuildProvider.GetBuildResult(CompilerResults results)
    at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath virtualPath)
    at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
    at System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
    at System.Web.Compilation.BuildManager.GetVPathBuildResult(HttpContext context, VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean ensureIsUpToDate)
    at System.ServiceModel.ServiceHostingEnvironment.HostingManager.GetCompiledCustomString(String normalizedVirtualPath)
    at System.ServiceModel.ServiceHostingEnvironment.HostingManager.CreateService(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
    at System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(ServiceActivationInfo serviceActivationInfo, EventTraceActivity eventTraceActivity)
    at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
    --- End of inner exception stack trace ---
    at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
    at System.ServiceModel.ServiceHostingEnvironment.EnsureServiceAvailableFast(String relativeVirtualPath, EventTraceActivity eventTraceActivity)
    Process Name: w3wp
    Process ID: 8040

    I have no idea what could be causing the error. Maybe this SP Solution should be upgraded to a SP App. No clue.

    Thanks again.

    Regards

    ReplyDelete
    Replies
    1. Ok, we're getting closer, the .NET runtime could not resolve something referenced in the MyService.svc. Could you please post the contents of your MyService.svc file:

      1) the version from the Visual Studio project
      2) the version from the C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI

      Btw, how did you deploy your wsp package and where were your dlls and the 3rd party dll deployed?

      Your dll should have been deployed to either C:\Windows\Microsoft.NET\assembly\GAC_MSIL or C:\Windows\Microsoft.NET\assembly\GAC_64, depending on the build settings (AnyCpu or x64). Note: this is the *new* .NET 4.x GAC, not the old .NET 2.x and .NET 3.x GAC found under C:\Windows\assembly!

      Where did the 3rd party dll go after the deployment?

      Delete
    2. Content of MyService.svc (it's not really named that way).
      http://pastebin.com/JmWwvv2H

      1) The version of what? Of the service assembly? it's 1.0.0.0.
      2) Here's the .svc that gets deployed to the 15/ISAPI folder
      http://pastebin.com/dd5WNGQT

      So the $SharePoint.Project.AssemblyFullName$ is being replaced correctly.

      The wsp package is deployed through VS, nothing strange there.

      All solution assembly dlls are deployed to 'C:\Windows\Microsoft.NET\assembly\GAC_64'.

      I was wrong about 3rd party dll, it's not referencing anything.
      But I did have to add some Sharepoint dlls via the browse dialog because they didn't show up in the Add Reference list.

      Delete
    3. Hi emzero,

      it looks like the public key token of the Factory dll in your .svc file is missing a "c" at the end, it should be "71e9bce111e9429c" instead of "71e9bce111e9429". If this piece of information is not correct, the .NET runtime will not be able to find the dll at runtime and will throw the binding error like the one you got.

      Please try it out and let me know if it helped.

      Regards, Robert

      Delete
    4. Yeah, it was exactly that. I figured it out yesterday.

      Thank you for all your help.

      Regards

      Delete
  10. Hey Robert, thanks for the great article.

    Unfortunately its nor working for me. When I browse to svc file on my browser it throws below exception:

    The type 'ContractorWorkflowServiceProject.ISAPI.ContractorService, ContractorWorkflowServiceProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d802edf0b4cd8476', provided as the Service attribute value in the ServiceHost directive, or provided in the configuration element system.serviceModel/serviceHostingEnvironment/serviceActivations could not be found.

    Any help will be appreciated.

    Thanks

    ReplyDelete
    Replies
    1. Hi Yousuf,

      two questions:

      1) do you have a web.config file in your project that is deployed together with the .svc file?
      2) the ContractorWorkflowServiceProject.ISAPI.ContractorService, ContractorWorkflowServiceProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d802edf0b4cd8476 assembly should have been deployed to the .NET 4.5 GAC (C:\Windows\Microsoft.NET\assembly\) - can you check if it is there (either in the \GAC_MSIL or the \GAC_64 subfolder)?

      Thanks, Robert

      Delete
    2. Hi Robert

      1) No, there is no web.config file deployed with .svc file.

      2) I can see ContractorWorkflowServiceProject.dll file at this location "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\ContractorWorkflowServiceProject\v4.0_1.0.0.0__d802edf0b4cd8476"

      Thanks

      Delete
    3. Hi Yousuf,

      in that case there is something wrong with the ContractorWorkflowServiceProject.ISAPI.ContractorService class - is this class present in your project? Is it public (and the service interface too)? Is the class name correct (upper-lower case)? Can you post the contents of the ULS log (and maybe the windows event log) at the time the error occured?

      Delete
    4. Hi Robert,

      The veent log goes below:

      WebHost failed to process a request.
      Sender Information: System.ServiceModel.Activation.HostedHttpRequestAsyncResult/32914628
      Exception: System.ServiceModel.ServiceActivationException: The service '/_vti_bin/ContractorService/ContractorService.svc' cannot be activated due to an exception during compilation. The exception message is: The type 'ContractorWorkflowServiceProject.ContractorService, ContractorWorkflowServiceProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d802edf0b4cd8476', provided as the Service attribute value in the ServiceHost directive, or provided in the configuration element system.serviceModel/serviceHostingEnvironment/serviceActivations could not be found.. ---> System.InvalidOperationException: The type 'ContractorWorkflowServiceProject.ContractorService, ContractorWorkflowServiceProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d802edf0b4cd8476', provided as the Service attribute value in the ServiceHost directive, or provided in the configuration element system.serviceModel/serviceHostingEnvironment/serviceActivations could not be found.
      at System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String constructorString, Uri[] baseAddresses)
      at System.ServiceModel.ServiceHostingEnvironment.HostingManager.CreateService(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
      at System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(ServiceActivationInfo serviceActivationInfo, EventTraceActivity eventTraceActivity)
      at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
      --- End of inner exception stack trace ---
      at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
      at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result)
      Process Name: w3wp
      Process ID: 7440

      Delete
    5. Also I have double-checked the name, upperlowercases and access modifiers.

      Delete
    6. Hi Yousuf,

      the exception message is pretty clear, the Type ContractorWorkflowServiceProject.ContractorService, ContractorWorkflowServiceProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d802edf0b4cd8476 cannot be loaded. That typically happens when one or more of the following is true:

      1) The ContractorWorkflowServiceProject.all assembly is not in the GAC or has a different signature (Public Key Token and/or the version number)

      2) The ContractorWorkflowServiceProject.all assembly was compiled with a wrong .NET version and thus landed in the wrong GAC (3.5 instead of 4.x)

      3) The ContractorWorkflowServiceProject.ContractorService class within the assembly cannot be - either it is not public, or it doesn't exist under exactly this namespace and name.

      Unfortunately the exception message does not say what exactly went wrong, so I can't say from here. Is it possible for you to upload your project somewhere so that I can test it? Can you download my project from https://dl.dropboxusercontent.com/u/21382560/SP2013WCFService.zip and check if it works for you and what exactly is different to your project?

      Regards, Robert

      Delete
  11. This comment has been removed by the author.

    ReplyDelete
  12. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. Hi emzero,

      It might have to do with security, but in that case you would get a 401 unauthorized from the server, not 400. Is there anything in the ULS log and/or the Windows event log?

      Delete
  13. Hey Robert, it's me again, sorry to bother you but...

    Following your example I could make it work, everything as you've done it.

    Problem is I changed my project to be exactly like your but when I access the service url I get "400 - Bad request".
    I checked the 15/ISAPI folder and the .svc is there, opened it, everything look just like the "MyService" example.
    The DLLs are indeed being deployed to the new 4.x GAC.

    Which could be causing this?

    Thanks

    ReplyDelete
    Replies
    1. Please check the ULS log and the windows event log first if there are any other exceptions being logged, 400 could unfortunately mean almost anything and nothing at the same time...

      Delete
    2. Hi again,

      I uploaded my project to https://dl.dropboxusercontent.com/u/21382560/SP2013WCFService.zip - it contains the service and a test client. Adjust the URLs in the test client and the service project and deploy it on your machine, it should work (at least it does on my environment).

      Regards, Robert

      Delete
  14. thanks, Robert, your post is the best post I have found on line right regarding to this topic, and I just want to know whether you have some ideas how to consume the service from c# code, and how to pass the user name and password when the service is based on https protocol, thanks again

    ReplyDelete
  15. Hi, Robert, I did try your code, but when I apply my service with https, I got error saying The username is not provided, do you have any example regarding to the service hosted in a https site, thanks a lot.

    ReplyDelete
    Replies
    1. Hi Tao,

      thanks for the question. Just to clarify: how did you set up https on your service? Since the service is now part of SharePoint, you should set up https on the web application level using the Alternate Access Mappings, that will automatically propagate the https down to every element including your custom service.

      Can you paste code that you used to call the service?

      Delete
    2. Thanks a lot, Robert,
      I setup the service according to your instruction, since I would like to run a trial version first, then I can do the customization, the service call is like this below, thanks a lot for your detailed instruction:
      string serviceUrl = "https://shakespeare.vm13-ttao.envisionit.com/";

      BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
      binding.Security.Mode = BasicHttpSecurityMode.TransportWithMessageCredential;

      // This assumes that your SP web app is configured for Windows claims using NTLM.
      binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;

      EndpointAddress address = new EndpointAddress(serviceUrl);

      using (MyServiceTest1.MyServiceClient client = new MyServiceTest1.MyServiceClient(binding, address))
      {
      // Use this if you are invoking the service under different credentials
      client.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("ttao", "p@ssw0rd", "PCCLIMITED");
      client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

      try
      {
      Console.WriteLine(client.HelloWorld());
      }
      catch (Exception e)
      {
      string error = e.Message;
      }
      }

      Delete
    3. Hi Tao,

      sorry for the delay, I'll have a look at your code and get back to you here. I assume that the binding should be either wsHttpBinding or one of the *Federated* bindings but I'll have to give it a try.

      Delete
    4. thanks a lot Robert, Now I need to apply wcf to our workflow, seems this concept is really important, and you will be one of the most famous SP 2013 WCF experts.

      Delete
    5. Hi,Robert,

      I think this link is great reference for us to check, I will try it and let you know whether I can make it, but still you are more experienced than me, and I like to hear more ideas from you, thanks a lot.
      http://stackoverflow.com/questions/4946971/sharepoint-2010-custom-wcf-service-windows-and-fba-authentication

      Delete
  16. After deployment, we we see for .svc showing empty on browser. I checked on wcfclient.exe

    Error:
    Error: Cannot obtain Metadata from http://ukzn-sp-test:34136/hr/TRS/_vti_bin/Myservice/Myservice.svc?wsdl If this is a Windows (R) Communication Foundation service to which you have access, please check that you have enabled metadata publishing at the specified address. For help enabling metadata publishing, please refer to the MSDN documentation athttp://go.microsoft.com/fwlink/?LinkId=65455.WS-Metadata Exchange Error URI: http://ukzn-sp-test:34136/hr/TRS/_vti_bin/Myservice/Myservice.svc?wsdl Metadata contains a reference that cannot be resolved: 'http://ukzn-sp-test:34136/hr/TRS/_vti_bin/Myservice/Myservice.svc?wsdl'. The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'. The remote server returned an error: (401) Unauthorized.HTTP GET Error URI: http://ukzn-sp-test:34136/hr/TRS/_vti_bin/Myservice/Myservice.svc?wsdl There was an error downloading 'http://ukzn-sp-test:34136/hr/TRS/_vti_bin/Myservice/Myservice.svc?wsdl'. The request failed with HTTP status 404: Not Found.

    ReplyDelete
  17. I realize I'm a bit late to the party, but could you walk me through the steps necessary to move this from development to production? This is all very new to me and I don't want to cause issues on our production farm. Thanks.

    ReplyDelete
    Replies
    1. Hi Benjamin,

      I'm not sure what exactly are your concerns, but there should be nothing special about moving this from dev to production. When you build and publish the project you will get a standard wsp package that you can take and deploy in any other farm you want - there are no environment-specific settings in this project. Does that answer your question?

      Delete
    2. I see the wsp file within the bin folder of my solution and have found a walkthrough for deploying the file here: http://jaxsharepoint.blogspot.com/2013/06/deploying-wsp-to-sharepoint-2013.html. Am I correct in my assumption that the wsp file contains everything necessary to create all of the associated files on the production box when deployed?

      When following the steps above I had to modify the hosts file as noted, so I assume this is a modification I'll also need to make on the production server? Additionally, my web service makes a CAML query using SPQuery, so should I just modify the URL pointer for the query in my project, build, and deploy to production?

      Thank you for the help, I realize many of my questions are elementary. My background is in finance, not development.

      Delete
    3. Hi Benjamin,

      yes, the wsp package contains all you need to deploy the service to your production environment. Regarding the URL used for the SPQuery - it depends, if you hard coded it, then you will have to change it to the one corresponding to your target environment and rebuild the package. Normally, however, you should not hardcore such things but rather save the environment specific settings outside of your package so that you don't have to rebuild the package for every environment. You can save them to a list, property bag or even the web.config file, depending on your requirements. Check the SharePoint 2010 Guidance package at http://spg.codeplex.com, this package already contains some modules and guidance on how to manage the app settings for SharePoint. Finally, you don't need to change the hosts file on your target environment, this was only necessary on your dev environment to make Visual Studio accept your test site URL if it is not the same as the computer name (which is the case with host named site collections).

      Delete
    4. I was able to get the web service deployed successfully to our production environment. Thanks for all of the help and great walkthrough!

      Delete
    5. Robert,

      This may be a bit beyond the scope of your walkthrough, but I figured I would ask anyway as you seem quite knowledgeable in this area.

      I'm in the process of extending the web service I created previously using your walkthrough to pull data from an SSAS database. The problem I am having is related to (I believe) double-hop authentication. Our SP 2013 environment is configured to use claims auth, but my understanding is that SSAS uses classic auth exclusively. Therefore, it is necessary to utilize C2WTS. Am I correct in this assertion? Or is this something that can be resolved via the web service itself? Also, I believe I need to create a SPN record for the SSAS database itself and then configure constrained delegation on an account, but I am unclear which (C2WTS or AppPool?).

      I believe if I have a general understanding of the steps necessary to authenticate against the SSAS database I can find the implementation details elsewhere. Thanks again, Robert.

      Delete
    6. Hi Benjamin,

      the SSAS requires a Windows Identity, so you will need to create one. You basically have two choices here, either you use the App Pool account to access the SSAS, or you use the identity of the currently logged on user. In the first case you simply have to wrap the call to the SSAS using the SPSecurity.RunWithElevatedPrivileges() - and you will obviously have to configure the constrained delegation for the App Pool account. I am not sure about the C2WTS account, though. In the latter case (the user's identity), you will need to use the C2WTS as you already wrote yourself. For that you will need the user's UPN - check the excelent article from Sahil Malik on how to do that at http://blah.winsmarts.com/2013-11-Use_C2WTS_to_get_a_classic_windows_identity_from_a_claims_identity.aspx.

      Delete
    7. Thank you for your reply. I've been pulled away from this project for the time being, but when I get back to it I'll be sure to follow up.

      Delete
  18. Hi Robert, great article, congrats!!!

    ReplyDelete
  19. Hi Robert,

    Nice article. Thank You.

    But i am getting End Point not found exception after hosting the WCF and while running the .svc file from browser. Please help.

    Thank you.

    ReplyDelete
  20. great job Robert.
    The best WCF SP introduction I've found so far
    Thanks

    ReplyDelete
  21. Robert, Hi. Thank you very much for this post. Its helped me considerably.

    I've been trying to implement the AddPresident operation in javascript/ajax but with little success.

    The following gives me a Bad Request error code.
    function AddPresident() {

    var newPresident = {"EmailAddress":"Elvis@email.com","FirstName":"Elvis","Id":"99","LastName":"Elvis"};

    var serviceUri = _spPageContextInfo.webAbsoluteUrl + "/_vti_bin/BarkesServices/PresidentsService.svc/AddPresident";

    $.ajax({
    type: "POST",
    url: serviceUri,
    data: JSON.stringify(newPresident),
    contentType: "application/json; charset='utf-8'",
    dataType: "json",
    processData: true,
    success:
    function (response, status, jqXHR) {
    alert(response);
    },
    error:
    function (err) {
    alert(err.statusText);
    }
    });
    }

    I was wondering if you could help resolve this.
    Thanks,

    ReplyDelete
  22. This comment has been removed by the author.

    ReplyDelete
  23. Hi Robert,

    I consumed the WCF service from C# console application and it is working fine when I add the end point address and binding objects to service object dynamically through code but when I configured the same binding and end point address in app.config file in client side giving below error, any idea?

    "The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'."

    App.config file is auto generated when I added the WCF service reference in client side, I haven't changed any setting and called the method, not sure whether the service retrieving the end point and binding details from app.config file or not?.

    Please let me know if I can provide any additional details to understand this issue.

    Our requirement is, we shouldn't ask users, who consumes our WCF service to add end point and address in their client code as they may use different programming languages.

    ReplyDelete
  24. Do you know what apppool or what process the service runs in? I have multiple services that we deployed to our farm about a year ago, and are currently building additional enhancements, except that after deploying the updated .wsp's the new services seem to still run against the old version of the code, until after multiple IISRESETs on each server in the farm. The rest of the code in my .wsp is picked up just fine, but it is always a struggle to get the deployed web services to reference latest dll. Is there some server-side cache of web serve DLL or some specific service or process that needs to be reset?

    ReplyDelete
    Replies
    1. Hi Eric,
      The WCF services are running in the SharePoint IIS (w3wp.exe) process, but you could have more of those, one for each web application in SharePoint and even more if the web applications were extended. It looks like the deployment of your new WSPs didn't work as expected and left some old DLLs laying around. Did you check the GAC to see which version is there? Did you do the IISRESET on all your front ends? I had a similar problem once and it was caused by the timer service which was holding a lock on one of the assemblies and prevented the new DLL version to be written to the file system during the WSP deployment. The solution was to restart the timer service before the deployment in order to release the locks. Does your service have its own DLL, or is it compiled together with other stuff in one assembly? Separating the service into its own DLL might reduce the risk of the DLL being locked by some non-IIS process such timer service or the sandboxed user code host process.

      Delete
    2. One more thing: to troubleshoot this you can use the Sysinternals Process Explorer to see what assemblies are loaded in the w3wp.exe process, maybe you have an old version of the service assembly somewhere where it gets picked up by the .NET assembly loader. Alternatively, you can also use the Fusion Log Viewer (Fuslogvw.exe) to log the assembly binds and see where the DLL is coming from. Or you could create a memory dump of your w3wp.exe process and analyze it using the Visual Studio 2012+ (works only for the .NET 4.x dumps), the DebugDiag or - if you are one of the brave ones - the WinDbg. In the dump you will also be able see the loaded assemblies, so it should give you the idea where they are coming from.

      Delete
  25. Hi Robert. I am praying you can help me. Background: we have a host named site collection on a http web application. A 3rd party vendor developed and deployed wcf web services using c# code. Everything worked fine. We now need to run everything over https so ive had to extend the web application and rename the host named site collection to https. But now there is a error with running https://host-named-site-coll/_vti_bin/customservices/service.svc. so i had to unextend the web application back to http and revert host named site coll back to http. My UI is working as expected, but now I get a 400 error when i go to the service.svc url again. Any ideas?
    Cheers
    Danielle

    ReplyDelete
    Replies
    1. Hi Danielle, I'm sorry, I've seen your post only now. What error do you get - please create a network trace using Fiddler or developer tools in the browser - what does the response look like? Can you also check the ULS log and the IIS log?

      Delete
  26. Hi Robert, Nice post. I have tried your code on SharePoint server 2013 with visual studio 2015. I am not getting any error, its getting deployed properly at : C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\FWCService, also. But when I try to search through site "http://{SiteUrl}/_vti_bin/FWCService/FWCService.svc", getting error as "This page can't be displayed". I am able to other default services at : http://{SiteUrl}/_vti_bin/lists.asmx. Please advise. Thank you in advance.

    ReplyDelete
  27. This comment has been removed by the author.

    ReplyDelete



  28. JayuFebruary 7, 2017 at 3:02 AM

    Hi Robert, nice article. I have followed all those things as mentioned here. Projects deployed successfully, also svc files changes appeared too after deployment. but created service is not appearing at specified address : http://{SiteUrl}/_vti_bin/MyService/MyService.svc, though its there : C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\MyService.
    Still facing issue as "The page can't be displayed" at : http://{SiteUrl}/_vti_bin/MyService/MyService.svc

    ReplyDelete
    Replies
    1. Hi Jayu, did you check if the service dll was correctly deployed to the GAC as well (keep in mind that the GAC location has changed in .NET 4.x)? Also, could you check the response headers when you try to invoke the service in the browser - what HTTP code do you get and do you see the SharePoint HTTP headers in there, like SPRequestId? This is the correlation ID, so please check in the ULS log what errors are logged for this correlation ID. Btw, which SharePoint version do you use and which .NET version did you configure for your service project?

      Delete
    2. Hello, thanks for your reply. Following comments on your points:
      1. DLL is correctly deployed at : C:\Windows\Microsoft.NET\assembly\GAC_MSIL
      2. SharePoint : 2013, Target Framework: .NET Framework 4.5
      3. I am not getting any correlation id on page as error..just " This Page can't be displayed", nothing is appearing in site url and neither on page to trace the error.

      Delete
  29. Hello,

    I'm trying to use a customized WCF service that I deployed on Sharepoint under ISAPI folder, for that I followed the steps in your article for sharepoint 2013 because I didn't find any documentation for 2016.

    The error that I met each Time I browse to my service from IIS is that :

    Sorry, something went wrong
    The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)

    The only modification I have done, is the update of dll version in my SVC file :

    <%@ ServiceHost Language="C#" Debug="true"
    Service="Project.MsiWcf, $SharePoint.Project.AssemblyFullName$"
    CodeBehind="MsiWcf.svc.cs" Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory
    Microsoft.SharePoint.Client.ServerRuntime, Version=16.0.0.0, Culture=neutral,
    PublicKeyToken=71e9bce111e9429c" %>

    Any one have an idea how to fix it ??

    ReplyDelete