C # WCF closing channels and using Func <T> functions
This is the point, I have a WCF service, it works now. Therefore, I begin to work on the client side. And when the application started, an exception occurred: a timeout. So I started reading, there are many examples of how to keep in touch on the network, but I also found that the best way is to create a channel, use it and dispose of it. And honestly, I liked it. So, now, reading about the best way to close a channel, there are two links that can be useful to anyone who needs them:
1. Clear customers, the right way
In the first link, this is an example:
IIdentityService _identitySvc; ... if (_identitySvc != null) { ((IClientChannel)_identitySvc).Close(); ((IDisposable)_identitySvc).Dispose(); _identitySvc = null; } So, if the channel is not null, it closes, deletes, and sets it to null. But I have a little question. In this example, the channel has a .Close () method, but in my case intellisense does not show the Close () method. It exists only in the factory object. Therefore, I believe that I should write this. But in an interface that has contracts or a class that implements it? And what should this method do?
Now, the following link, I have something that I have not tried before. Func<T> . And after reading the goal, it is quite interesting. This creates a funcion that with lambdas creates a channel, uses it, closes it and scatters. This example implements this function as a Using() statement. This is really good, and a great improvement. But I need a little help, to be honest, I can not understand this function, so a little explanation from an expert will be very useful. This is the function:
TReturn UseService<TChannel, TReturn>(Func<TChannel, TReturn> code) { var chanFactory = GetCachedFactory<TChannel>(); TChannel channel = chanFactory.CreateChannel(); bool error = true; try { TReturn result = code(channel); ((IClientChannel)channel).Close(); error = false; return result; } finally { if (error) { ((IClientChannel)channel).Abort(); } } } And here is how to do it:
int a = 1; int b = 2; int sum = UseService((ICalculator calc) => calc.Add(a, b)); Console.WriteLine(sum); Yes, I think, really, really well, I would like to understand how to use it in the project that I have.
And, as always, I hope this can be useful to many people.
Thanks for the advanced.
The UseService method accepts a delegate that uses the channel to send the request. The delegate has a parameter and a return value. You can put a WCF service call in a delegate.
And in UseService, it creates a channel and passes the channel to the delegate, which should be provided by you. At the end of the call, he closes the channel.
The proxy object implements not only your contract - it also implements IClientChannel , which allows you to control the time of the proxy server
The code in the first example is not reliable - it will leak if the channel is already busted (for example, the service has switched to session interaction). As you can see in the second version, in case of an error, it calls Abort on the proxy server, which still clears the client side
You can also do this using the extension method as follows:
enum OnError { Throw, DontThrow } static class ProxyExtensions { public static void CleanUp(this IClientChannel proxy, OnError errorBehavior) { try { proxy.Close(); } catch { proxy.Abort(); if (errorBehavior == OnError.Throw) { throw; } } } } However using this is a little cumbersome
((IClientChannel)proxy).CleanUp(OnError.DontThrow); But you can make it more elegant if you create your own proxy interface that will expand your contract and IClientChannel
interface IPingProxy : IPing, IClientChannel { } To answer the question remaining in the comment in Jason's answer, a simple GetCachedFactory example might look like this. This example searches for the endpoint to create by finding the endpoint in the configuration file with the "Contract" attribute equal to the "File_name" parameter of the service that the factory should create.
ChannelFactory<T> GetCachedFactory<T>() { var endPointName = EndPointNameLookUp<T>(); return new ChannelFactory<T>(endPointName); } // Determines the name of the endpoint the factory will create by finding the endpoint in the config file which is the same as the type of the service the factory is to create string EndPointNameLookUp<T>() { var contractName = LookUpContractName<T>(); foreach (ChannelEndpointElement serviceElement in ConfigFileEndPoints) { if (serviceElement.Contract == contractName) return serviceElement.Name; } return string.Empty; } // Retrieves the list of endpoints in the config file ChannelEndpointElementCollection ConfigFileEndPoints { get { return ServiceModelSectionGroup.GetSectionGroup( ConfigurationManager.OpenExeConfiguration( ConfigurationUserLevel.None)).Client.Endpoints; } } // Retrieves the ConfigurationName of the service being created by the factory string LookUpContractName<T>() { var attributeNamedArguments = typeof (T).GetCustomAttributesData() .Select(x => x.NamedArguments.SingleOrDefault(ConfigurationNameQuery)); var contractName = attributeNamedArguments.Single(ConfigurationNameQuery).TypedValue.Value.ToString(); return contractName; } Func<CustomAttributeNamedArgument, bool> ConfigurationNameQuery { get { return x => x.MemberInfo != null && x.MemberInfo.Name == "ConfigurationName"; } } The best solution, however, is to let the IoC container manage the client creation for you. For example, using autofac , he would like to get the following. First you need to register the service as follows:
var builder = new ContainerBuilder(); builder.Register(c => new ChannelFactory<ICalculator>("WSHttpBinding_ICalculator")) .SingleInstance(); builder.Register(c => c.Resolve<ChannelFactory<ICalculator>>().CreateChannel()) .UseWcfSafeRelease(); container = builder.Build(); Where "WSHttpBinding_ICalculator" is the name of the endpoint in the configuration file. Then you can use the service like this:
using (var lifetime = container.BeginLifetimeScope()) { var calc = lifetime.Resolve<IContentService>(); var sum = calc.Add(a, b); Console.WriteLine(sum); }