2009-03-06 3 views
4

빠른 시작을 통해 Prism v2를 연구 중입니다.서비스 참조에서 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 
} 

난 : 내 실버 라이트 프로젝트에서이 서비스에 대한 서비스 참조를 추가 할 때 그 인터페이스와 클래스를 생성

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; 
    } 
    } 
} 

: 그리고 나는 다음과 같은 서명 WCF 서비스를 만들었습니다 구체적인 클래스 대신 인터페이스를 통과하여 애플리케이션을 분리하려고합니다. 하지만이 작업을 수행하는 방법의 예를 찾는 데 어려움이 있습니다. 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에 의해 발생합니다.

답변

0

HelloWorldMessageServiceClient 클래스 대신 HelloWorldMessageService를 사용하는 것만 큼 (클라이언트를 인스턴스화 한 후에) 인터페이스를 전달해야합니다.

UI를 업데이트하려면 Dispatcher 객체를 사용해야합니다. 이를 통해 UI 스레드의 컨텍스트에서 호출되는 대리자를 제공 할 수 있습니다. 자세한 내용은 blog post을 참조하십시오.

+0

내가 시도하지만 UnauthorizedAccessException 얻을 : 여기에 최근 그게 내가 마지막으로 모든 것을 처리하는 방법에 대한 내용이수록되어 쓴 글입니다 잘못된 크로스 스레드 액세스. 문자열 messageresult = _service.EndGetMessage (result); Application.Current.RootVisual.Dispatcher.BeginInvoke (() => this.Message = messageresult ); 예외는 Application.Current.RootVisual에 의해 throw됩니다. –

1

내 진짜 문제는 내 서비스 참조로 만든 프록시 클래스에 대한 의존성을 분리하는 방법이었습니다. 프록시 클래스와 함께 생성 된 인터페이스를 사용하여이 작업을 수행하려고했습니다. 어느 것이 잘 작동했을지 모르지만 서비스 레퍼런스를 소유 한 프로젝트를 참조해야만했을 것입니다. 그래서 진정으로 분리되지는 않을 것입니다. 그래서 여기에 내가 끝내주는 것이 있습니다. 그것은 약간의 해킹이지만, 지금까지는 작동하는 것 같습니다.

우선 여기 내 인터페이스 정의 내 프록시 생성 된 사용자 정의 이벤트 핸들러 인수에 대한 어댑터 클래스입니다 :

using System.ComponentModel; 

namespace HelloWorld.Interfaces.Services 
{ 
    public class GetMessageCompletedEventArgsAdapter : System.ComponentModel.AsyncCompletedEventArgs 
    { 
     private object[] results; 

     public GetMessageCompletedEventArgsAdapter(object[] results, System.Exception exception, bool cancelled, object userState) : 
      base(exception, cancelled, userState) 
     { 
      this.results = results; 
     } 

     public string Result 
     { 
      get 
      { 
       base.RaiseExceptionIfNecessary(); 
       return ((string)(this.results[0])); 
      } 
     } 
    } 

    /// <summary> 
    /// Create a partial class file for the service reference (reference.cs) that assigns 
    /// this interface to the class - then you can use this reference instead of the 
    /// one that isn't working 
    /// </summary> 

    public interface IMessageServiceClient 
    { 
     event System.EventHandler<GetMessageCompletedEventArgsAdapter> GetMessageCompleted; 
     event System.EventHandler<AsyncCompletedEventArgs> UpdateMessageCompleted; 

     void GetMessageAsync(); 
     void GetMessageAsync(object userState); 

     void UpdateMessageAsync(string message); 
     void UpdateMessageAsync(string message, object userState); 
    } 
} 

그럼 난 그냥 서비스 참조에 의해 생성 된 프록시 클래스를 확장하는 부분 클래스를 만드는 데 필요한 :

using System; 

using HelloWorld.Interfaces.Services; 
using System.Collections.Generic; 

namespace HelloWorld.Core.Web.Services 
{ 
    public partial class HelloWorldMessageServiceClient : IMessageServiceClient 
    { 

     #region IMessageServiceClient Members 

     private event EventHandler<GetMessageCompletedEventArgsAdapter> handler; 
     private Dictionary<EventHandler<GetMessageCompletedEventArgsAdapter>, EventHandler<GetMessageCompletedEventArgs>> handlerDictionary 
      = new Dictionary<EventHandler<GetMessageCompletedEventArgsAdapter>, EventHandler<GetMessageCompletedEventArgs>>(); 

     /// <remarks> 
     /// This is an adapter event which allows us to apply the IMessageServiceClient 
     /// interface to our MessageServiceClient. This way we can decouple our modules 
     /// from the implementation 
     /// </remarks> 
     event EventHandler<GetMessageCompletedEventArgsAdapter> IMessageServiceClient.GetMessageCompleted 
     { 
      add 
      { 
       handler += value; 
       EventHandler<GetMessageCompletedEventArgs> linkedhandler = new EventHandler<GetMessageCompletedEventArgs>(HelloWorldMessageServiceClient_GetMessageCompleted); 
       this.GetMessageCompleted += linkedhandler; 
       handlerDictionary.Add(value, linkedhandler); 
      } 
      remove 
      { 
       handler -= value; 
       EventHandler<GetMessageCompletedEventArgs> linkedhandler = handlerDictionary[value]; 
       this.GetMessageCompleted -= linkedhandler; 
       handlerDictionary.Remove(value); 
      } 
     } 

