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.
Korlátozások
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.
Fontos megjegyezni, hogy mobilon nem létezik a NetTCPBinding, csak valamilyen *HttpBinding típust lehet használni.
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 {}