2017-08-09 1 views
0

ich einen sehr einfachen WCF-Dienst haben, ich brauche einen benutzerdefinierten Client für, zu schreiben, die CreateChannel außer Kraft setzen muss, aber wenn ich in meinem EndInvokeChannelBase Implementierung nenne ich bekommenIndividuelle WCF Kanal wirft Nullreferenceexception auf ChannelBase.EndInvoke

System.NullReferenceException occurred 
    HResult=0x80004003 
    Message=Object reference not set to an instance of an object. 
    Source=System.ServiceModel 
    StackTrace: 
    at System.ServiceModel.Dispatcher.ProxyOperationRuntime.MapAsyncEndInputs(IMethodCallMessage methodCall, IAsyncResult& result, Object[]& outs) 
    at System.ServiceModel.ClientBase`1.ChannelBase`1.EndInvoke(String methodName, Object[] args, IAsyncResult result) 
    at Client.TestServiceClient.TestServiceChannel.<SayHello>b__1_0(IAsyncResult result) 
    at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously) 

Ich bin mir nicht sicher, was ich falsch gemacht habe und der StackTrace hilft mir auch nicht, Google hat nichts Nützliches ergeben. Meine Lösung verwendet .net 4.6.2 und der Aufruf an den Dienst ist erfolgreich (es druckt auf Konsole), aber EndInvoke löst aus Rahmencode. Jede Hilfe würde sehr geschätzt werden.

Minimal Repro:

namespace Service { 
    using System; 
    using System.ServiceModel; 

    [ServiceContract] 
    public interface ITestService { 
     [OperationContract] 
     void SayHello(); 
    } 

    public class TestService : ITestService { 
     public void SayHello() => Console.WriteLine("Hello"); 
    } 
} 


namespace Host { 
    using System; 
    using System.ServiceModel; 
    using Service; 

    internal static class Program { 
     private static void Main() { 
      var host = new ServiceHost(typeof(TestService)); 
      host.AddServiceEndpoint(typeof(ITestService), new BasicHttpBinding(BasicHttpSecurityMode.None), "http://localhost:13377/"); 
      host.Open(); 
      Console.ReadLine(); 
     } 
    } 
} 

namespace Client { 
    using System; 
    using System.ServiceModel; 
    using System.ServiceModel.Channels; 
    using Service; 

    public class TestServiceClient : ClientBase<ITestService>, ITestService { 
     public TestServiceClient(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) {} 

     public void SayHello() => Channel.SayHello(); 

     protected override ITestService CreateChannel() => new TestServiceChannel(this); 

     private class TestServiceChannel: ChannelBase<ITestService>, ITestService { 
      public TestServiceChannel(ClientBase<ITestService> client) : base(client) {} 

      public void SayHello() => base.BeginInvoke("SayHello", new object[0], result => base.EndInvoke("SayHello", new object[0], result), null); 
     } 
    } 

    internal static class Program { 
     private static void Main() { 
      var client = new TestServiceClient(new BasicHttpBinding(BasicHttpSecurityMode.None), new EndpointAddress("http://localhost:13377/")); 
      client.SayHello(); 
      Console.ReadLine(); 
     } 
    } 
} 

Antwort

0

Der Grund dafür ist, dass BeginInvoke/EndInvoke Paar ruft nur dann, wenn der Dienstvertrag Begin.../End... Methodenpaar hat erlaubt ist. WCF analysiert intern die Vertragsschnittstelle und wenn es die Methode sieht, die wie eine asynchrone aussieht (beginnend mit "Begin", 3 oder mehr Parameter usw.), initialisiert es einige interne Strukturen, die später von EndInvoke (in der MapAsyncEndInputs) verwendet werden.

Wenn Sie die WCF-Infrastruktur für asynchrone Anrufe verwenden möchten, sollte die clientseitige Servicevertragsschnittstelle asynchron sein. Alternativ müssen Sie das gesamte Kanalobjekt umschließen, um oben asynchrone Vorgänge bereitzustellen.

Sie können überprüfen, this answer (Option # 3), wie es für Task-basierte asynchrone Muster getan wurde.

Verwandte Themen