9

I can't seem to find an example of generating proxies from WSDLs with shared types but without having any XSDs to go along with them. Can anyone please mark this as duplicate and point me to an example please?

Here are 2 services, each has its own namespace and a common type. The only thing that is publicly accessible are their WSDLs, there is no type's XSD or its .dll to pass to wsdl.exe /sharedtypes or svcutils and without it I end up with identical class Foo that I can't pass in to SetFoo and class Foo1.

The best I could come up with is generating proxies programmatically and detecting duplicates via CodeDOM, ignoring DataContract/WebServiceBinding namespaces, but it's a huge mess...

[WebService(Namespace = "http://tempuri.org/FOO1")]
public class Service1 : WebService
{
    [WebMethod]
    public Foo GetFoo()
    {
        return new Foo();
    }
}

[WebService(Namespace = "http://tempuri.org/FOO2")]
public class Service2 : WebService
{
    [WebMethod]
    public void SetFoo(Foo foo)
    {
    }
}

public class Foo
{
    public int Bar { get; set; }
}
Ilya Kozhevnikov
  • 10,242
  • 4
  • 40
  • 70
  • As part of the WSDL definitions are the XSDs defining the types. If they are not defined in the WSDL how are you supposed to consume the service? – tom redfern Apr 24 '13 at 13:27
  • @hugh They are defined twice, in each WSDL, so given `Service1` and `Service2` wsdls I can't see a way to generate proxies for them with common `Foo` definition that can be passed from `GetFoo` to `SetFoo`. – Ilya Kozhevnikov Apr 24 '13 at 13:38
  • I understand. See my answer below – tom redfern Apr 24 '13 at 14:42
  • 1
    Those types are in different namespaces, so they are different types. If you want them to be the same type, then use the same namespace. – John Saunders Apr 24 '13 at 14:47
  • @JohnSaunders I agree, they are "different" as far as wsdls and xsd are concerned, on the backend however it's the same common type and it's unfortunate that each web service is under its own namespace attribute, it's a given. The question is exactly that -- how to extract common identical `Foo` type that only differ by namespace due to the way wsdl is autogenerated. – Ilya Kozhevnikov Apr 24 '13 at 15:07
  • 2
    My point is, that the WSDL and XSD are reality. You only imagine that the types are identical. If the services intend them to be seen as the same type, then they _must_ put them in the same XML namespace. "It's not just a good idea; it's the law" – John Saunders Apr 24 '13 at 15:09
  • @JohnSaunders Thing is, technically I don't even have wsdl/xsd, I have a ton of reflected types with `WebMethod`s in them and I need to programmatically generate proxies for them (`ServiceDescriptionReflector`/`WsdlImporter`/`ServiceContractGenerator`) so I go type -> `ServiceDescription` (WSDL) -> `MetadataSet` -> `CodeDOM` -> proxy, and those buggers all have unique namespaces even though they share common types like nobody's business. – Ilya Kozhevnikov Apr 24 '13 at 16:09
  • All I can think of is, if you can't change the services, then you can create wrapper services that actually _do_ use the correct namespaces. A consumer of the wrapper services will be able to share the types. Tye wrapper will hold the knowledge of the fact that the types with separate namespaces are really the same. – John Saunders Apr 24 '13 at 16:32

3 Answers3

1

There is a way of doing this, which is outlined here.

In your case you can skip the first step, generate the proxy from service 1 and then use the /r flag on svcutil to reference the service 1 proxy assembly when you generate your service 2 proxy.

This will ensure your service 2 proxy will use the same instance of Foo from your service 1 proxy.

However, have you considered just hosting a single service with two operations? It would save you a lot of work.

Edit: Also have a look at this post: http://blogs.msdn.com/b/youssefm/archive/2009/10/09/reusing-types-in-referenced-assemblies-with-svcutil-s-r-switch.aspx

Community
  • 1
  • 1
