I am following this tutorial on how to send a SOAP message via C#, and have reached this stage:
Program
using System;
using System.Xml;
using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Addressing;
using Microsoft.Web.Services3.Messaging;
namespace SOAP
{
class Program
{
static void Main(string[] args)
{
Uri strEpr = new Uri("http://www.webservicex.com/globalweather.asmx?WSDL");
EndpointReference epr = new EndpointReference(strEpr);
TcpClient client = new TcpClient(epr);
}
}
}
TcpClient
using System.Xml;
using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Addressing;
using Microsoft.Web.Services3.Messaging;
namespace SOAP
{
class TcpClient : SoapClient
{
public TcpClient(EndpointReference endpointreference)
{
SoapClient();
}
[SoapMethod("RequestResponseMethod")]
public SoapEnvelope RequestResponseMethod(SoapEnvelope envelope)
{
return base.SendRequestResponse("RequestResponseMethod", envelope);
}
}
}
However, in the constructor in my TcpClient class I am seeing this error:
Non-invocable member 'SoapClient' cannot be used like a method.
I can see why this is, because the SoapClient class is abstract and its constructors are all protected. Does this mean that the MSDN documentation is out of date, or am I missing something here?
All I need to do is send a SOAP message to a web service and get the response - surely this should be quite easy in C#?
Although you now use a different approach, the problem with your posted code is that you didn't call the base classes constructor.
You should have done it like so
public TcpClient(EndpointReference endpointreference)
: base(endpointreference)
{}
As per Kosala W's recommendation, I found a solution to this without using SOAP messaging.
Right click the project in the Solution Explorer and select Add -> Service Reference.
In the dialog, type in the WSDL address and give it a name . This generates some tags in the app.config file with details about the Endpoint entered.
The service reference can now be called in the code. For example, if I created a Service Reference called Darwin, I can now call the methods associated with this web service like this:
Darwin.LDBServiceSoapClient client = new Darwin.LDBServiceSoapClient();
Darwin.StationBoard myBoard = client.GetDepartureBoard(params, go, here);
Where the client is used to send the message, and the GetDepartureBoard performs some operation on the web server (In this case, the method retrieves data about the specified Train Times Departure Board and returns it in SOAP message format).
Thanks Kosala w!
Related
I add the reference and all and then when i have to build up to getting the methods and then parsing values to the different atrributes i cant seem to get methods that i can pass variables too, all i can get is the attributes themselves which i cant parse values directly too.. i am new to soap web services and would like some assistance. the WSDL URL is https://secure.transunion.co.za/TUBureau118test/Consumer.asmx?WSDL and I have trying to do this with the HelloWorld method and the BureauPing, any of the two as an example to the way forward would be great.
Assuming that you added the reference as a WCF service under the namespace 'ServiceReference1':
class Program
{
static void Main(string[] args)
{
var ws = new ServiceReference1.ConsumerSoapClient();
string result = ws.HelloWorld();
}
}
My infrastructure:
Main - ServiceStack self hosted console app. 'Main' sends messages to MQ.
Background - ServiceStack self hosted console app. 'Background' receives messages from MQ.
Locally installed Redis
In 'Main' AppHost I configure Redis manager:
container.Register<IRedisClientsManager>(
new PooledRedisClientManager("localhost:6379"));
Then I run this code somewhere in service:
using (var client = new RedisMessageQueueClient(TryResolve<IRedisClientsManager>()))
{
client.Publish(new TestMessage { Value = "From ping" });
}
Everything works great and I can get message in my 'Background'. But problem comes when I wrap this code in class:
public class MessageQueuePublisher : IMessageQueuePublisher
{
public void Publish(object message)
{
using (var client = new RedisMessageQueueClient(
EndpointHost.AppHost.TryResolve<IRedisClientsManager>()))
{
client.Publish(message);
}
}
}
When I call MessageQueuePublisher.Publish method from the exactly same place where previous code was executed, it seems like it works correctly (no exceptions are thrown), but my message doesn't reach 'Background'.
Is this OK?
I found a solution. On my 'Background' I expect message with type TestMessage
mqService.RegisterHandler<TestMessage>(ServiceController.ExecuteMessage);
But when using MessageQueuePublisher.Publish message was of type object and went to the object queue and wasn't handled.
So to solve this problem Publish method should be generic:
public void Publish<T>(T message)
It doesn't change how method is called but code is not so good because if you look at it, it's not clear why generic is used. But at least it works.
I'm consuming a SOAP web service. The web service designates a separate service URL for each of its customers. I don't know why they do that. All their functions and parameters are technically the same. But if I want to write a program for the service I have to know for each company is it intended. That means for a company called "apple" i have to use the following using statement:
using DMDelivery.apple;
and for the other called "orange"
using DMDelivery.orange;
But I would like to my program to work for all of them and have the name of the company or the service reference point as a parameter.
Update: If I have to write a separate application for each customer then I would have to keep all of them updated with each other with every small change and that would be one heck of an inefficient job as the number of customers increase.
Can anyone think of a solution? I'll be grateful.
If you have a base contract (interface) for all your services you can use a kind of factory to instantiate your concrete service and only have a reference to your interface in your client code (calling code).
//service interface
public interface IFruitService{
void SomeOperation();
}
//apple service
public class AppleService : IFruitService{
public void SomeOperation(){
//implementation
}
}
Having for example a kind of factory class (you can put your using statements here)
public static class ServiceFactory{
public static IFruitService CreateService(string kind){
if(kind == "apple")
return new AppleService();
else if(kind == "orange")
return new OrangeService();
else
return null;
}
}
And in your calling code (you just add an using statement for the namespace containing your interface):
string fruitKind = //get it from configuration
IFruitService service = ServiceFactory.CreateService( fruitKind );
service.SomeOperation();
You can also use the Dependency Injection principle.
If everything is the same and it's only the endpoint address that is different, maybe you can try changing only that before invoking the web service methods.
MyWebServiceObject ws= new MyWebServiceObject();
ws.Endpoint.Address = new System.ServiceModel.EndpointAddress("http://www.blah.com/apple.asmx");
Use any one client in your implementation. ex. Apple
Write a message inspector and attach this into the out going point
In message inspector replace the name space of the type with appropriate client name space.
EX:
Before Message inspector :MyClinet.Apple.Type
After Message Inspector : MyClient.Orange.Type, if the Provider is Orange.
I have a WCF Host with something like this:
[ServiceContract]
public interface IMountToOs
{
[OperationContract]
char GetMountDriveLetter();
[OperationContract]
MyTestClass MyTest();
}
public class MyTestClass
{
public string A { get; set; }
public string B { get; set; }
}
Client
private IMountToOs _proxy;
public IMountToOs Proxy
{
get
{
if (_proxy == null)
{
NetTcpBinding binding = new NetTcpBinding();
binding.MaxReceivedMessageSize = 2147483647;
binding.OpenTimeout = TimeSpan.FromMilliseconds(50000);
EndpointAddress address = new EndpointAddress("net.tcp://localhost:1234/MountToOsHost");
//_proxy = new MountToOsClient(binding, address);
ChannelFactory<IMountToOs> factory = new ChannelFactory<IMountToOs>(binding);
_proxy = factory.CreateChannel(address);
}
return _proxy;
}
}
While I can access
MessageBox.Show("Okay - " + Proxy.GetMountDriveLetter());
I can't call this method:
MessageBox.Show("Okay - " + Proxy.MyTest().A);
The complete extension is not working. But only while using it in an extension. Even if I insert a Messagebox in the first line of the extension it is not hit. I don't know why. It seems to run a pre-check and find the call of the custom class which is refused or so...
If I use a winform or so there is no problem.
.net 3.5
curious is that I have a break-point and a message of the hosts side. So I see that the method is not called
Update
now I moved the wcf-call in the Load Method of the extension and get a exception:
System.MissingMethodException: method not found:
"Contracts.Interfaces.MyTestClass
Contracts.Interfaces.IMountToOs.MyTest()".
My winform test and this extension use the same interface so that the method should known from both. no contract or so is outdated
According to what I found here and in the comments of the post: "For creating dynamic service proxy using client channel factory method, you will need datacontracts of the service. If you don't have datacontracts but you have the service URL, then you could use reflection to create proxy at runtime and call the service method."
Seems that the MyTestClass type is not known on the client side, so I think you could use reflection, or share the class between the client and server or much more simple, use the datacontract attribute.
Also, found something on MSDN that says something like this:
"When to use a proxy?
We create proxy using svcutil.exe. The output of this tool gives a proxy class and makes corresponding changes to the application configuration file. If you have a service that you know is going to be used by several applications or is generic enough to be used in several places, you'll want to continue using the generated proxy classes. We use proxy in WCF to be able to share the service contract and entities with the client. Proxies have several restrictions like they need to have gets and sets , contructors can't be exposed , methods other than the service contract cannot be exposed, repetition of code, everytime that we add/modify a service contract/data contract/message contract we need to re-generate the proxy for the client.
When to use ChannelFactory
The other option is using the ChannelFactory class to construct a channel between the client and the service without the need of a proxy . In some cases, you may have a service that is tightly bound to the client application. In such a case, it makes sense to reference the Interface DLL directly and use ChannelFactory to call your methods using that. One significant advantage of the ChannelFactory route is that it gives you access to methods that wouldn't otherwise be available if you used svcutil.exe..
When to use a ChannelFactory vs Proxy class?
A DLL is helpful if the client code is under you control and you'd like to share more than just the service contract with the client -- such as some utility methods associated with entities and make the client & the service code more tightly bound. If you know that your entities will not change much and the client code is less, then a DLL would work better than a proxy. If the client to your service is external to the system, such as API, it makes sense to use a proxy, because it makes sharing the contract easier by giving a code file rather than a DLL."
We cant see the class
MountToOsClient: IMountToOs
So we can only assume it is ok.
[DataContract] // Missing
public class MyTestClass
{
[DataMember] // Missing
public string A { get; set; }
[DataMember] // Missing
public string B { get; set; }
}
MountToOsClient can not expose Mytestclass without these attributes.
I have a very basic web service:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
namespace WebService1
{
/// <summary>
/// Summary description for Service1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class Service1 : System.Web.Services.WebService
{
public int myInt = 0;
[WebMethod]
public int increaseCounter()
{
myInt++;
return myInt;
}
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
}
}
when I run that project my browser opens showing me the service:
on a different solution: (console application)
I am able to connect to that service by adding the reference:
then click on the add web reference button:
Lastly I type the url of the service I just created:
Now I am able to instantiate an object from the class Service1 from my console application as:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication36
{
class Program
{
static void Main(string[] args)
{
localhost.Service1 service = new localhost.Service1();
// here is the part I don't understand..
// from a regular class you will expect myInt to increase every time you call
// the increseCounter method. Even if I call it twice I always get the same result.
int i;
i=service.increaseCounter();
i=service.increaseCounter();
Console.WriteLine(service.increaseCounter().ToString());
Console.Read();
}
}
}
why does myInt does not increase every time I call the increaseCounter method? every time I call that method it returns 1.
Services created through the older .asmx technology are not singleton instances. This means that each call you make to the server instantiates a new instance of the service each time. Two real solutions, either use static variables (eugh....), or switch to using WCF.
Becaue on the server side the class is created and disposed with EVERY call you make from the client... your client is just a "proxy" and doesn't correspond directly to an instance on the server side...
You can either make myInt static or make the server side service class a Singleton... both options would mean that myIntis shared across ALL client... or you could implement some session management to achieve a client-specific myInt... using WCF for the server side seems IMHO the best solution - it comes with configurable options for singleton, session management etc.
EDIT - as per comments:
With WCF you can have .NET-clients with session management which in turn allows you to have different (client-specific) values for myInt...
webservice instance is destroyed at the end of each method call, so that's why you always get the same result. You need some way to persist that value.