Customizing Visual Studio generated service reference namespaces

When using WCF to consume a web service from your .NET application, you have a couple of different options:

  • Using a contract-only assembly reference, generating a proxy at runtime
  • Generate a proxy by running svcutil.exe
  • Adding a service reference to your project from Visual Studio

This post is focused on the last of these three options. When adding a service reference, Visual Studio presents you with a dialog box which allows you some level of control over the proxy being generated. One of these options allows you to choose the namespace for the generated classes.

Unfortunately, there is a small but annoying limitation on your ability to choose any namespace you like; Visual Studio will always prefix whatever namespace you enter with the current project's default namespace.

For example, we're writing an web application called MyApplication. The default namespace is MyCompany.MyApplication. The application depends on a service MyService, for which I'm adding a service reference with that same name. The generated proxy class will then be in the namespace MyCompany.MyApplication.MyService. Usually, this is not a big problem, but sometimes this is not what you want, so I went to look for some way to get rid of the bit that Visual Studio adds for me.

My first attempt was to provide Visual Studio with a rooted namespace name: global::MyCompany.MyService. Unfortunately, this is not allowed. When generating a proxy class with svcutil.exe, you have full control over the namespace being used. Visual Studio uses that same mechanism for its code generation, so maybe there was some way to influence which parameters it uses.

Solution

It turns out that Visual Studio does actually allow for some customization beyond what is offered in the dialog box it shows you. After you've added the service reference, enable the Show All Files option in your solution explorer. The service reference will become an expandable node, containing (amongst other things) a Reference.svcmap file. This file contains the settings for your service reference, from which all the other files are generated. We'll be hand editing this XML based file.

Consider this contract definition:

[ServiceContract(Namespace = "http://mycompany.com/MyCompany.MyService")]
public interface IMyService
{
    [OperationContract]
    CompositeType GetData();
}

[DataContract(Namespace = "http://mycompany.com/MyCompany.MyService")]
public class CompositeType
{
    // Whatever
}

Suppose I wanted to generate my proxy into the namespace MyCompany.MyService, I would open up the Reference.svcmap file and find the <NamespaceMappings /> element, replacing it with something like this:

<NamespaceMappings>
    <NamespaceMapping
        TargetNamespace="http://mycompany.com/MyCompany.MyService"
        ClrNamespace="MyCompany.MyService" />
</NamespaceMappings>

After you save the file and update the service reference, the generated code will be inside the desired namespace.

Wait a minute however, what's with these explicit namespaces in my contracts? Well, your contract definitions should really be already containing those, but I imagine that most people don't actually bother with them. If you don't supply any namespace in your contract, .NET will auto generate one in the form http://schemas.datacontract.org/2004/07/MyCompany.MyService. You could map that namespace URI instead of the one I used in the example above, and it'll work. However, I don't really like relying on this behavior, which I why I explicitly provided namespace URIs.

Difference with svcutil

The downside to the above solution, is that you need to use explicit namespace mappings. If you use more than one namespace, you will need to map all of them manually, which can be a bit of a pain. With svcutil, you actually have the option of mapping everything that is not explictly mapped into a default namespace, like this:

svcutil /namespace:*,MyCompany.MyService ...

Since Visual Studio actually uses svcutil to do its job, you might try to be clever and use:

<NamespaceMappings>
    <NamespaceMapping
        TargetNamespace="*"
        ClrNamespace="MyCompany.MyService" />
</NamespaceMappings>

Unfortunately, this will not work. Visual Studio adds this mapping implicitly, to that darned MyCompany.MyApplication.MyService namespace we were trying to get rid of.

Another caveat: mapping two TargetNamespaces to a single ClrNamespace seems to break code generation.

Dude, this solution totally sucks...

I'll readily admit that it is far from ideal, but it saves you from creating batch files for svcutil code generation. To ease the pain of maintaining the Reference.svcmap file a little, it is possible to combine multiple service references into one. That way, you only have to maintain the NamespaceMappings list in one place if you're using multiple services. The key is the MetadataSources element:

<MetadataSources>
    <MetadataSource Address="http://localhost/MyService/mex" Protocol="mex" SourceId="1" />
    <MetadataSource Address="http://localhost/MyOtherService/mex" Protocol="mex" SourceId="2" />
</MetadataSources>

After updating the service reference, a proxy for services will be generated. Also, if you have data contracts that are shared between these two services, they will actually only be generated once.

Comments (4) -

  • Hi Marcel

    Good post, thanks.

    Any thoughts on how to extend a service reference using a Partial class, when the reference is part of a website (not a web application project)? In this case, SvcUtil generates the proxy down deep in the ASP.NET temp structure. It's hard to get at from there.

    Thanks,
    Jeff
    • Jeff,

      What version of Visual Studio are you using? I've tried creating a website project in Visual Studio 2010, but I don't see any deep nested structure on the service reference.

      I would suggest that you create an assembly library project, where you add the service reference, as well as the extra code.

      If the web service is your own, it may be easier to create a separate interface assembly, which contains the service and data contracts. That way, you can do without a service reference altogether.
  • It may be an old post, but I just had need to do this. However, editing the svcmap file and updating the service reference just doesn't work. The generated reference.cs file still has the original namespace with the project prefix. No matter what I tried, it would not generate the way I needed. I had to resort to using the svcutil and importing the resulting .cs file into my project.
    • You need to update the reference after updating the file. Are you sure you didn't forget that part?

Pingbacks and trackbacks (1)+

Add comment

Loading