A ritka kivételektől eltekintve szinte mindegyik mobil alkalmazás kommunikál valamilyen backend rendszerrel, most csupán a Windows Communication Foundation (WCF) vonalra térnék ki.
Sok helyen lehet találkozni olyannal, hogy nem működik a WCF Xamarin alatt, pedig csupán néhány korlátozással kell együtt élni a cél elérése érdekében.
Ezen a linken elérhető egy követhető leírás Xamarinhoz arról, hogyan kell összekötni egy WCF-es kiszolgálóval. Ehhez tennék kiegészítést néhány speciális esetet megvizsgálva.
Sok helyen lehet találkozni olyannal, hogy nem működik a WCF Xamarin alatt, pedig csupán néhány korlátozással kell együtt élni a cél elérése érdekében.
Ezen a linken elérhető egy követhető leírás Xamarinhoz arról, hogyan kell összekötni egy WCF-es kiszolgálóval. Ehhez tennék kiegészítést néhány speciális esetet megvizsgálva.
Korlátozások
- Nincs XAML konfig
A "hagyományos" XML alapú konfigurációs fájlokkal tényleg nem lehet megvalósítani, mivel nem lehet csak úgy odatenni az indítófájl mellé. Másfelől .NET C# alatt, - hasonlóan a WPF UI vezérlőkhöz - ami konfig fájlban XAML-lel leírható, az C# kóddal is. Ezt használja ki a fentebb linkelt tutoriál is.
- Nincs NetTCPBinding
Fontos megjegyezni, hogy mobilon nem létezik a NetTCPBinding, csak valamilyen *HttpBinding típust lehet használni.
- Nincs EndpointBehaviors
PCL készítésekor egy WCF kliensben a Behavior helyett az EndpointBehaviort kell használni, ami Windows RT-n még elfut, de Android és iOS rendszereken a MONO alatt NotImplementedExceptionnel elszáll. Szerencsére reflectionnel kinyerhető a megoldás:
//client.Endpoint.EndpointBehaviors.Add(new EndpointBehavior()); //client is of type System.ServiceModel.ClientBase<T> var prop = client.Endpoint.GetType() .GetTypeInfo() .GetDeclaredProperty("Behaviors"); var obj = (KeyedCollection<Type, IEndpointBehavior>)prop .GetValue(client.Endpoint); obj.Add(new EndpointBehavior());
Ugyanez a helyzet a MessageInspectorral is:
//clientRuntime.ClientMessageInspectors.Add(new MessageInspector()); var prop = clientRuntime.GetType() .GetTypeInfo() .GetDeclaredProperty("MessageInspectors"); var obj = (ICollection<IClientMessageInspector>)prop .GetValue(clientRuntime); obj.Add(new MessageInspector());
Wrapper a WCF hívás körül
Korábban már írtam arról, hogyan célszerű felkészülni arra, ha a WCF kliens hívása kezeletlen hibára fut. Ilyenkor nem szabad a Close()-t ráhívni, mint ahogy azt teszi alapértelmezetten a USING, helyette Abort() kell.
Async/await és a WCF
Xamarin alatt is használhatók az async/await kulcsszavak, amik tökéletesen illeszkednek az aszinkron WCF hívások köré. Bővebb leírás itt található.
public class WcfServiceWrapper:IWcfServiceWrapper { private IWcfService _wcfService; public WcfServiceWrapper(IWcfService wcfService) { if (wcfService == null) throw new ArgumentNullException("wcfService"); _wcfService = wcfService; } public async Task<string> GetDataAsync(int number) { return await new TaskFactory() .FromAsync<int,string>( _wcfService.BeginGetData, _wcfService.EndGetData, number, null, TaskCreationOptions.None ); } }
.NET Portable Assembly
Ha esetleg bármi miatt szeretnénk hozzáadni mobilon is használható .NET portable assembly-t, akkor azok itt találhatók:
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\
Generált proxy és az IExtensibleDataObject
A WCF által publikált WSDL alapján le lehet generálni a hálózaton keresztül utazó osztályokat. Amennyiben a DataContractSerializert használjuk szerver oldalon, akkor ráteszi az IExtensibleDataObject interfészt és a hozzátartozó ExtensionDataObject tulajdonságot ezen generált osztályokra. Önmagában nem lenne baj, hiszen a System.Runtime.Serialization.dll-ben elérhető ez az interfész és osztály, van belőle portable DLL is, gondolhatnánk, hogy majd hozzáadjuk a PCL projekthez és készen vagyunk. Nos, ez az út nem járható, nem engedi hozzáadni azzal az üzenettel, hogy már referálja a sajátját. A probléma az, hogy abban viszont nincsenek benne ezek.
1) Egyik megoldás lehet, hogy a kiszolgáló oldalon a ServiceContract attribútumban vagy akár magában a DataContractSerializerben bekapcsoljuk, hogy IgnoreExtensionDataObject. Viszont ezek a nevüknek megfelelően csak annyit csinálnak, hogy figyelembe kell-e venni az ExtensionDataObjectet vagy sem, de továbbra is megmaradnak az osztályokban. Elvileg a platformspecifikus projekthez már hozzá lehet adni ezt a dll-t, és utána működhet valahogy a DependencyService-en keresztül, de ezt a vonalat nem próbáltam ki.
2) Ha biztosan tudjuk, hogy egyáltalán nem használjuk semmire az ExtensionDataObjectet, akkor egy-egy üres osztállyal és interfésszel könnyedén orvosolható a helyzet:
public interface IExtensibleDataObject { ExtensionData { get; set; } } public sealed class ExtensionDataObject {}