Pass interface methods as parameters


Alex

Note : This is most likely a very C#specific language issue that has nothing to do with it WCFor web servicesnot at all.

There is a 3rd party ASMXweb service which should be used for data retrieval. I created a generic method called ExecuteCommand(), which is used for every request made to the web service. The purpose of this method is to handle cookie sessions/exceptions and other common logic. For each request, a new channel should be used to simplify disposal of unused resources.

The problem is with that ExecuteCommand()method - I have to initialize a channel each time to be able to pass the method to be executed as a parameter. Sorry it sounds too complicated. Here is a usage example:

string color = "blue";
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
var cars = WcfHelper.ExecuteCommand(channel, () => channel.GetCars(color));
// channel is null here. Channel was closed/aborted, depending on Exception type.

After that ExecuteCommand()is called - channelhas been disposed of. The reason an channelobject is needed at all is to be able to provide a method that can be executed as a parameter! ie () => channel.GetCars(). To further support these words, here is WcfHelperthe inside of the class:

public static class WcfHelper
{
    public static Cookie Cookie { get; set; }

    public static T ExecuteCommand<T>(IClientChannel channel, Expression<Func<T>> method)
    {
        T result = default(T);

        try
        {
            // init operation context
            using (new OperationContextScope(channel))
            {
                // set the session cookie to header
                if (Cookie != null) {
                    HttpRequestMessageProperty request = new HttpRequestMessageProperty();
                    request.Headers["Cookie"] = cookie.ToString();
                    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = request;  
                }

                // execute method
                var compiledMethod = method.Compile();
                result = compiledMethod.Invoke();
            }
        }
        // do different logic for FaultException, CommunicationException, TimeoutException
        catch (Exception)
        {
            throw;
        }
        finally
        {
            CloseOrAbortServiceChannel(channel);
            channel = null;
        }

        return result;
    }

    private static void CloseOrAbortServiceChannel(ICommunicationObject communicationObject)
    {
        bool isClosed = false;

        if (communicationObject == null || communicationObject.State == CommunicationState.Closed)
            return;

        try
        {
            if (communicationObject.State != CommunicationState.Faulted)
            {
                communicationObject.Close();
                isClosed = true;
            }
        }
        catch (Exception)
        {
            throw;
        }
        finally
        {
            if (!isClosed)
                AbortServiceChannel(communicationObject);
        }
    }

    private static void AbortServiceChannel(ICommunicationObject communicationObject)
    {
        try
        {
            communicationObject.Abort();
        }
        catch (Exception)
        {
            throw;
        }
    }
}

So, short question - is it possible to initialize variables inside channelthe ExecuteCommandmethod itself , while at the same time it is possible to define which method should be executed inside ExecuteCommandfor a given channel ?

I am trying to accomplish something like this:

string color = "blue";
var cars = WcfHelper.ExecuteCommand<Car[], CarServiceSoapChannel>(channel => channel.GetCars(color));

even

string color = "blue";
var cars = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.GetCars(color));

Of course, any other code improvement suggestions are welcome, but not mandatory.

PS is added ASMXas Service referencein Visual Studio. So there are some entities that are auto-generated for "CarService" like - CarServiceSoapChannelinterfaces, CarServiceSoapClientclasses, and of course interfaces that CarServicecontain web service methods. In the above example, a ChannelFactoryis used to create a channel for the CarServiceSoapChannelinterface , so here the question name comes from: Passing an interface method as a parameter. This can be misleading, but I hope it's pretty clear what's being accomplished from the description itself.

Update 25.05.2018 I followed @nvoigt 's suggestion and was able to achieve my desired result:

public static TResult ExecuteCommand<TInterface, TResult>(Func<TInterface, TResult> method)
    where TInterface : IClientChannel
{
    TResult result = default(TResult);
    IClientChannel channel = null;

    try
    {
        channel = StrategyFactory.CreateChannel<TInterface>();

        // init operation context
        using (new OperationContextScope(channel))
        {
            // set the session cookie to header
            if (Cookie != null)
                Cookie.SetCookieForSession();

            // execute method
            result = method((TInterface)channel);
        }
    }
    catch (Exception)
    {
        throw;
    }
    finally
    {
        CloseOrAbortServiceChannel(channel);
        channel = null;
    }

    return result;
}

