WCF service reference namespace differs from original

Thorarin picture Thorarin · Jul 29, 2009 · Viewed 37k times · Source

I'm having a problem regarding namespaces used by my service references. I have a number of WCF services, say with the namespace MyCompany.Services.MyProduct (the actual namespaces are longer).
As part of the product, I'm also providing a sample C# .NET website. This web application uses the namespace MyCompany.MyProduct.

During initial development, the service was added as a project reference to the website and uses directly. I used a factory pattern that returns an object instance that implements MyCompany.Services.MyProduct.IMyService. So far, so good.

Now I want to change this to use an actual service reference. After adding the reference and typing MyCompany.Services.MyProduct in the namespace textbox, it generates classes in the namespace MyCompany.MyProduct.MyCompany.Services.MyProduct. BAD! I don't want to have to change using directives in several places just because I'm using a proxy class. So I tried prepending the namespace with global::, but that is not accepted.

Note that I hadn't even deleted the original assembly references yet, and "reuse types" is enabled, but no reusing was done, apparently. However, I don't want to keep the assembly references around in my sample website for it to work anyway.

The only solution I've come up with so far is setting the default namespace for my web application to MyCompany (because it cannot be empty), and adding the service reference as Services.MyProduct. Suppose that a customer wants to use my sample website as a starting point, and they change the default namespace to OtherCompany.Whatever, this will obviously break my workaround.

Is there a good solution to this problem?

To summarize: I want to generate a service reference proxy in the original namespace, without referencing the assembly.

Note: I have seen this question, but there was no solution provided that is acceptable for my use case.


Edit: As John Saunders suggested, I've submitted some feedback to Microsoft about this:
Feedback item @ Microsoft Connect

Answer

Thorarin picture Thorarin · Jul 29, 2009

I've added a write-up of this solution to my blog. The same information really, but perhaps a little less fragmented

I've found an alternative to using svcutil.exe to accomplish what I want. It (imo) makes updating the service reference easier than rerunning the utility.

You should explicitly specify a namespace uri on your ServiceContract and DataContracts (see further below for comment).

[ServiceContract(Namespace = "http://company.com/MyCompany.Services.MyProduct")]
public interface IService
{
    [OperationContract]
    CompositeType GetData();
}

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

The namespace could be anything, but technically it needs to be a valid uri, so I chose this scheme. You might have to build manually for things to work later, so do that.

Once this is done, enable the Show All Files option in the Solution Explorer. Expand the service reference you added previously. Double click the Reference.svcmap file.

There will be a <NamespaceMappings /> element, which you will need to edit. Continuing my example:

<NamespaceMappings>
    <NamespaceMapping
        TargetNamespace="http://company.com/MyCompany.Services.MyProduct"
        ClrNamespace="MyCompany.Services.MyProduct" />
</NamespaceMappings>

Save the file, right click the service reference and select Update Service Reference.

You can add as many mappings as you need (I actually needed two). The effect is the same as the svcutil /namespace: approach, but without having to use the command line util itself, making for easier updating.

Difference with svcutil

The downside to this approach is that you need to use explicit namespace mappings. Using svcutil, you have the option to map everything not explicitly mapped like this (the solution John Saunders was referring to):

svcutil /namespace:*,MyCompany.Services.MyProduct ...

You might think to use:

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

but this will not work, because Visual Studio already implicitly adds this mapping, pointing to the generated namespace name we're trying to get rid of. The above configuration will cause Visual Studio to complain about a duplicate key.

Ad explicit namespaces:
When no explit namespace is specified in your code, it seems that .NET will generate a uri of the form http://schemas.datacontract.org/2004/07/MyCompany.Services.MyProduct. You could map that just as well as the explicit namespaces in my example, but I don't know if there is any guarantee for this behavior. Therefore, going with an explicit namespace might be better.

NB: mapping two TargetNamespaces to the same ClrNamespace seems to break code generation