4

クイックスタートを実行して Prism v2 を調査しています。そして、次の署名を使用して WCF サービスを作成しました。

namespace HelloWorld.Silverlight.Web
{
[ServiceContract(Namespace = "http://helloworld.org/messaging")]
[AspNetCompatibilityRequirements(RequirementsMode =
                                 AspNetCompatibilityRequirementsMode.Allowed)]
  public class HelloWorldMessageService
  {
    private string message = "Hello from WCF";

    [OperationContract]
    public void UpdateMessage(string message)
    {
      this.message = message;
    }

    [OperationContract]
    public string GetMessage()
    {
      return message;
    }
  }
}

このサービスへのサービス参照を Silverlight プロジェクトに追加すると、インターフェイスとクラスが生成されます。

[System.ServiceModel.ServiceContractAttribute
        (Namespace="http://helloworld.org/messaging",
         ConfigurationName="Web.Services.HelloWorldMessageService")]
public interface HelloWorldMessageService {

  [System.ServiceModel.OperationContractAttribute
          (AsyncPattern=true,
      Action="http://helloworld.org/messaging/HelloWorldMessageService/UpdateMessage", 
ReplyAction="http://helloworld.org/messaging/HelloWorldMessageService/UpdateMessageResponse")]
    System.IAsyncResult BeginUpdateMessage(string message, System.AsyncCallback callback, object asyncState);

    void EndUpdateMessage(System.IAsyncResult result);

    [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="http://helloworld.org/messaging/HelloWorldMessageService/GetMessage", ReplyAction="http://helloworld.org/messaging/HelloWorldMessageService/GetMessageResponse")]
    System.IAsyncResult BeginGetMessage(System.AsyncCallback callback, object asyncState);

    string EndGetMessage(System.IAsyncResult result);
}

public partial class HelloWorldMessageServiceClient : System.ServiceModel.ClientBase<HelloWorld.Core.Web.Services.HelloWorldMessageService>, HelloWorld.Core.Web.Services.HelloWorldMessageService {
{
    // implementation
}

具象クラスの代わりにインターフェイスを渡すことで、アプリケーションを分離しようとしています。しかし、これを行う方法の例を見つけるのに苦労しています。EndGetMessage を呼び出して UI を更新しようとすると、間違ったスレッドで UI を更新するという例外が発生します。バックグラウンド スレッドから UI を更新するにはどうすればよいですか?


私は試しましたが、私は得UnauthorizedAccessException : Invalid cross-thread accessます。

string messageresult = _service.EndGetMessage(result);

Application.Current.RootVisual.Dispatcher.BeginInvoke(() => this.Message = messageresult );

によって例外がスローされApplication.Current.RootVisualます。

4

6 に答える 6

2

これは私が好きなことです...サービスプロキシはインターフェースで生成されます

HelloWorldClient : IHelloWorld

しかし問題はIHelloWorld、メソッドの非同期バージョンが含まれていないことです。したがって、非同期インターフェイスを作成します。

public interface IHelloWorldAsync : IHelloWorld
{
    void HelloWorldAsync(...);
    event System.EventHandler<HelloWorldEventRgs> HelloWorldCompleted;
}

次に、部分的にインターフェイスを実装するようにサービス プロキシに指示できます。

public partial class HelloWorldClient : IHelloWorldAsync {}

HelloWorldClient確かに、これらの非同期メソッドを実装しているため、これは機能します。

次に、IHelloWorldAsyncどこでも使用して、インターフェイスに使用するように指示できUnityContainerます。HelloWorldClientIHelloWorldAsync

于 2009-11-09T18:38:35.183 に答える
1

わかりました、私はこれを一日中いじっています、そして解決策はそれよりも本当にはるかに簡単です。私はもともと、concreateクラスではなく、インターフェース上のメソッドを呼び出したかったのです。プロキシクラスジェネレータによって生成されたインターフェイスには、メソッドとメソッドのみが含まれてBeginXXXおりEndXXX、を呼び出したときに例外が発生していましたEndXXX

さて、読み終えたばかりで、System.Threading.Dispatcherようやく使い方がわかりました。UI要素が行うDispatcherから継承するクラスのメンバーです。DispatcherObjectDispatcherUIスレッドで動作します。ほとんどのWPFアプリケーションでは、UIスレッドは1つだけです。例外はありますが、これを明示的に行う必要があると思います。そうすれば、それを行っているかどうかがわかります。それ以外の場合は、UIスレッドが1つだけです。したがって、UI以外のクラスで使用するために、ディスパッチャへの参照を保存しても安全です。

私の場合、Prismを使用しており、PresenterはUIを更新する必要があります(直接ではありませんが、IPropertyChanged.PropertyChangedイベントを発生させています)。したがってBootstrapper、シェルをに設定すると、次のようなApplication.Current.RootVisual参照も保存されます。Dispatcher

public class Bootstrapper : UnityBootstrapper
{
    protected override IModuleCatalog GetModuleCatalog()
    {
    // setup module catalog
    }