This has achieved the original goal. However, there is a problem with this approach. In order to call the method - you have to explicitly specify the return parameter of the method.

If you're going to call the method, so to speak - it's not enough to write code:

var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.IsBlue())

You will have to specify the return type asboolean

var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel, bool>(channel => channel.IsBlue())

I personally don't mind writing extra code as it still has a big advantage over my initial method implementation, however, I'm wondering if the new version can improve this?

For example, when I have only 1 generic type TResultin the method - the return type <TResult>can be omitted. This is no longer the case with 2 generics. Anyway, thanks @nvoigt !

urge

Your method signature should be:

public static TResult ExecuteCommand<TInterface>(Func<TInterface, TResult> method)

Then, in WcfHelper (probably no longer should because staticit requires a member _strategyFactory), you create a channel as before:

{
    var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();

    return method(channel);
}

Obviously you need to add all the fancy try/finally stuff again.


Since you should now have the factory in your class as an instance of the member, you can put a generic service contract into your class to make it easier for the user:

public class ConnectionToService<TInterface> : where TInterface : class
{
    public TResult ExecuteCommand<TResult>(Func<TInterface, TResult> method)
    {
        var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();

        return method(channel);
    }
}

usage:

var service = new ConnectionToService<ICarService>();

var color = service.ExecuteCommand(s => s.GetColor());

Related


Implementing interface methods with variadic parameters

Short oil protein: I start with a simple interface: type Module interface { Init(deps ...interface{}) error } I think, the implementation will be very simple as this method should match any number of provided arguments. Here is the code I finally came up

Pass parameters as interface

Wreck Fish: In Go, what happens when an argument is passed to a function with an interface? Specifically, is it pass-by-reference or pass-by-value? Burak Serdar: If an interface value is passed to a function that takes an interface, it can be passed without an

Interface type parameters in restricted methods

Hugo Sereno Ferreira What I'm trying to convey is that a method only makes sense if the type parameters of its interface obey certain constraints. Here is a (non-compiled) example: interface Array<T> { fold<B>(base: B, f: (acc: B, e: T) => B): B sum():

Pass parameters to methods

