7

This practical Haskell example encouraged me to install the Visual Studio 2012 trial in order to use F# type providers. However, I am completely stumped as to how to use it to solve that problem. There is an RCSB SOAP web service. I copied an example (which doesn't work because the web service API has changed) using the URL for the WSDL web service from the RCSB:

open Microsoft.FSharp.Data.TypeProviders

type pdb = WsdlService<"http://www.rcsb.org/pdb/services/pdbws?wsdl">

do
    let ws = pdb.Getpdbws()
    ws.getCurrentPdbIds()
    |> printfn "%A"

But this crashes at run-time with the error:

Unhandled Exception: System.InvalidOperationException: RPC Message blastPDBRequest1 in operation blastPDB1 has an invalid body name blastPDB. It must be blastPDB1
   at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector.OperationReflector.EnsureMessageInfos()
   at System.ServiceModel.Description.XmlSerializerOperationBehavior.Reflector.EnsureMessageInfos()
   at System.ServiceModel.Description.XmlSerializerOperationBehavior.CreateFormatter()
   at System.ServiceModel.Description.XmlSerializerOperationBehavior.System.ServiceModel.Description.IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
   at System.ServiceModel.Description.DispatcherBuilder.BindOperations(ContractDescription contract, ClientRuntime proxy, DispatchRuntime dispatch)
   at System.ServiceModel.Description.DispatcherBuilder.ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime clientRuntime)
   at System.ServiceModel.Description.DispatcherBuilder.BuildProxyBehavior(ServiceEndpoint serviceEndpoint, BindingParameterCollection& parameters)
   at System.ServiceModel.Channels.ServiceChannelFactory.BuildChannelFactory(ServiceEndpoint serviceEndpoint, Boolean useActiveAutoClose)
   at System.ServiceModel.ChannelFactory.CreateFactory()
   at System.ServiceModel.ChannelFactory.OnOpening()
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.ChannelFactory.EnsureOpened()
   at System.ServiceModel.ChannelFactory`1.CreateChannel(EndpointAddress address, Uri via)
   at System.ServiceModel.ChannelFactory`1.CreateChannel()
   at System.ServiceModel.ClientBase`1.CreateChannel()
   at System.ServiceModel.ClientBase`1.CreateChannelInternal()
   at System.ServiceModel.ClientBase`1.get_Channel()
   at Program.pdb.ServiceTypes.PdbWebServiceClient.getCurrentPdbIds()
   at Program.pdb.ServiceTypes.SimpleDataContextTypes.PdbWebServiceClient.getCurrentPdbIds()
   at <StartupCode$ConsoleApplication2>.$Program.main@() in c:\users\jon\documents\visual studio 11\Projects\ConsoleApplication2\ConsoleApplication2\Program.fs: line 5

Also the SOAP web service is being deprecated in favor of a RESTful one. How might I use that from F# 3.0? What does the simplest working example look like?

J D
  • 46,493
  • 12
  • 162
  • 266

1 Answers1

7

Looks like operation BlastPDB is overloaded and the underlying generation code used by the TypeProvider does not properly support that (it is not putting the "1" on the body name). See this answer for the same issue when using Svcutil directly WCF: Svcutil generates invalid client proxy, Apache AXIS Web Service, overload operations - This page shows that the Type Provider uses svcutil internally.

AFAIK you will not be able to access the REST service using a Type Provider as REST services don't provide schema (see this answer F# Type Providers and REST apis). You will likely have to fallback on a REST client library (see some options here .NET Rest Client Frameworks) or do raw HTTP.

What does the simplest working example look like?

Following is a simple example to get the the current list of PDB ids using the List all current PDB IDs REST API (I believe this is equivalent to the call you attempted with the web service). You will need to add a reference to System.Net.Http.

open System.Net.Http
open System.Threading.Tasks

[<EntryPoint>]
let main argv = 

    use httpClient = new HttpClient()
    let task = httpClient.GetStringAsync("http://www.rcsb.org/pdb/rest/getCurrent")
    printfn "%s" task.Result

    0
Community
  • 1
  • 1
bentayloruk
  • 3,900
  • 25
  • 31
  • So the sole purpose of the type provider is to autogenerate code with a statically typed interface and the code it is generating is wrong? – J D Aug 19 '12 at 12:50
  • 2
    The sole purpose of the WSDL type provider is to provide the types for a WSDL web service. The F# team have reused existing tools where they can (no point in writing twice). Unfortunately, you have immediately hit upon a bug in the tool they used. I doubt the F# team are aware of this, but I suggest you raise it as a bug (there may be a workaround). – bentayloruk Aug 19 '12 at 12:55
  • Type providers were released with known bugs, I don't think this was one of them though. – 7sharp9 Aug 19 '12 at 12:59
  • W3 Basic Profile prohibits overloading. The WSDL may be at fault. – Sentinel Nov 17 '13 at 19:08