views:

1574

answers:

3

I'm attempting to split up my WCF web services into a few services instead of 1 giant service. But the Visual Studio (Silverlight client) duplicates the common classes shared by both services. Here is a simple example to illustrate my problem.

In this example there are two services. Both return the type "Person". By default VS will create two seperate Person proxy's under unique NameSpaces. This means that the "Person" returned by the different services cannot be consumed by the client as the same thing. How do I fix this? Is it possible without writing the proxy classes myself?

Common

[DataContract]
public class Person
{
    [DataMember]
    string FirstName { get; set; }
    [DataMember]
    string LastName { get; set; }
    [DataMember]
    string PrivateData { get; set; }
}

StaffService.svc

[ServiceContract(Namespace = "")]
public class StaffService
{
     [OperationContract]
     public Person GetPerson ()
     {
         return new Person {"John", "Doe", "secret"};
     };
}

PublicService.svc

[ServiceContract(Namespace = "")]
public class PublicService
{
     [OperationContract]
     public Person GetPerson ()
     {
         return new Person {"John", "Doe", "*****"};
     };
}

Thanks for you help! Justin

+1  A: 

If you generate the proxies at the same time using svcutil.exe it will only generate one type. I don't know how to do this with adding a service reference to the project.

We run it in a batch file so I have clipped that down and changed the names to protect the innocent. It is really about mapping the service namespaces together and then including all the URLs together. It also has the collection type set (for lists) and includes an assembly reference (which some of the other answers reference.

@ECHO OFF

SET cmd=C:\"Program Files"\"Microsoft SDKs"\Windows\v6.0a\bin\SvcUtil.exe
SET cmd=%cmd% /out:Traffic.cs /noConfig /collectionType:System.Collections.Generic.List`1

SET cmd=%cmd% /reference:..\..\..\lib\Architecture.Frameworks.dll

REM ######### Service namespace mappings (Service Contracts and Message Contracts)
SET cmd=%cmd% /namespace:"http://services.test.com/app/2005/09/"
SET cmd=%cmd%,"app.ServiceProxies"

REM ######### Schema namespace mappings (Data Contracts)
SET cmd=%cmd% /namespace:"http://schemas.company.com/app/2005/09/"
SET cmd=%cmd%,"Co.ServiceProxies.app.DataContracts"

REM ######### Set all the URLs that have common types
SET cmd=%cmd% http://localhost/Services/MyService1.svc
SET cmd=%cmd% http://localhost/Services/MyService2.svc

%cmd%

PAUSE

If all the items are in the same service namespace, you could possibly get away with just having all the URLs and not worry about the namespaces, but I have not tried it that way.

Brian ONeil
Thanks Brian. Do you have an example or link to explain how to do that?I'll start looking it up by myself in the meantime.
Justin
Brian thank you for your answer. I ended up using the other solution but I could see this one being very useful in the near future.
Justin
A: 

svcutil.exe can reuse types from specified assemblies. You can configure this by setting service reference properties or by /reference key if you are running svcutil.exe manually.

If you are the owner of mentioned services, consider putting data types like Person class into separate assembly which is available from both client and service.

Ihar Voitka
You can do this with the advanced settings in the add service reference dialog as well.
Brian ONeil
+2  A: 

There is a check box under the Advanced section of "Add Service Reference" named "Reuse types in referenced assemblies". This will hunt for types used in your service and if they already exist in a referenced assembly then they'll be used rather than a proxy class generated.

One caveat here is that it's only "referenced assemblies" that are searched, so it won't pick up proxies generated by other services (and I believe the different namespace would stop it as well).

I usually have a business / domain project in my Silverlight project so I add my shared classes to that project (usually with the "Add Existing Item" > "Add as Link" so the code is shared).

Once that's done you can generate your service references and they should pick up your existing types.

Hope this helps

Nigel Sampson
Thanks Nigel. I have seen the "Reuse types in referenced assemblies" checkbox in the Advanced dialog. It seems to be on by default. You are correct that it doesn't appear to pick up proxies generated by other services.When you speak of adding your own shared classes I assume they are from a "Silverlight Class Library"? So it sounds like you are generating the proxy yourself and sharing it with the services. Is that correct?
Justin
I shared the DataContracts between the client and server, I do this by "add existing item", the trick is to make sure you select "add as link" this means the class is shared between the two. Essentially I'm still generating the service proxy (StaffServiceClient) but not the classes (Person).
Nigel Sampson
Awesome Nigel. I've gone ahead with this solution and its works. Took a little bit of fiddling I see that you MUST fill in the "Namespace" attributes in your DataContracts or it doesn't pick the classes up for some reason. Also its really annoying that I have to create a separate assembly for just the Proxies on the client side. Would be much nicer if I could just link the source to the same project that I'm creating the service in. Reason is I was already putting the service in its own Project so it could be shared amongst other projects.
Justin