So here it is:
I am trying to invoke a WCF Service's method from with in a windows form client application. I do not have the Contract Class Definition. The only thing I have is the Service's URI, which is http://ip:port/Service1.svc.
So I thought i could make a proxy class for that. I want to make the proxy class at run time so any external tool to create a proxy class and import to project can't help me.
I managed to make the proxy class on the fly. Here's the code:
Dim mexClient As MetadataExchangeClient = New MetadataExchangeClient(New Uri(webServicesWSDLUrl), MetadataExchangeClientMode.HttpGet)
mexClient.ResolveMetadataReferences = True
Dim metaDocs As MetadataSet = mexClient.GetMetadata()
Dim importer As WsdlImporter = New WsdlImporter(metaDocs)
Dim generator As ServiceContractGenerator = New ServiceContractGenerator()
Dim dataContractImporter As New Object
Dim xsdDCImporter As XsdDataContractImporter
If (Not importer.State.TryGetValue(GetType(XsdDataContractImporter), dataContractImporter)) Then
Console.WriteLine("Couldn't find the XsdDataContractImporter! Adding custom importer.")
xsdDCImporter = New XsdDataContractImporter()
xsdDCImporter.Options = New ImportOptions()
importer.State.Add(GetType(XsdDataContractImporter), xsdDCImporter)
Else
xsdDCImporter = TryCast(dataContractImporter, XsdDataContractImporter)
If (xsdDCImporter.Options Is Nothing) Then
Console.WriteLine("There were no ImportOptions on the importer.")
xsdDCImporter.Options = New ImportOptions()
End If
End If
Dim exts As System.Collections.IEnumerable = importer.WsdlImportExtensions
Dim newExts As New System.Collections.Generic.List(Of IWsdlImportExtension)
For Each ext As IWsdlImportExtension In exts
Console.WriteLine("Default WSDL import extensions: {0}", ext.GetType().Name)
newExts.Add(ext)
Next
Dim polExts As System.Collections.IEnumerable = importer.PolicyImportExtensions
importer = New WsdlImporter(metaDocs, polExts, newExts)
Dim Contracts As System.Collections.ObjectModel.Collection(Of ContractDescription) = importer.ImportAllContracts()
importer.ImportAllEndpoints()
importer.ImportAllBindings()
For Each contract As ContractDescription In Contracts
generator.GenerateServiceContractType(contract)
Next
If generator.Errors.Count <> 0 Then
Throw New Exception("There were errors during code compilation.")
End If
Dim nmspace As CodeNamespace = New CodeNamespace()
Dim unit1 As CodeCompileUnit = New CodeCompileUnit()
unit1.Namespaces.Add(nmspace)
Dim CodeDomProvider As System.CodeDom.Compiler.CodeDomProvider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("VB")
Dim assemblyReferences() As String
assemblyReferences = New String() {"System.dll", _
"System.Web.Services.dll", "System.Web.dll", _
"System.Xml.dll", "System.Data.dll"}
Dim parms As CompilerParameters = New CompilerParameters(assemblyReferences)
parms.GenerateInMemory = True '(Thanks for this line nikolas)
'CompilerRsults
results = CodeDomProvider.CompileAssemblyFromDom(parms, generator.TargetCompileUnit)
Now using the results
I make an Instance of my WebService (methodName = "GetData"
, args = new Object() { 4 }
): See Below..
Dim wsvcClass As Object = results.CompiledAssembly.CreateInstance(serviceName)
Dim retValue As Object = wsvcClass.GetType().InvokeMember(methodName, BindingFlags.InvokeMethod, Nothing, wsvcClass, args)
The last command throws an Exception
A first chance exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
System.ServiceModel.EndpointNotFoundException: There was no endpoint listening at http://ip:port/Service1.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. ---> System.Net.WebException: The remote server returned an error: (404) Not Found.
at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
--- End of inner exception stack trace ---
Server stack trace:
at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.ReA first chance exception of type 'System.NullReferenceException' occurred in ThirdPartyAPIClientApp.exe
questChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at IService1.GetData(Int32 value)
at Service1Client.GetData(Int32 value)
Below You can find the web.config of the Web Service and the App.config of the client application
web.config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" strict="false" explicit="true" targetFramework="4.0" />
</system.web>
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="messages"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="c:\logs\messages.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
<system.serviceModel>
<diagnostics>
<messageLogging
logEntireMessage="true"
logMalformedMessages="false"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="false"
maxMessagesToLog="3000"
maxSizeOfMessageToLog="2000"/>
</diagnostics>
<services>
<!-- Note: the service name must match the configuration name for the service implementation. -->
<service name="WcfService1.Service1" behaviorConfiguration="BasicHttpBinding_This" >
<endpoint address="/Service1.svc" contract="WcfService1.IService1" binding="basicHttpBinding" bindingNamespace="http://ip:port/WCF1_Test" />
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="BasicHttpBinding_This">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_This" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="200000000000000" maxReceivedMessageSize="200000000000000"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
App.config
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_This" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="200000000" maxReceivedMessageSize="200000000"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="200000000"
maxArrayLength="200000000" maxBytesPerRead="200000000" maxNameTableCharCount="200000000" />
<security mode="None">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://ip:port/Service1.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_This"
contract="IService1" name="BasicHttpBinding_This" />
</client>
</system.serviceModel>
</configuration>
Additionally I am sending the contract and the code class of the WebService
Contract
<ServiceContract(Namespace:="http://ip:port/WCF1_Test")>
Public Interface IService1
<OperationContract(isOneway:=False)>
Function GetData(ByVal value As Integer) As String
<OperationContract(isOneway:=False)>
Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType
' TODO: Add your service operations here
End Interface
' Use a data contract as illustrated in the sample below to add composite types to service operations.
<DataContract()>
Public Class CompositeType
<DataMember()>
Public Property BoolValue() As Boolean
<DataMember()>
Public Property StringValue() As String
End Class
Code Class
<ServiceBehavior(Namespace:="http://ip:port/WCF1_Test")>
Public Class Service1
Implements IService1
Public Sub New()
End Sub
Public Function GetData(ByVal value As Integer) As String Implements IService1.GetData
Return String.Format("You entered: {0}", value)
End Function
Public Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType Implements IService1.GetDataUsingDataContract
If composite Is Nothing Then
Throw New ArgumentNullException("composite")
End If
If composite.BoolValue Then
composite.StringValue &= "Suffix"
End If
Return composite
End Function
End Class
Does any one knows the solution? What is the proper way to invoke a WCF method Dynamically if someone only knows the URI? PS: the Web Service is Up and Running on an IIS7