I'm currently segregating the interface definition of a WCF web service and sorting out the client to depend on these interfaces rather than the generated service client class.
The pattern that's currently being used reads like this -
var client = new ServiceClient();
client.DoSomethingCompleted += (o,args) =>
{
client.CloseAsync();
//Do Something
}
client.DoSomething();
Nice and simple. As soon as the client returns, close the connection.
By depending on the interface of the proxy you lose out on the generated events and have to use the Async Begin/End pattern. Now it would read -
//client is now an IDoSomethingable
client.BeginDoSomething(new AsyncCallback((result) =>
{
var somethingDone = client.EndDoSomething(result);
}),null);
So my question is does the client get closed when 'EndDoSomething' is called or am I missing something since there doesn't appear to be an explicit way to close it.
Much thanks in advance.
Related
I am experimenting with a gRPC service and client using proto files. The advice is to use gRPC client factory integration in .NET Core (https://learn.microsoft.com/en-us/aspnet/core/grpc/clientfactory?view=aspnetcore-3.1). To do this you register the client derived from Grpc.Core.ClientBase that is generated by the Grpc.Tools package, like this:
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddGrpcClient<MyGrpcClientType>(o =>
{
o.Address = new Uri("https://localhost:5001");
});
})
My understanding is that MyGrpcClientType is registered with DI as a transient client, meaning a new one is created each time it is injected, but that the client is integrated with the HttpClientFactory, allowing the channel to be reused rather than be created each time.
Now, I would like to use protobuf-net.grpc to generate the client from an interface, which appears to be done like this:
GrpcClientFactory.AllowUnencryptedHttp2 = true;
using var http = GrpcChannel.ForAddress("http://localhost:10042");
var calculator = http.CreateGrpcService<ICalculator>();
If I am correct in thinking that channels are expensive to create, but clients are cheap, how do I achieve integration with the HttpClientFactory (and so reuse of the underlying channel) using protobuf-net.grpc? The above appears to create a GrpcChannel each time I want a client, so what is the correct approach to reusing channels?
Similarly, is it possible to register the protobuf-net.grpc generated service class with the below code in ASP.Net Core?
endpoints.MapGrpcService<MyGrpcServiceType>();
(Please correct any misunderstandings in the above)
Note that you don't need the AllowUnencryptedHttp2 - that's just if you aren't using https, but: you seem to be using https.
On the "similarly"; that should already work - the only bit you might be missing is the call to services.AddCodeFirstGrpc() (usually in Startup.cs, via ConfigureServices).
As for the AddGrpcClient; I would have to investigate. That isn't something that I've explored in the integrations so far. It might be a new piece is needed.
The Client Factory support not exists, and works exactly like documented here except you register with the method
services.AddCodeFirstGrpcClient<IMyService>(o =>
{
o.Address = new Uri("...etc...");
});
I have a wrapper around a WCF service APIs
Class APIWrapper
{
private WCFClient _client;
//constructor opens a client connection
public APIWrapper()
{
_client = new WCFClient();
_client.open();
}
public API1()
{
_client.doSomething1();
}
public API2()
{
_client.doSomething2();
}
}
I want to ask:
Q1 will timeout exception occur? if this wrapper class instance exists for too long? (does the WCF connection by default keepalive? without setting that attribute in config)
for example, after a wrapper class is constructed, its API1 or API2 is called after 10mins, which is longer than the timeout value of this WCF connection.
Q2 Do I need explicitly close the connection, if so, should I do it in the destructor of the wrapper class like below?
~APIWrapper
{
if(_client !=null)
try{
_client.close(); }
catch(Exception e){
_client.Abort(); }
}
I am not sure why you wanna do that, but if the WCF is hosted in a IIS7, the WCF will start with or without connections, there is no point keeping a connection alive.
In my experience, those kind of services works best when are stateless (unless you have a really good reason). I strongly suggest opening and closing the connection each time. If you are doing this for performance, there are another ways to avoid closing and opening each time.
Q1: According to MSDN the openTimeout is 1 minute
http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.binding.opentimeout.aspx
Q2: you do not need to close the connection explicitly, but it's a good practice and I strongly recommend to do it. Non closing the connections could lead in a overhead on the WCF.
As I know, WCF do no keep the connection alive. After a predefined time passes (inactivityTimeout="00:10:00"), the connection will throw an exception when you try to call _client.doSomething1() or any other method on the service.
WCF inactivity timeout
To keep connection alive you should call a simple method at predefined intervals, lets say every 1 minute.
However, I agree with Jordi about that you should use wcf services stateless until it is realy necessary.
I'm making a WCF service with netTcpBinding which has a main lobby with multiple chatrooms which the clients can enter. The Lobby class implements ILobby as the service contract.
When a client wishes to enter a room I want to callback the client exposing a new Channel containing the InstanceContext for the room he just entered but after much searching I am doubting that this is possible.
For example on the Service side I might have
class Lobby : ILobby
{
Dictionary<string, Chatroom> rooms;
public void JoinRoom(string roomname)
{
if (rooms[roomname].TryEnter()) {}
}
}
class ChatRoom : IChatRoom
{
public bool TryEnter(string username)
{
ILobbyCallback callback =
OperationContext.Current.GetCallbackChannel<ILobbyCallback>();
// How do I do this next bit?
callback.JoinedRoom(pass some instance context here);
return true;
}
}
On the client side callback method I want
public void JoinedRoom(InstanceContext for the room on the service side)
{
// Create a new WCF proxy using above InstanceContext
// Create a WPF UI for the new room passing the proxy so it can communicate
// with the room class directly without going via the root service
}
Is this possible? What's the best practice for spawning new classes with their own contracts on the service side? Or do I just have to bundle everything into one massive MyService class and handle everything myself?
You cannot pass instance context as parameter to any operation contract. It doesn't make sense because that context has local scope. It is called "instance context" = it is context of current service instance. In duplex scenario both client and server has its own service:
Clients calls server's service through its proxy
Server calls client' service through received callback channel
Server's service instance context has meaning only on the server. It is not clear what you are trying to achieve (except very complex architecture).
If you want to share context on client you can try to pass around the instance context used for the very first proxy you created - I'm not sure if it will work but you can try it
If you want to share service instance context between multiple proxies you must develop your own IInstanceContextProvider and perhaps also your own IInstanceProvider (depending on what you want to achieve), wrap them in behavior and add them to the service. That will put whole complexity of session handling and correct instance releasing under your control (it obviously has its pros and cons).
But is it really needed? When I look at your code I see that one service and one proxy is enough. Also your JoinRoom operation doesn't need to use callback at all, it can be just request response method.
Hi
Maybe this look like ridiculous but this is problem at least for me
I wrote duplex WCF service, in my service I need to get active client service and save them, and when with occurred special event I call specific client and send some values for it. So I define dictionary and save client in that. (With this method client calls)
public static Dictionary<int, IServiceCallbak> ActiveClients;
public void IConnect(int SenderId)
{
if (ActiveClients == null)
ActiveClients = new Dictionary<int, IServiceCallbak>();
Client = OperationContext.Current.GetCallbackChannel<IServiceCallbak>();
if (ActiveClients.Count(ac => ac.Key == SenderId) > 0)
ActiveClients.Remove(SenderId);
ActiveClients.Add(SenderId, Client);
}
So then when I need find client from that dictionary and call specific method : Client.DoSomthing().
Also when Client wants to exit, it calls IDisconnect method which will remove client from dictionary.
so I manage Active-client in service!!!
But there is problem in client for managing themselves
After a period time which define in app.config service connection will be closed and you should renew that and then open the service.
So in this case:
1)Is there any solution for recreate and open the service object automatically in client.
2)Or when in server side when service want call clients, check state of client-service-object from that dictionary, and reopen connection from server-side (Ridiculous-solution)
Edit
I think better solution is to handle Suggestion 1, I don't know how!!!.
So for now the question is: Is way exist to do Suggestion 1 Or not? Previously I describe Suggestion 1 in Comment:
"And automatically refer to event for this case(like closing or aborting), but I don't find anything for doing this in Service-Client"
In order to prevent the server side from closing the connection you could set up a Heartbeat() method in the contract that the client could call periodically. This is not ideal however, for one thing because the underlying socket could drop and this does nothing to remedy that.
As far as your suggestion 1) if on the client side you are inheriting from ClientBase you are somewhat stuck in that no indication of a problem may be given until you call a method to route to the service. You would have to wrap the call in a try / catch and then employ some reconnect logic:
public class MyClass : ClientBase<IContract>, IContract
{
public void ServiceMethod(String data) {
try {
base.Channel.ServiceMethod(data);
}
catch (CommunicationException ce) {
// Perform some reconnect logic here
base.Channel.ServiceMethod(data);
}
}
}
Your comment for suggestion 2) is correct, if there are any firewalls between the server side and client they would most likely not allow the connection
Edit:
To expand on my suggestion for 1), you would need to create a new connection when the call to the service fails with a CommunicationException. The simplest approach would be to create the service channel in the constructor and then create another when the call fails:
class ServiceClient {
Service1Client mService; // Class generated by VS tool
public ServiceClient()
: base() {
mService = new Service1Client();
}
#region IService1 Members
public string GetData(int value) {
CommunicationState state = mService.State;
if (state == CommunicationState.Closed || state == CommunicationState.Faulted) {
mService = new Service1Client();
}
try {
// Note: The state checked above may not be accurate,
// hence the try...catch
return mService.GetData(value);
}
catch (CommunicationException) {
mService = new Service1Client(); // Reconnect logic
return mService.GetData(value); // If it fails again we are out of luck...
}
}
#endregion
}
Edit2:
In WCF the session is handled by the client, if the session between the client and the service is lost, I know of no way to restore that session, either from the client or the service. You are, unfortunately, stuck here.
If the service wants to send via the callback with a broken session, simply put, it can't. Because of the way networks work the service may not know the actual client address. This and various other issues (like firewalls) mean that trying to reestablish a connection to the client from the service just isn't practical. The only approach for the service is to store what data it wanted to send to the client and send it when the service detects that the client has reconnected.
There is no guarantee that the client will know of the underlying socket dropping, until the client tries to send something over the socket, hence the try...catch. Recreating the channel from the client once it becomes aware of a broken connection is the only way I know of to handle the issue; which is what the code example does.
The heartbeat idea is a way to proactively deal with broken connection. Its efficiency depends on your requirements as to how fast you need to detect a broken connection and how many clients are present. The more clients connected the longer the heartbeat would have to be so that you don't put a load on the network at the service.
Edit3:
After some additional digging there may be a way to do what you want automatically. You can create what is known as a Reliable Session. Activating this involves creating additional entries in the config:
<netTcpBinding>
<binding>
<reliableSession ordered="Boolean"
inactivityTimeout="TimeSpan"
enabled="Boolean" />
</binding>
</netTcpBinding>
It is also available for Http related bindings, check out the link to the Microsoft documentation on the feature.
I'm putting together a WCF service using net.tcp and netTcpBinding to get duplex comms with my Silverlight client. I call into the service from the Silverlight app and the service calls out to another server, passing it a callback method in the WCF service class. The remote server calls back several times and each time it does, the WCF service uses the callbackchannel to send the data to the Silverlight client. It all works nicely most of the time.
If the user puts in a big request, I get a TimeoutException after a large number of callbacks have already worked. (Clearly, there's some work to do elsewhere to prevent this but I'd like to robustify the service, first.)
I was expecting to do some kind of 'if (client.ConnectionState == faulted)' check before trying to call back to the Silverlight client but I can't seem to find the object that holds the state of the connection. Is there one? Am I approaching this from the wrong side?
This is my first venture into a service net.tcp and duplex. I just moved house and my WCF bible is still in a box. Somewhere. :-) So, I can't do my usual background reading.
Any pointers would be gratefully received. Here's some bare code in case my description is too soupy:
private IActiveDirectoryClient client;
private AsyncSearchRunner runner;
public void Search(Request request)
{
this.client = OperationContext.Current.GetCallbackChannel<IActiveDirectoryClient>();
runner = new AsyncSearchRunner();
runner.Run(request.SearchRoot, request.SearchFilter, request.PageSize,
System.DirectoryServices.Protocols.SearchScope.Subtree, SendObjects);
}
private void SendObjects(List<DirectoryObject> items)
{
Response response = new Response();
response.DirectoryObjects = items.ToArray();
client.SendResponse(response);
}
Yes, there is a State property that is defined in the ClientBase<> class (all the proxy classes are derived from ClientBase<>).
There are some proxy wrappers out there that handle fault states of the connection and re-establish connections as needed. Google for "wcf proxy wrapper".
You can also home-brew something if you use some kind of ServiceLocator pattern.