How to implement IsOneWay=true in WCF nettcpBinding - wcf

How can I implement one way WCF operations?
I just tried using IsOneWay attribute as:
[OperationContract(IsOneWay=true)]
void MethodName(string param1, int param2)
Is there any other change I need to make or any specific change in app.config?
FYI, my WCF service implements netTcpBinding, though I think that shouldn't make any difference.

As shown, your code looks ok. There should be no problem with doing one-way calls with netTcpBinding.
If you're interested, chapter 5 in Juval Lowy's awesome Programming WCF Services 2nd Edition contains a good bit of information about one-way services.
From what you've shown, so far though I don't see anything wrong. Please give us some more details.

We had a problem with one-way calls not returning immediately using the NetTcpBinding. This blog post identifies the problem and provides a solution.
http://blogs.msdn.com/b/distributedservices/archive/2009/02/12/client-proxy-close-method-call-does-not-finish-immediately-in-one-way-wcf-calls.aspx
From the article:
Problem: Clients calling a one-way method in WCF Service and then close method on proxy does not return until the call is actually finished or call times out. Ever wonder why this happens?
Cause: When you specify “One-Way” on your interface, the underlying channel operation is still two-way since the one way binding element is not in the channel stack. Thus, the close operation gets blocked until the one way operation completes.
This is by design and the development team is working to change it in future versions of .Net framework.
...
Solution (Work around):
Layer the OneWayBindingElement on top of netTcpBinding as shown in the below code. This way, close call on proxy will return immediately and eventually the one-way call will return in fire and forget fashion.
[ServiceContract]
public interface IService1
{
[OperationContract(IsOneWay = true)]
void SetData(int value);
}
public class Service1 : IService1
{
public void SetData(int value)
{
//Application specific code
}
}
Service Host code:
Form1ServiceHost = new ServiceHost(this, new Uri("net.tcp://localhost:8091/WindowsFormApp/Form1/"), new Uri("http://localhost:8090/WindowsFormApp/Form1/"));
Binding binding = new NetTcpBinding();
BindingElementCollection oldBindingElements = binding.CreateBindingElements();
BindingElementCollection bindingElements = new BindingElementCollection();
bindingElements.Add(new OneWayBindingElement());
foreach (BindingElement bindingElement in oldBindingElements)
{
bindingElements.Add(bindingElement);
}
binding = new CustomBinding(bindingElements);
Form1ServiceHost.AddServiceEndpoint("WCFServiceLibrary.IService1", binding, "");
Form1ServiceHost.Open();
Client Code:
Binding binding = new NetTcpBinding();
BindingElementCollection oldBindingElements = binding.CreateBindingElements();
BindingElementCollection bindingElements = new BindingElementCollection();
bindingElements.Add(new OneWayBindingElement());
foreach (BindingElement bindingElement in oldBindingElements)
{
bindingElements.Add(bindingElement);
}
binding = new CustomBinding(bindingElements);
Service1Client client = new Service1Client(binding, new EndpointAddress("net.tcp://localhost:8091/WindowsFormApp/Form1/"));
client.SetData(10);
Console.WriteLine("set data");
Console.WriteLine("Now closing the channel,Before close, current time is {0}", DateTime.Now.ToString() + " " + DateTime.Now.Millisecond.ToString());
client.Close();
Console.WriteLine("Now closing the channel,After close, current time is {0}", DateTime.Now.ToString() + " " + DateTime.Now.Millisecond.ToString());`

Related

Adding service reference wcf

The issue has already discussed here, but it did not addressed it quite the way I am looking for.
I have already created a service reference from a client console app in visual studio, but I want to do it programmatically with the following contraint:
From Microsoft Docs - wcf, it obvious that we have to have the service interface reference available to the client. In my case I do have the reference available, instead I have the address where the service is hosted and this address is a dynamic one.
So I want to define a customized client class that will have its object declared with the host address only. Lets take the following snippet as an example:
public partial class CalculatorServiceClient : System.ServiceModel.ClientBase<ICalculatorService>, ICalculatorService
{
}
As you can see that ICalculatorService is available while defining the class. What to do if the interface to the service is not available while defining the class.
You can connect to the WCF service pragmatically without having to use the generated class methods, but note that this can have issues if the service changes in future
The idea is simple .
Create a service contract that matches your service implementation
[DataContract]
public class SomeDataContarctClass
{
[DataMember]
public string SomeMember{get;set;}
etc....
}
Create the interface
public IServiceInterface
{
[OperationContract]
List<SomeDataContarctClass> GetSomeData();
...etc
}
Now this is where you start to Glue things together,
Then create the service
public IServiceInterface CreateIService()
{
EndpointAddress myEndpoint = new EndpointAddress("SERVICE URL");
BasicHttpBinding binding= new BasicHttpBinding();
defaultBinding.MaxReceivedMessageSize = 2147483647;
defaultBinding.MaxBufferPoolSize = 2147483647;
defaultBinding.MaxBufferSize = 2147483647;
defaultBinding.ReaderQuotas.MaxArrayLength = 2147483647;
defaultBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
ChannelFactory<IUpdaterService> myChannelFactory = new ChannelFactory<IServiceInterface>(binding, myEndpoint);
myChannelFactory.Endpoint.EndpointBehaviors.Add(new ServiceInterceptionBehavior());
// Create a channel.
return myChannelFactory.CreateChannel();
}
Then you can call the service using
var myserviceImp = CreateIService();
var data = myserviceImp.GetSomeData();

How to implement an async wcf client for a synchronous service

Is there a way to create an async client for a synchronous WCF service without adding a service reference? This is for a .NET 4 client.
A service reference in Visual Studio is nothing else than a code generator that creates a proxy class with corresponding data elements necessary to call your web service. Of course you can hand build a proxy if you really want to go over tedious and boring work.
Maybe start by decompiling System.ServiceModel.ClientBase using .net reflector?
Do some research on ChannelFactory: http://msdn.microsoft.com/en-us/library/system.servicemodel.channelfactory.aspx
Even when implementing my own client by wrapping a ChannelFactory, I am still using the Add Service reference in another project to create the class definitions and move them into the real project. That's a good compromise.
Here's a simple async service interface:
[ServiceContract(Name = "IService")]
public interface IServiceAsync
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetStuff(string someData, AsyncCallback callback, object state);
IEnumerable<Stuff> EndGetStuff(IAsyncResult result);
}
The .NET contract might look like this:
[ServiceContract]
public interface IService
{
[OperationContract]
IEnumerable<Stuff> GetStuff(string someData);
}
Then in code, assuming you use HTTP, No security and binary message encoding, something like this (Sorry I haven't compiled any of this, just typed it using some of the code I have written for projects):
//Create a binding for the proxy to use
HttpTransportBindingElement httpTransportBindingElement;
httpTransportBindingElement = new HttpTransportBindingElement();
absoluteServiceUri = new Uri(absoluteServiceUri.OriginalString + BinaryEndpointUri, UriKind.Absolute);
}
//Create the message encoding binding element - we'll specify binary encoding
var binaryMessageEncoding = new BinaryMessageEncodingBindingElement();
//Add the binding elements into a Custom Binding
var customBinding = new CustomBinding(binaryMessageEncoding, httpTransportBindingElement);
// Set send timeout
customBinding.SendTimeout = this.SendTimeout;
var factory = new ChannelFactory<IServiceAsync>(customBinding, new EndpointAddress(absoluteServiceUri, new AddressHeader[0]));
var channel = factory.CreateChannel();
channel.BeginGetStuff(Bla, results => { // Do something }, null);

Using an existing WCF service

I am very new at programming WCF services, so I hope that if you answer my question - you will take that into account and explain it to me as if I was a kid (wcf services for dummies :). I have an existing WCF service which I need to connect to. I am supposed to make my own WCF service that will communicate with the existing one and share some request and response objects which are already defined in the existing service. Can anyone tell me how to do that (establish the communication between the two and use the same type of object in the service which I need to make as it is in the existing one), step by step? I have tried to find the answer online but it is all a bit confusing (referencing, using contracts...). As I said, you are free to explain as if you would to a real beginner. Any help is more than welcome...
"I am supposed to make my own WCF service that will communicate with the existing one and share some request and response objects which are already defined in the existing service." - This sounds like you need to create a client to connect to the service (see below how to create client). You can create WCF service to communicate with another service but you would need bit more background than this format allows.
You can get up to speed with WCF through WCF examples. Under WF_WCF_Samples\WCF\Basic in the examples you can find many Service/Client setups that you should go through first. MSDN Magazine has tons of articles on this topic.
In a 10,000 foot view of things:
Client - To consume service create a test console application. Add Service Reference in your project (when you right click references you will see that option). Point the address of the Service Reference dialog to the service you would like to consume and lot of stuff will happen. Final result is that you can call service methods on your service with something like below (where Service1 will be replaced with what ever service you are calling)
static void Main(string[] args)
{
var proxy = new ServiceReference1.Service1Client();
var test = proxy.GetData(1);
}
Service - you would create an interface with methods and types then decorate this interface with attributes for example:
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
}
These are operations (OperationContract) that your serive can perform. Service methods can return primitive or complex type (string vs. CompositeType) as well as take parameters that are complex or primitive.
You would implement this contract:
public class Service1 : IService1
{
public string GetData(int value)
{
throw new ApplicationException("Boom");
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
Next you need to host your service. You have many options to accomplish this depending on your hosting requirements. The simplest hosting you can do is using Console application:
class Program
{
static void Main(string[] args)
{
var host = new ServiceHost(typeof(Service1), new Uri("http://localhost:8999/"));
host.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "");
var metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetEnabled = true;
host.Description.Behaviors.Add(metadataBehavior);
}
host.Open();
Console.WriteLine("Running..");
Console.ReadLine();
}
}

Configuring WCF client and service for use with protobuf-net

I decided to open a new question about this matter, maybe expanding this question, not having found a precise answer about the issue anywhere on the Internet.
I want to use protobuf-net to serialize/deserialize messages exchanged between my WCF client and service. The service is self-hosted in a Windows Service. Both client and service are configured programmatically, using a custom binding very similar to wsHttpBinding. Service reference code is generated using "Add Service Reference" option in Visual Studio. The ORM used on the WCF service is EntityFramework 4 and it's code is generated using EF 4.x POCO Generator. More info about my service configuration can be found in a question I started here (that's where I described that my current serializer is DataContractSerialzizer).
I have only tested protobuf-net with one service operation which returns a list of custom DTOs.
Here is the operation (be advised that I just did a copy-paste of my code to here, there might be some fields named in my domestic language, not English):
public static List<OsobaView> GetListOsobas()
{
Database DB = new Database(); // EF object context
var retValue = DB.Baza.Osoba
.Select(x => new OsobaView
{
ID = x.ID,
Prezime = x.Prezime,
Ime = x.Ime,
Adresa = x.Adresa,
DatumRodjenja = x.DatumRodjenja,
JMBG = x.JMBG
});
return retValue.ToList();
}
Here is the definition of OsobaView class:
[ProtoContract]
public class OsobaView
{
[ProtoMember(1)]
public int ID;
[ProtoMember(2)]
public string Prezime;
[ProtoMember(3)]
public string Ime;
[ProtoMember(4)]
public string Adresa;
[ProtoMember(5)]
public DateTime DatumRodjenja;
[ProtoMember(6)]
public string JMBG;
}
As I am using "Add Service Reference" to generate the reference code, I had to use one of the two work-arounds in order to have my client recognize ProtoContracts and members:
using a shared assembly for DTOs (which is not an ideal solution in my case except for custom DTOs, due to the fact that I pass EF-generated POCOs to the client)
using ProtoPartialMember approach
I used both of them and I used both v1 and v2 of protobuf-net, all solutions yielded similar results which led me to believe my client is not deserializing at all. Read on.
Let's consider cases where I used the ProtoPartialMember approach. At first I used v2. I love the way ProtoOperationBehavior can be used. Here is the service operation to be invoked:
[ProtoBuf.ServiceModel.ProtoBehavior]
public List<OsobaView> GetListOsobas()
{
return OsobaQueries.GetListOsobas();
}
Here is how I replaced DataContractSerializerOperationBehavior with ProtoOperationBehavior for the needed service operation on client side:
OperationDescription op = Service.Proxy.Endpoint.Contract.Operations.Find("GetListOsobas");
if (op != null)
{
DataContractSerializerOperationBehavior dcsBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsBehavior != null)
op.Behaviors.Remove(dcsBehavior);
op.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoOperationBehavior(op));
}
And of course, here is the above mentioned work-around implementation for DTO:
[ProtoPartialMember(1, "ID")]
[ProtoPartialMember(2, "Prezime")]
[ProtoPartialMember(3, "Ime")]
[ProtoPartialMember(4, "Adresa")]
[ProtoPartialMember(5, "DatumRodjenja")]
[ProtoPartialMember(6, "JMBG")]
[ProtoContract]
public partial class OsobaView
{
}
Now when I call this service operation from my client, I get null. But Fiddler disagrees. It clearly says, in response header:
Content-Length: 1301963
Content-Type: application/soap+xml; charset=utf-8
...and in the message body:
<s:Body>
<GetListOsobasResponse xmlns="http://tempuri.org/">
<proto>CkMIpHES .../* REALLY LONG RESPONSE */... IyMDAxOA==</proto>
</GetListOsobasResponse>
</s:Body>
Then I thought, let's try with v1. On the service side, I haven't changed much. I just removed the reference to v2 .DLL and replaced it with a reference to v1 .DLL. On the client side, I had to remove the code to add ProtoOperationBehavior to my service operation behaviors and added the following line instead:
Service.Proxy.Endpoint.Behaviors
.Add(new ProtoBuf.ServiceModel.ProtoEndpointBehavior());
I fired it up, invoked the operation, and this time the result is not null. This time it is a list of blank fields. Again, Fiddler couldn't agree because it again said the same what it said before. The same content length and the same message body.
What's going on here?
P.S. If it's worth anything, here is the WCF configuration:
CustomBinding customBinding = new CustomBinding();
customBinding.CloseTimeout = TimeSpan.FromMinutes(10);
customBinding.OpenTimeout = TimeSpan.FromMinutes(10);
customBinding.ReceiveTimeout = TimeSpan.FromMinutes(10);
customBinding.SendTimeout = TimeSpan.FromMinutes(10);
HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
httpsBindingElement.AllowCookies = false;
httpsBindingElement.BypassProxyOnLocal = false;
httpsBindingElement.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
httpsBindingElement.MaxBufferPoolSize = 20480000;
httpsBindingElement.MaxBufferSize = 20480000;
httpsBindingElement.MaxReceivedMessageSize = 20480000;
httpsBindingElement.RequireClientCertificate = true;
httpsBindingElement.UseDefaultWebProxy = true;
TransportSecurityBindingElement transportSecurityElement = new TransportSecurityBindingElement();
transportSecurityElement.EndpointSupportingTokenParameters.SignedEncrypted.Add(new UserNameSecurityTokenParameters());
transportSecurityElement.EndpointSupportingTokenParameters.SetKeyDerivation(false);
TransactionFlowBindingElement transactionFlowElement = new TransactionFlowBindingElement();
TextMessageEncodingBindingElement textMessageEncoding = new TextMessageEncodingBindingElement();
textMessageEncoding.MaxReadPoolSize = 20480000;
textMessageEncoding.MaxWritePoolSize = 20480000;
textMessageEncoding.ReaderQuotas = XmlDictionaryReaderQuotas.Max;
ReliableSessionBindingElement reliableSessionElement = new ReliableSessionBindingElement();
reliableSessionElement.ReliableMessagingVersion = ReliableMessagingVersion.WSReliableMessagingFebruary2005;
customBinding.Elements.Add(transportSecurityElement);
customBinding.Elements.Add(transactionFlowElement);
customBinding.Elements.Add(textMessageEncoding);
customBinding.Elements.Add(reliableSessionElement);
customBinding.Elements.Add(httpsBindingElement);
EndpointAddress endpoint = new EndpointAddress(new Uri(ServiceAddress));
Service.Proxy = new BazaService.BazaClient(customBinding, endpoint);
Service.Proxy.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, CertificateSubject);
CustomBehavior behavior = Service.Proxy.Endpoint.Behaviors.Find<CustomBehavior>();
if (behavior == null)
{
Service.Proxy.Endpoint.Behaviors.Add(new CustomBehavior()); // message inspector
}
Service.Proxy.Endpoint.Contract.Behaviors.Add(new CyclicReferencesAwareContractBehavior(true));
Service.Proxy.Endpoint.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoEndpointBehavior());
/* code used for protobuf-net v2
OperationDescription op = Service.Proxy.Endpoint.Contract.Operations.Find("GetListOsobas");
if (op != null)
{
DataContractSerializerOperationBehavior dcsBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsBehavior != null)
op.Behaviors.Remove(dcsBehavior);
op.Behaviors.Add(new ProtoBuf.ServiceModel.ProtoOperationBehavior(op));
} */
Service.Proxy.ClientCredentials.UserName.UserName = LogOn.UserName;
Service.Proxy.ClientCredentials.UserName.Password = LogOn.Password;
Service.Proxy.Open();
EDIT
To provide even more information, I have read what's written there but it didn't help. I have deleted the service reference generated by Visual Studio and created my own, sharing the whole service contract, but nothing has changed.
After concentrating a bit better, I decided to restart the solution from scratch. I created one class library for the EDMX with it's POCOs, one for ServiceContract and DataContracts and one for the actual WCF service implementation. Then I shared those two libraries containing ServiceContract and DataContracts, and POCOs with the WCF client and tried again, which yielded the same results as before. After trying some other operations which didn't use protobuf-net for serialization, turned out they behaved the same as the first one, resulting in empty fields (!).
The thing was that, I screwed my WCF client's .datasource files while refactoring after I decided to use the assembly sharing technique. So this was a typical PEBKAC, it of course works fine when done properly. Great work with protobuf-net, Marc Gravell!

System.ServiceModel.ClientBase connected to Service

I have the following code:
public partial class MyServiceClient : System.ServiceModel.ClientBase<...
if (m_MyClient == null)
m_MyClient = new MyServiceClient
("BasicHttpBinding_IMyService", remoteAddress);
WriteOutput("Successfully connected to service");
My question is how do i know that my client actually connected to the service at this point? I would like to display a message of either failure or success.
When you've created the client, and no exception like EndpointNotFoundException has occured - then you are "connected" to the service which really means: the communication channel between the client and the service is ready to be used for sending messages back and forth. That's all there is - there's nothing on the server side yet to really handle your calls (except for the channel listener which will get activated if a message arrives).
You can also check the client channel's .State property - ideally, it should be Opened at that point:
Use this if you're deriving from ClientBase<T>
m_MyClient.State == CommunicationState.Opened
or this is you're using the standard client class generated by the Add Service Reference functionality in Visual Studio:
(m_MyClient as IClientChannel).State == CommunicationState.Opened
After realizing what i mentioned in my above comment, i realized the answer to my question was as follows:
In my ServiceContract i added the following:
[OperationContract]
bool IsAlive();
Whose implentation simply looks as follows:
public bool IsAlive()
{
return true;
}
Then changed my code as follows:
m_MyClient = new MyServiceClient("BasicHttpBinding_IMyService", remoteAddress);
try
{
m_MyClient.IsAlive();
}
catch (EndpointNotFoundException)
{
WriteOutput("Unable to connect to service");
m_MyClient = null;
}
if (m_MyClient != null)
WriteOutput("Successfully connected to service");