21

I get the following exception (Cannot have two operations in the same contract with the same name, methods ExecuteAsync and Execute) when the following service is activated.

    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        byte[] Execute(MyRequest request);

        [OperationContract]
        Task<byte[]> ExecuteAsync(MyRequest request);
    }

I guess this makes sense if you are using the svcutil.exe to create your service reference, because the task-based operations are created automatically for you. However, I don't want to add a service reference and instead just use the standard ChannelFactory to create the WCF Channel. Is there any other way this is possible without renaming the async method to something else? Or must I wrap the sync method on the client in a Task.Run?

Brett Janer
  • 517
  • 1
  • 4
  • 20

6 Answers6

13

The above is not valid because WCF itself makes two methods for each OperationContract in your case Execute() one synchronous and second asynchronous, which can be called on client side by writing ServiceClientObj.ExexuteAsync(request), so you don't need to add async method explicitly in the IMyService.The Framework is itself responsible for generating async method of each Operation

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
7

Here's what I did. I have two separate contracts. One for the client and one for the server:

namespace ServiceLibrary.Server
{
    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        byte[] Execute(MyRequest request);
    }
}

namespace ServiceLibrary.Client
{
    [ServiceContract]
    public interface IMyService : Server.IMyService
    {
        [OperationContract]
        Task<byte[]> ExecuteAsync(MyRequest request);
    }
}

Because both ServiceContracts share the same name, the OperationContracts' Action and ReplyAction are the same for the async and sync methods. The client now has both the sync and async version and the server stays unmodified.

Brett Janer
  • 517
  • 1
  • 4
  • 20
  • 1
    Your answer to your own question is not correct even if it worked for you, check answers of both JeroenMostert and @EhsanSajjad – Muhammad Gouda Apr 25 '15 at 17:29
  • 2
    @Gouda I'm going to have to disagree with you. Even though Jeroen and Ehsan provided workable solutions, they did not meet my requirements. I did not want to waste a thread on the client, I did not want to use svcutil.exe, and I did not want to not to touch the server side leaving it synchronous. If you didn't notice my answer has been linked to 3 other different SO questions. So it looks like I'm not the only one that thinks my answer is not half bad :) – Brett Janer Apr 27 '15 at 22:36
  • +1 for this workaround. I firstly created `IMyServiceAsync : IMyService` in the *same* namespace as `IMyService`, but this cause some runtime error about async method's names conflict. So only new interface with the same name `IMyService` in different namespace worked, like in this answer. – Sam Apr 14 '16 at 13:36
6

You can declare either, but not both. WCF will automatically make either method work in the generated channel proxy -- you can connect an interface that uses an async method to an implementation that uses a sync one, and vice versa.

If you want to share the interface definition across client and server code (which is not unreasonable) and have to settle for one, your best bet is the async one: offering only sync means async clients must waste a thread, while offering only async means callers have the choice to block and waste the thread themselves if they have to. As calling a WCF service involves I/O, you definitely want to give the client the option for async, even if the server has a sync implementation.

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
Jeroen Mostert
  • 27,176
  • 2
  • 52
  • 85
  • `callers have the choice to block and waste the thread themselves` Unfortunately it's not that easy to do sync over async without deadlocking, having exceptions wrapped by `AggregateException`, etc. – Nelson Rothermel Apr 16 '15 at 01:07
4

Try adding the name in OperationMethod to make it more simpler.

[ServiceContract]
public interface IMyService
{
    [OperationContract(Name = "Service1")]
    byte[] Execute(MyRequest request);

    [OperationContract(Name = "Service2")]
    Task<byte[]> ExecuteAsync(MyRequest request);
}
befree2j
  • 361
  • 5
  • 11
3

Async methods are a .NET construct. The Web Services standard does not know about them. You do not need to do anything to enable clients to use async operations because server and client are independent. Pick sync or async independently for client and server.

Since this fact often results in disbelief let me state the following: You can have a sync server with an async client and the other way around.

Community
  • 1
  • 1
usr
  • 168,620
  • 35
  • 240
  • 369
0

I know this is late answer but there are always people looking for a simpler way.

Consider You have two methods with names SearchSaleMansAll and SearchSaleMans. But You want to show them in same address. I did it like this:

[ServiceContract]
public interface IUserService
{
    [OperationContract]
    List<UserSalemanModel> SearchSaleMansAll();
    
    [OperationContract]
    List<UserSalemanModel> SearchSaleMans(string data);
}

    
    public class UserService : IUserService
    {
        [WebInvoke(
            Method = "GET",
            RequestFormat = WebMessageFormat.Json,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "SearchSaleMans")]
        public List<UserSalemanModel> SearchSaleMansAll()
        {
            var saleMane = new UserDL().SearchSaleMans("");
            return saleMane;
        }


        [WebInvoke(
            Method = "POST",
            RequestFormat = WebMessageFormat.Json,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "SearchSaleMans/{data}")]
        public List<UserSalemanModel> SearchSaleMans(string data)
        {
            var saleMane = new UserDL().SearchSaleMans(data);
            return saleMane;
        }
}

You can use WebGet instead of WebInvoke(Method = "GET", .... The point is UriTemplate. Please customise this part with your needs.