User 9177029 I have js code and i pass data to repo method via parameter. Here is the repo method: public List<SpeedLimitViewModel> GetSpeedData(decimal imei, DateTime start, DateTime end) { using (TraxgoDB ctx = new TraxgoDB()) {

Pass interface methods as parameters

Alex Note : This is most likely a very C#specific language issue that has nothing to do with it WCFor web servicesnot at all. There is a 3rd party ASMXweb service which should be used for data retrieval. I created a generic method called ExecuteCommand(), whic

Pass additional parameters to methods

Izzy I am using Devexpressfile upload control. I use the following overloadin my UploadControlExtensionclass : public static UploadedFile[] GetUploadedFiles(string name, UploadControlValidationSettings validationSettings, EventHandler<FileUploadCompleteEventAr

Pass parameters as interface

Wreck Fish: In Go, what happens when an argument is passed to a function with an interface? Specifically, is it pass-by-reference or pass-by-value? Burak Serdar: If an interface value is passed to a function that takes an interface, it can be passed without an

Pass optional parameters to methods

Hartman I'm trying to pass optional parameters to a method and am having a hard time understanding why it doesn't work. As the most popular answer here ( How to use optional parameters in C#? ) says public void SomeMethod(int a, int b = 0). So I tried the same

Pass optional parameters to methods

Hartman I'm trying to pass optional parameters to a method and am having a hard time understanding why it doesn't work. As the most popular answer here ( How to use optional parameters in C#? ) says public void SomeMethod(int a, int b = 0). So I tried the same

Pass parameters to methods

User 9177029 I have js code and i pass data to repo method via parameter. Here is the repo method: public List<SpeedLimitViewModel> GetSpeedData(decimal imei, DateTime start, DateTime end) { using (TraxgoDB ctx = new TraxgoDB()) {

Pass interface methods as parameters

Alex Note : This is most likely a very C#specific language issue that has nothing to do with it WCFor web servicesnot at all. There is a 3rd party ASMXweb service which should be used for data retrieval. I created a generic method called ExecuteCommand(), whic

Pass interface methods as parameters

Alex Note : This is most likely a very C#specific language issue that has nothing to do with it WCFor web servicesnot at all. There is a 3rd party ASMXweb service which should be used for data retrieval. I created a generic method called ExecuteCommand(), whic

Java - Given interface with methods - how to pass parameters?

CMA I've tried to make the code as generic as possible , this just represents the basic setup. I'm a Java beginner trying to understand interfaces, classes and methods. I did change the interface and class names to make it easier to reference them. I am fully

Java - Given interface with methods - how to pass parameters?

CMA I've tried to make the code as generic as possible , this just represents the basic setup. I'm a Java beginner trying to understand interfaces, classes and methods. I did change the interface and class names to make it easier to reference them. I am fully

Pass optional parameters to methods

Hartman I'm trying to pass optional parameters to a method and am having a hard time understanding why it doesn't work. As the most popular answer here ( How to use optional parameters in C#? ) says public void SomeMethod(int a, int b = 0). So I tried the same

Pass parameters to Java methods

George Georgiev I need to pass parameters to java method func(String, String...). The problem is that I need to pass all the strings as parameters, but they are in the ArrayListcontainer. E.g: for(...some condition...) { //the other method returns an Array

Pass parameters to Java methods

George Georgeville I need to pass parameters to java method func(String, String...). The problem is that I need to pass all the strings as parameters, but they are in the ArrayListcontainer. E.g: for(...some condition...) { //the other method returns an Ar

Pass parameters to Java methods

George Georgiev I need to pass parameters to java method func(String, String...). The problem is that I need to pass all the strings as parameters, but they are in the ArrayListcontainer. E.g: for(...some condition...) { //the other method returns an Array

Pass parameters to methods

Sunny I have a method with gcdtwo parameters . When I do: gcd (20,40) i get this error syntax error, unexpected ',', expecting ')' Removing the parentheses fixed the problem, but is there any way to use parentheses and still get this code to work? Marek Lipk

Pass parameters as interface

Wreck Fish: In Go, what happens when an argument is passed to a function with an interface? Specifically, is it pass-by-reference or pass-by-value? Burak Serdar: If an interface value is passed to a function that takes an interface, it can be passed without an

Pass optional parameters to methods

Hartman I'm trying to pass optional parameters to a method and am having a hard time understanding why it doesn't work. As the most popular answer here ( How to use optional parameters in C#? ) says public void SomeMethod(int a, int b = 0). So I tried the same

Pass optional parameters to methods

Hartman I'm trying to pass optional parameters to a method and am having a hard time understanding why it doesn't work. As the most popular answer here ( How to use optional parameters in C#? ) says public void SomeMethod(int a, int b = 0). So I tried the same

Pass interface methods as parameters

Alex Note : This is most likely a very C#specific language issue that has nothing to do with it WCFor web servicesnot at all. There is a 3rd party ASMXweb service which should be used for data retrieval. I created a generic method called ExecuteCommand(), whic

Pass interface methods as parameters

Alex Note : This is most likely a very C#specific language issue that has nothing to do with it WCFor web servicesnot at all. There is a 3rd party ASMXweb service which should be used for data retrieval. I created a generic method called ExecuteCommand(), whic

Pass optional parameters to methods

Hartman I'm trying to pass optional parameters to a method and am having a hard time understanding why it doesn't work. As the most popular answer here ( How to use optional parameters in C#? ) says public void SomeMethod(int a, int b = 0). So I tried the same

Pass optional parameters to methods

Hartman I'm trying to pass optional parameters to a method and am having a hard time understanding why it doesn't work. As the most popular answer here ( How to use optional parameters in C#? ) says public void SomeMethod(int a, int b = 0). So I tried the same

Pass parameters to Java methods

George Georgiev I need to pass parameters to java method func(String, String...). The problem is that I need to pass all the strings as parameters, but they are in the ArrayListcontainer. E.g: for(...some condition...) { //the other method returns an Array

Pass parameters to methods

Sunny I have a method with gcdtwo parameters . When I do: gcd (20,40) i get this error syntax error, unexpected ',', expecting ')' Removing the parentheses fixed the problem, but is there any way to use parentheses and still get this code to work? Marek Lipk

Pass parameters to methods

National Service I'm using some functions in my Articlemodel that add likes and record time to a cookie for a specific article public static function hasLikedToday($articleId, string $type) { $articleLikesJson = \Cookie::get('article_likes', '{}');