Tom Van Schaijk Tom Van Schaijk - 1 month ago 19
C# Question

Set CurrentPrincipal in an asynchronous login in WPF

I've been searching the web for this, and couldn't really find a solution that actually worked. Situation is as follows: I've got a WPF application, where I want to present the user with a simple logon form. Trying to work MVVM, so I've got a LoginViewModel with the following code behind the login command:

try
{
WithClient(servfact.GetServiceClient<IAccountService>(), proxy =>
{
principal = proxy.AuthenticateUser(Login, password);
});
Thread.CurrentPrincipal = principal;
}
catch(...) { ... }


"WithClient" is a short method in my viewmodel baseclass, which I use to instantiate and dispose of my service proxies:

protected void WithClient<T>(T proxy, Action<T> codeToExecute)
{
try { codeToExecute(proxy); }
finally
{
IDisposable toDispose = (proxy as IDisposable);
if(toDispose != null) { toDispose.Dispose(); }
}
}


Now, most of my services are Async, and I've got an async variant of WithClient going on, which also works fine:

protected async Task WithClientAsync<T>(T proxy, Func<T, Task> codeToExecute)
{
try { await codeToExecute(proxy); }
finally
{
IDisposable toDispose = (proxy as IDisposable);
if(toDispose != null) { toDispose.Dispose(); }
}
}


The trouble begins whenever I also want to do the login asynchronously. Obviously I don't want the UI to freeze up as I do the login (or visit any WCF service for that matter). That in itself is working fine, but the problem sits in the piece of code where I set the CurrentPrincipal. This problem is probably familiar to most of you: it seems to set it just fine. Then in my program I want to use the CurrentPrincipal (either on the client side or to send the users login to a WCF service in a messageheader), but it seems to be reset to a standard GenericPrincipal. When I revert the login back to being synchronous, the CurrentPrincipal is just fine. So in short: how do I set the principal in the asynchronous code, having it persist later on, instead of reverting back to a standard principal?

Answer

Well, well, no answer in a year. No worries, since I managed to solve this myself: I simply wrapped a singleton around it all:

    public sealed class CurrentPrincipalFacade : IPrincipal
{
    #region Singleton mechanism

    private static readonly CurrentPrincipalFacade instance = new CurrentPrincipalFacade();
    public static CurrentPrincipalFacade Instance { get { return instance; } }
    private CurrentPrincipalFacade() { }

    #endregion

    #region IPrincipal members

    public IPrincipal Principal { get; set; }

    public IIdentity Identity { get { return Principal == null ? null : Principal.Identity; } }

    public bool IsInRole(string role) { return Principal != null && Principal.IsInRole(role); }

    public void Reset() { Principal = new GenericPrincipal(new GenericIdentity(""), new string[] { }); }
    #endregion}

So I set that after login. I guess the problem was I was setting the principal in another thread, which got lost when I got out of that?

Comments