tom redfern
  • 30,562
  • 14
  • 91
  • 126
  • Doesn't seem to work, keeps 2 `Foo`s in 2 different namespaces. Here's the setup (https://github.com/kozhevnikov/WebService1) -- a `WebService1` with two services, a `ClassLibrary1` with shared `Foo` type and a `ConsoleApplication1` that tries to call `GetFoo`/`SetFoo` via proxy. I tried `svcutil Service1.wsdl && csc /t:library Service1.cs && svcutil Service2.wsdl /r:Service1.dll` and `svcutil Service1.wsdl /r:..\ClassLibrary1\bin\Debug\ClassLibrary1.dll` but in both cases end up with 2 incompatible `Foo`s. – Ilya Kozhevnikov Apr 24 '13 at 15:55
  • Can you post the WSDLs and I will have a look - it may be that the two Foo's really are separate types defined within different XSDs, in which case you cannot use the /r flag because the types are not truly equivalent. – tom redfern May 07 '13 at 09:59
  • Exactly, they come from same class but technically are different cause autogenerated wsdl puts two copies under two different namespaces (parent web services where they're used) hence the question. Example wsdls are at https://github.com/kozhevnikov/WebService1/tree/master/wsdl – Ilya Kozhevnikov May 07 '13 at 10:01
1

First off, you need to set the [DataContract(Namespace="some namespace here")] for all common service data types, otherwise when the WSDL and XSDs are generated then you will have objects from two difference namespace --- this is absolutely essential. The namespace value will only apply to the types defined in the XSD and not in the WSDL. XSD = data, WSDL = service.

The XSDs and WSDL and generated if, and only if, you have the META service behavior set - add this behavior and then you can navigate to the URL. The URL of the META service behavior will then have a link to your WSDLs and XSDs.

I use the following piece of code to self-host services in windows services rather than through IIS, however the same principals apply....

/// <summary>
/// Enables meta data output for a service host.
/// </summary>
/// <param name="host">The service host.</param>
/// <remarks>Must be invoked prior to starting the service host.</remarks>
public static void SetupMetaDataBehaviour(ServiceHost host)
{
    ServiceMetadataBehavior metaDataBehaviour = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
    if (metaDataBehaviour == null)
    {
        metaDataBehaviour = new ServiceMetadataBehavior();
        metaDataBehaviour.HttpGetEnabled = true;
        host.Description.Behaviors.Add(metaDataBehaviour);
    }
    else
    {
        metaDataBehaviour.HttpGetEnabled = true;
    }
}
  • Thanks, but as per OP I cannot modify services or types, I only have access to raw autogenerated WSDLs as if they are public (technically I have reflected types from which I generate WSDLs myself), services have unique [WebService(Namespace)] on them and types don't have anything on them except for occasional XmlInclude, no service or namespace related attributes. – Ilya Kozhevnikov May 01 '13 at 10:21
  • Just like the `GetFoo`/`SetFoo` example, I have WSDLs for `Service1` and `Service2` and I need to generate proxies for them with shared `Foo` so I can call `GetFoo` and pass it to `SetFoo` under different autogenerated namespace (works when I fiddle with `CodeDOM`, extract `Foo` and drop any namespaces, but this approach doesn't scale to hundreds of obscure legacy web services). – Ilya Kozhevnikov May 01 '13 at 10:23
1

after adding your two web references:

  1. double click on the second web service reference
  2. in the object browser navigate to the definition of Foo
  3. right click on Foo and choose go to definition
  4. delete the definition for the class Foo
  5. add a using statement for the namespace of webservice one
  6. find and replace all instances of <namespace-of-service-reference-2>.Foo with just Foo

This should fix your problem as it forces the autogenerated code for both service references to use the same class declaration.

Charles Lambert
  • 5,042
  • 26
  • 47
  • 1
    Thanks, but it's the same as post-generation CodeDOM manipulations, only manual. I'm looking for a way to do it automatically or programmatically (eg CodeDOM) and scalable to hundreds or legacy web services with thousands of shared types. – Ilya Kozhevnikov May 07 '13 at 10:05
  • I understand what you want to do. But since visual studio autogenerates two classes in different namespaces using only the wsdl file, you aren't going to get that. My solution takes all of about 5 minutes to make the change on a large project. Its repeatable, and easy to remember. – Charles Lambert May 07 '13 at 12:36
  • Also, this is not a common scenario. especially in an environment with "hundreds or legacy web services" you may run into this problem once, with one 3rd party API out of hundreds. If it is internal code, don't write it this way. If you aren't the person calling the shots, and someone is architecting the services this way, you need to sit down and show them the level of frustration that this design causes. – Charles Lambert May 07 '13 at 12:41