views:

2789

answers:

2

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

A: 

Your use case was wrong.

You should never have included the service as a reference in the first place.

I believe svcutil.exe will accept a switch specifying the full namespace to use.

John Saunders
It's a valid use case, but apparently not supported by the GUI. I've been looking at `svcutil.exe` switches myself since I posted this and it looks like that will work. I'd still like to avoid having to do it like this if I can though. I also ran into a second problem that I used a silly workaround for... I'll update here when I've tried some options.
Thorarin
I disagree completely. A service is a service. The fact that it builds to an assembly is no excuse to reference the assembly. Even for the purpose of unit testing, that assembly should not be referenced at all. You should act as though the service were written in Java or some other non .NET language, in which case you would never have considered referencing the assembly.
John Saunders
Oh, you were referring to that part... but my namespace problem occurs when I reference it as a service. For example, the generated namespace violates the IDesign based coding guidelines we use.For Unit Testing we use a `ChannelFactory`, which *does* actually need an assembly reference, but allows you to easily host it as a service for the test.
Thorarin
What's wrong with referencing a 100% ServiceContract and DataContract assembly (no implementation, of course). What's the difference, especially if you own both sides of the connection. I wouldn't say this is categorically bad, especially when you are refactoring a lot at the beginning of a project it can be a boon for productivity. In these cases I don't create a service reference at all.
Anderson Imes
If it's only the contracts, I don't have a problem with it, especially if they're _only_ contracts, with no behavior. The issue I have is that he seems to be referencing the entire service, including the behavior.
John Saunders
@Thorarin: do those coding guidelines apply to generated code? And I've got no problem with referencing _contracts_ that are in a separate assembly, and no problem with using ChannelFactory in a client. It would be nice if "Add Service Reference" permitted an arbitrary namespace. It seems it will only allow one relative to the root namespace of the project. I was able to add "Random.Namespace", but it became "RootNamespace.Random.Namespace". This would be a good suggestion for http://connect.microsoft.com/visualstudio/.
John Saunders
Well, I'm hoping Pavel will read this and gets cracking ;)
Thorarin
"Pavel" who? I don't know who that is.
John Saunders
Him: http://stackoverflow.com/users/111335/pavel-minaev :) He posted somewhere that he is working on VS2010 (and it says so in his profile)
Thorarin
+6  A: 

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

Thorarin
Jersey Dude
After encountering this error I found this was the answer before reaching this post on SO but I have to say this is absolutely ridiculous to have to do this. WCF should do this automatically, there is not one valid justification for requiring every single user of the service to manually have to edit their client files just to talk to the service. As much as I love .NET stuff like this makes me so mad at Microsoft.
Chris Marisic
@Chris: I wouldn't consider it an error. You can talk to the service just fine without any manual editing. The editing is just to get things to match some personal preferences. It is all rather inconvenient though.
Thorarin
What do you mean its not an error? Without doing this the project isn't even capable of updating its service reference.
Chris Marisic