     void HelloWorldMessageServiceClient_GetMessageCompleted(object sender, GetMessageCompletedEventArgs e) 
     { 
      if (this.handler == null) 
       return; 

      this.handler(sender, new GetMessageCompletedEventArgsAdapter(new object[] { e.Result }, e.Error, e.Cancelled, e.UserState)); 
     } 

     #endregion 
    } 
} 

이것은 이벤트 처리기를 명시 적으로 구현하므로 이벤트를 연결할 수 있습니다. 사용자가 어댑터 이벤트를 등록 할 때 실제 이벤트가 발생하면 등록합니다. 이벤트가 발생하면 어댑터 이벤트가 발생합니다. 지금까지이 "Works On My Machine".

1

좋아, 나는 하루 종일이 장난 꾸러웠다. 그리고 그 해결책은 그보다 훨씬 간단하다. 나는 원래 concreate 클래스 대신 인터페이스에서 메소드를 호출하기를 원했습니다. 프록시 클래스 생성기에 의해 생성 된 인터페이스는 BeginXXXEndXXX 메서드를 포함하며 EndXXX을 호출하면 예외가 발생했습니다.

음, 방금 System.Threading.Dispatcher에서 읽고 끝내고 마침내 그것을 사용하는 방법을 이해합니다. Dispatcher은 UI 요소가 수행하는 DispatcherObject에서 상속하는 모든 클래스의 멤버입니다. Dispatcher은 UI 스레드에서 작동합니다. 대부분의 WPF 응용 프로그램에는 UI 스레드가 하나뿐입니다.예외가 있지만이 작업을 명시 적으로 수행해야한다는 것을 알고 있으므로 수행 중인지 알 수 있습니다. 그렇지 않으면 하나의 UI 스레드 만 있습니다. 따라서 비 UI 클래스에서 사용하기 위해 Dispatcher에 대한 참조를 저장하는 것이 안전합니다.

필자의 경우 필자는 프리즘을 사용하고 있으며 발표자는 UI를 업데이트해야하지만 직접 IPropertyChanged.PropertyChanged 이벤트가 발생합니다. 나는이 같은 Dispatcher에 대한 참조를 저장 Application.Current.RootVisual 셸을 설정할 때 그래서 나는 짓을하는 것은 내 Bootstrapper에 있습니다

그럼 내 발표자 인수로 IUnityContainer을 받아들이는 ctor에있다
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를 사용) 다음을 수행 할 수 있습니다.

_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); 
} 

이것은 훨씬 간단한 sooooo입니다. 나는 그걸 전에 이해하지 못했습니다.

0

훨씬 간단하게 만들 수 있습니다.

WCF는 서비스가 실행될 때 실행중인 스레드에서 콜백을 작성하는 대신 호출 스레드에서 콜백을 "게시"하는 코드로 프록시를 생성하기 때문에 프록시가 작동하고 계약서 복사본이 생성되지 않기 때문입니다 호출이 반환됩니다. 키가 syncronizationContext의 저장과 Post 메소드를 호출

{ 
    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); 
} 

:

은 훨씬 간소화, 검증되지 않은 부분의 구현은 당신에게 WCF 프록시 작업 같은 것을 보이는 방법에 대한 아이디어를 제공합니다. 그러면 Begin이 호출 된 것과 같은 스레드에서 콜백이 발생합니다. UI 스레드에서 Begin을 호출하면 Dispatcher 객체를 사용하지 않고도 항상 작동합니다. 그렇지 않으면 Dispatcher를 사용하여 사각형으로 돌아 오지만 WCF 프록시에서도 동일한 문제가 발생합니다.

이 링크를 수동으로이 작업을 수행하는 방법을 설명하는 좋은 일을 :
여기 http://msdn.microsoft.com/en-us/library/dd744834(VS.95).aspx

2

는 서비스 프록시가 인터페이스를 생성 ... 내가하고 같은 것입니다

HelloWorldClient : IHelloWorld 

그러나 문제는 IHelloWorld에 메서드의 비동기 버전이 포함되어 있지 않다는 것입니다. 그래서, 비동기 인터페이스를 만들 : 그럼

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

을, 당신은 부분을 통해 인터페이스를 구현하는 서비스 프록시를 알 수 있습니다 :

public partial class HelloWorldClient : IHelloWorldAsync {} 

HelloWorldClient은, 참으로, 그 비동기 메소드를 구현 않기 때문에,이 공장.

그럼 어디서나 IHelloWorldAsync을 사용하고 UnityContainerIHelloWorldAsync 인터페이스에 HelloWorldClient을 사용할 수 있습니다.

+0

모의 클래스의 HelloWorldAsync 메서드가 null이 아닌 경우 HelloWorldCompleted 메서드를 호출 할 수 있기 때문에 저는 이것을 좋아합니다. HelloWorldCompleted (null, 새로운 HelloWorldEventArgs ("hello world", null, false, null)). 나는 이것을 Silverlight 단위 테스트에서 시도해 보았습니다. 비동기 유닛 테스트를 사용하면 RaisePropertyChanged 알림을 보내지 않아도됩니다. 왜 SL/WCF가 이벤트 기반 메소드를 서비스 인터페이스에 자동으로 포함시키지 않는지 궁금 할 것입니다. 너무 많은 코드를 저장합니다! –

관련 문제