我在 PRISM 架构中有一个 WPF 应用程序。
当应用程序加载时,我有一个显示在“主要区域”中的“登录视图”。
当用户按下“登录”时 - 我连接到 WCF 服务,对用户进行身份验证,并从服务中获取该用户的角色列表。
然后 - 根据用户的角色 - 我使用“模块管理器”加载不同的模块。
问题是 - 我希望按下“登录”按钮后的所有工作都在单独的线程中完成,因为连接到服务等可能需要时间,而且我不希望 UI 被冻结。
但是 - 如果我将代码“连接、验证、获取角色、加载模块”放在一个单独的线程中 - 当我调用“_moduleManager.LoadModule”时会出现异常:
调用线程必须是 STA,因为许多 UI 组件都需要这个。
我该如何解决这个问题?
我尝试了不同的解决方案。我试图设置新线程的“Apartment State = STA”,但没有帮助。我想过在 View-Model 的构造函数中保存“Dispatcher”对象,然后在调用“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;
}
}