    protected override DependencyObject CreateShell()
    {
        // calling Resolve instead of directly initing allows use of dependency injection
    Shell shell = Container.Resolve<Shell>();

        Application.Current.RootVisual = shell;

        Container.RegisterInstance<Dispatcher>(shell.Dispatcher);

        return shell;
    }
}

次に、私のプレゼンターは(DIを使用して)引数として受け入れるctorを持っているIUnityContainerので、次のことができます。

_service.BeginGetMessage(new AsyncCallback(GetMessageAsyncComplete), null);    

private void GetMessageAsyncComplete(IAsyncResult result)
{
    string output = _service.EndGetMessage(result);
    Dispatcher dispatcher = _container.Resolve<Dispatcher>();
    dispatcher.BeginInvoke(() => this.Message = output);
}

これはすっごく簡単です。以前は理解できませんでした。

于 2009-03-06T23:05:53.197 に答える
0

古い投稿を再訪するだけで、最終的に答えが見つかりました。これは私が最近書いた投稿で、最終的にこれらすべてをどのように処理したかについて詳しく説明しています。

http://www.developmentalmadness.com/archive/2009/11/04/mvvm-with-prism-101-ndash-part-6-commands.aspx

于 2009-12-08T01:08:06.173 に答える
0

インターフェイスの受け渡し (クライアントをインスタンス化した後) は、HelloWorldMessageServiceClient クラスの代わりに HelloWorldMessageService を使用するのと同じくらい簡単です。

UI を更新するには、Dispatcher オブジェクトを使用する必要があります。これにより、UI スレッドのコンテキストで呼び出されるデリゲートを提供できます。詳細については、このブログ投稿を参照してください。

于 2009-03-06T01:12:11.657 に答える
0

これをさらに簡単にすることができます。

プロキシが機能し、コントラクトのコピーが機能しない理由は、WCF が、サービス呼び出しが返されたときに実行中のスレッドでコールバックを作成するのではなく、呼び出し元のスレッドにコールバックを「ポスト」するコードを使用してプロキシを生成するためです。

WCF プロキシがどのように機能するかを理解するための、非常に単純化された、テストされていない部分的な実装は、次のようになります。

{
    var state = new
        {
            CallingThread = SynchronizationContext.Current,
            Callback = yourCallback
            EndYourMethod = // assign delegate
        };

    yourService.BeginYourMethod(yourParams, WcfCallback, state);
}

private void WcfCallback(IAsyncResult asyncResult)
{
    // Read the result object data to get state
    // Call EndYourMethod and block until the finished
    state.Context.Post(state.YourCallback, endYourMethodResultValue);
}

重要なのは、syncronizationContext の格納と Post メソッドの呼び出しです。これにより、Begin が呼び出されたのと同じスレッドでコールバックが発生します。UI スレッドから Begin を呼び出すと、Dispatcher オブジェクトを使用しなくても常に機能します。そうしないと、Dispatcher を使用して振り出しに戻りますが、WCF プロキシでも同じ問題が発生します。

このリンクは、これを手動で行う方法をうまく説明しています:
http://msdn.microsoft.com/en-us/library/dd744834(VS.95).aspx

于 2009-11-03T18:51:49.407 に答える