2012-07-23 6 views
0

PRISM 아키텍처의 WPF 응용 프로그램이 있습니다.WPF + PRISM - 별도의 스레드에서 'LoadModule'을 사용할 수 없습니다.

앱이로드 될 때 '주 영역'에 표시된 '로그인보기'가 있습니다.

사용자가 '로그인'을 누르면 WCF 서비스에 연결하고 사용자를 인증하고 서비스에서 해당 사용자의 역할 목록을 가져옵니다.

사용자의 역할에 따라 '모듈 관리자'를 사용하여 다른 모듈을로드합니다.

문제는 - 서비스에 연결하는 데 시간이 걸릴 수 있기 때문에 '로그인'버튼을 눌러 별도의 스레드에서 수행하기를 원하는 모든 작업을 원하지만 UI를 원하지 않습니다. 겨울 왕국.

하지만 - 나는 '연결 인증, 역할,로드 모듈 가져 오기'를 별도의 스레드에서하는 코드를 삽입하는 경우 - 나는 말한다 '_moduleManager.LoadModule'호출 할 때 예외를 얻을 :

호출 스레드는 STA 여야합니다. 왜냐하면 많은 UI 구성 요소에서이를 필요로하기 때문입니다.

어떻게 해결할 수 있습니까?

다른 해결책을 시도했습니다. 새 스레드의 'Apartment State = STA'를 설정하려고했지만 도움이되지 않았습니다. 'Dispatcher'객체를 View-Model의 생성자에 저장하고 'LoadModule'을 호출 할 때 'dispatcher.Invoke'를 수행하는 것이 좋겠지 만 이는 잘못된 디자인입니다 (View-Model은 Dispatcher를 사용해서는 안되며 또한 그것은 시험을 위해 나쁘다).

어떻게하면 해결할 수 있을까요?

'LoadModule'만 나에게 슬픔을 안겨줍니다. 다른 모든 것들은 잘 작동합니다.

.

[업데이트] - 추가 된 코드 샘플 :

[Export] 
public class LoginViewModel : NotificationObject 
{ 
    [ImportingConstructor] 
    public LoginViewModel(IRegionManager regionManager, IModuleManager moduleManager) 
    { 
     this.LoginCommand = new DelegateCommand(LoginExecute, LoginCanExecute); 
     this._regionManager = regionManager; 
     this._moduleManager = moduleManager; 
    } 

    private void LoginExecute() 
    { 
     IsBusy = true; // Set this to 'true' so controls go disabled 
     LoginStatus = ""; // Clear the 'login status' string 


     Thread loginThread = new Thread(new ThreadStart(LoginWork)); 
     loginThread.SetApartmentState(ApartmentState.STA); 
     loginThread.Start(); 
    } 

    private void LoginWork() 
    { 
     ParamsToGetRoles param = new ParamsToGetRoles 
     { 
      Username = Username, 
      InputtedPassword = Password 
     }; 

     try 
     { 
      // Connect to the secure service, and request the user's roles 
      _clientSecure = new AuthenticationServiceClient("WSHttpBinding_MyService"); 
      _clientSecure.ClientCredentials.UserName.UserName = param.Username; 
      _clientSecure.ClientCredentials.UserName.Password = param.InputtedPassword; 
      _clientSecure.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted); 
      var local = _clientSecure.ChannelFactory.CreateChannel(); 
      _clientSecure.GetRolesCompleted += new EventHandler<GetRolesCompletedEventArgs>(clientSecure_GetRolesCompleted); 
      _clientSecure.GetRolesAsync(param); 
     } 
     catch (Exception ex) 
     { 
     Console.WriteLine("Exception : " + ex.Message.ToString()); 
     } 
    } 

    void clientSecure_GetRolesCompleted(object sender, GetRolesCompletedEventArgs e) 
    { 
     if (e.Error == null) 
     { 
      _clientSecure.Close(); 
      LoginSuccess(e.Result.UserRoles); 
     } 
     else 
     { 
      LoginFailure("Unable to authenticate"); 
     } 
     _clientSecure = null; 
    } 

    private void LoginSuccess(List<UserTypeEnum> rolesOfAuthenticatedUser) 
    { 
     LoginStatus = "Success"; 

     if (rolesOfAuthenticatedUser.Contains(UserTypeEnum.Administrator)) 
     { 
      // This is what throws the exception ! 
      // This is called by the 'EndInvoke' of the 'GetRoles' operation, 
      // Which was called in the 'LoginWork' function which was run on a separate thread ! 
      _moduleManager.LoadModule(WellKnownModuleNames.ModuleAdmin); 
     } 

     NavigateToMainMenu(); 

     this.IsBusy = false; 
    } 
} 
+1

올바른 스레드 상태를 STA에 넣으시겠습니까? 또한, http://stackoverflow.com/questions/2329978/the-calling-thread-must-be-sta-because-many-ui-components-require-this 그리고 다른 많은 사람들은 정확히 같은 것을 묻습니다. – stijn

+1

그냥 사용하고 싶습니다. 메인 UI 스레드에 LoadModules 명령을 보내는 Dispatcher :) – Rachel

+0

@Rachel - 테스트 목적으로 'Dispatcher'를 사용하지 않는가? 또한 'View-Model'은 UI에 대해 아무것도 모르는 것이고, 'Dispatcher'를 사용하여 UI 스레드에서 작업을 수행하면 목적이 조금 낫습니다. –

답변

2

당신은 디버거를 연결하고 clientSecure_GetRolesCompleted 설정 중단 점과 스레드 창을 검사해야한다. 나는 그것이 loginThread에서 호출되지 않는다고 확신한다. LoginWork이 loginThread에서 실행되는 동안, 비동기 작업의 완료 이벤트에 eventhandler를 추가한다. Async =는 또 다른 스레드에서 실행됩니다.

은 그래서 아마 발생합니다

  • LoginExecute는 UI 스레드에서 실행

  • 그렇게 (하지 STA 인) 스레드 C를 시작 LoginWork
  • 전화 GetRolesAsync을 실행하는 별도의 스레드 B를 시작합니다 역할을 얻으려면
  • 스레드 C는 결국 스레드 B가 아니라 'clientSecure_GetRolesCompleted'를 호출합니다.

LoginWork에 별도의 스레드가 필요하지 않으므로 실제 작업이 이미 비동기 작업으로 수행 되었기 때문에 가능합니다.로드 문제를 해결하려면 get role 스레드를 STA 이상으로 만들거나 디스패처를 사용하여 UI 스레드에서 LoginSuccess이 호출되도록하십시오.

+0

그것을 넣는 훌륭한 방법. Dispatcher가 작동합니다. WCF 서비스에 연결하는 데 1 ~ 2 초가 걸릴 수 있기 때문에 'LoginWork'에 대한 별도의 스레드가 필요합니다. UI 스레드가 멈추길 원하지 않습니다. 그래서 'LoginWork'를 다른 스레드. –

+0

그 말은 어쨌든 UI 스레드에서 수행되지 않았기 때문에 이제 'ASYNC'호출 모드를 사용하는 것이 바보 같은 것처럼 보입니다. 그래서 그냥 SYNC 작업을 호출 할 수도 있고 디스패처를 통해 'LoadModule'을 마샬링 할 때 ... –

+0

그 경우에는 가장 좋은 옵션입니다. – stijn