Task-based asynchronní volání WCF operací v Silverlight

Tomáš Holan       10. 12. 2012       Silverlight, WCF/WS, Threading       5661 zobrazení

Silverlight 5.0 pomoci Async Targeting Pack podporuje použití nové syntaxe resp. nových klíčových slov async/await. Pokud ale do Silverlight projektu přidáme referenci na WCF službu není zde podporováno automatické “zaobalení“ asynchronních volání operací WCF služby do metod vracející objekt Task. Toto si musíme udělat sami.

Dialog pro přidání nebo konfiguraci reference na WCF službu vypadá totiž v Silverlightu (a VS 2012) následovně:

image

Je vidět, že volbu “Generate task-based operations” zde nelze vybrat a pro generování proxy tříd je tedy stále použit event-based asynchronous pattern(EAP) (i když alternativně lze použít i APM).

Pokud tedy máme na WCF službě implementovanou například tuto operaci:

[OperationContract]
public UserInfo GetUserInfo(string sessionToken);

Je pro ní na klientské straně ve WCF proxy dle EAP vygenerováno následující:

public partial class GetUserInfoCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs 
{
    public UserInfo Result { get; }
}

public event EventHandler<GetUserInfoCompletedEventArgs> GetUserInfoCompleted;
public void GetUserInfoAsync(string sessionToken);
public void GetUserInfoAsync(string sessionToken, object userState);

Co my musíme udělat je to, že pro každou operaci implementujeme metodu, která mechanicky “zaobalí” volání asynchronní operace jako Task (nebo Task<T>). Přitom použijeme objekt TaskCompletionSource<TResult>. Metodu umístíme do statické třídy Proxy.

Pro operaci výše to bude vypadat takto:

public static Task<UserInfo> GetUserInfo(string sessionToken, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
{
    var tcs = new TaskCompletionSource<UserInfo>();
    if (cancellationToken.IsCancellationRequested)
    {
        tcs.SetCanceled();
        return tcs.Task;
    }
    var cancellationRegistration = cancellationToken.Register(() => tcs.TrySetCanceled());
    var service = Proxy.Service;
    Guid token = Guid.NewGuid();
    EventHandler<GetUserInfoCompletedEventArgs> handler = null;
    handler = (s, e) =>
        {
            if ((Guid)e.UserState != token)
            {
                return;
            }
            service.GetUserInfoCompleted -= handler;
            cancellationRegistration.Dispose();

            if (e.Cancelled || cancellationToken.IsCancellationRequested)
            {
                tcs.TrySetCanceled();
            }
            else if (e.Error != null)
            {
                tcs.SetException(e.Error);
            }
            else
            {
                tcs.SetResult(e.Result);
            }
        };
    service.GetUserInfoCompleted += handler;
    service.GetUserInfoAsync(sessionToken, token);
    return tcs.Task;
}

Instance WCF proxy je zde zpřístupněna přes vlastnost Proxy.Service. Její implementace může vypadat například následovně:

private static WCFServiceClient s_Service;

private static WCFServiceClient Service
{
    get
    {
        if (s_Service == null)
        {
            //Inicializace proxy služby
            var address = new Uri(Application.Current.Host.Source, "../WCFService.svc");

            var endpoint = new System.ServiceModel.EndpointAddress(address);
            var binding = new CustomBinding(new BinaryMessageEncodingBindingElement(),
                                            address.Scheme == "https" ? new HttpsTransportBindingElement() { MaxReceivedMessageSize = 2147483647 } : new HttpTransportBindingElement() { MaxReceivedMessageSize = 2147483647 });

            s_Service = new WCFServiceClient(binding, endpoint);
        }

        return s_Service;
    }
}

Ještě bych se chvilku pozastavil na tom, jak se řešena podpora pro cancellation. Metodě GetUserInfo může být při volání předán CancellationToken, který umožňuje signalizovat požadavek na zrušení probíhající operace.

V takovém případě ale není prováděna žádná komunikace pro přerušení operace směrem na server, protože taková obsluha není u obecné operace možná. Místo toho je při signalizování zrušení operace alespoň provedeno okamžité přerušení čekání (pomoci await) na její dokončení. Tohoto je docíleno v nastaveném callbacku metodou CancellationToken.Register. Při dokončení operace na serveru je pak případ jejího případného zrušení již pouze ošetřen tak, aby nebyla provedena žádná další akce.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback