20 February 2008 .NET, .NET 3.5, AJAX, HowTo, Json, WCF Robert Muehsig

Das Thema ist eigentlich schon alt - wie kann man .NET Methoden aus Javascript aufrufen. Mit ASMX Webservices ging dies recht einfach - siehe HowTo - doch geht das auch mit WCF?

Seit .NET 3.5 und Visual Studio 2008 strahlte mich dieses Itemtemplate an:

image

"AJAX-enabled WCF Service" klingt schonmal gut.

Daraus wird dann sowas (ich hab die Standardmethode mal abgewandelt) :

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service
{
   // Add [WebGet] attribute to use HTTP GET
   [OperationContract]
   public DateTime GetDateTime()
   {
        return DateTime.Now;
   }

   // Add more operations here and mark them with [OperationContract]
}

Wie üblich, muss man den ScriptManager diesen Service (mit der .svc Endung) noch registrieren:

    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Services>
            <asp:ServiceReference Path="~/Service.svc" />
        </Services>
    </asp:ScriptManager>

Wenn man dies jetzt einfach mal so ausführt, bekommt man folgendes (im Firebug) zu sehen:

image

Schauen wir uns das Ergebnis jetzt mal per Javascript an:

image

Eigentlich ist es genau dasselbe wie bei den "alten" ASMX Webservices - nur diesmal mit WCF.

Doch im Detail gibt es Unterschiede

Um dies deutlich zu machen, folgender Aufbau der Website:

image

Ich habe einmal einen WCF und einen ASMX Webservice erstellt, mit folgenden Methoden:

- GetDateTime: Gibt ein DateTime Objekt zurück
- GetComplexOne: Gibt einen eigenen Objekttyp "Complex" zurück
- GetComplexList: Gibt eine Collection an "Complex" Typen zurück.

Beschreibung von eigenen Objekttyp "Complex"

image

"GetList" und "GetOne" sind beide statisch. "Number" ist vom Typ integer und die anderen beiden sind Strings.

Erstellung des ASMX Webservice

Der ASMX Webservice ist nicht weiter schwierig:

using System;
using System.Collections;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;
using System.Collections.Generic;

/// <summary>
/// Summary description for WebService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
[System.Web.Script.Services.ScriptService]
public class WebService : System.Web.Services.WebService {

    public WebService () {

    }

    [WebMethod]
    public DateTime GetDateTime()
    {
        return DateTime.Now;
    }

    [WebMethod]
    public ComplexType GetComplexOne()
    {
        return ComplexType.GetOne();
    }

    [WebMethod]
    public List<ComplexType> GetComplexList()
    {
        return ComplexType.GetList();
    }
}  

Wichtige Attribute sind hier das für die Ajax-Integration wichtige "ScriptServiceAttribut", sowie das "WebMethodAttribut". Wer mehr Infos möchte, dem sei dieser MSDN Artikel ans Herz gelegt.

Im Javascript werden diese Webmethoden in solch einen Wrapper gesetzt:

image

Erstellung des WCF Services

Der WCF Service ist ähnlich vom Aufbau, allerdings sind andere Attribute und Klassen im Gebrauch:

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.ServiceModel.Description;
using System.Collections.Generic;

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service
{
   // Add [WebGet] attribute to use HTTP GET
   [OperationContract]
   public DateTime GetDateTime()
   {
        return DateTime.Now;
   }

    [OperationContract]
    public ComplexType GetComplexOne()
    {
        return ComplexType.GetOne();
    }
    
    
    [OperationContract]
    public List<ComplexType> GetComplexList()
    {
        return ComplexType.GetList();
    }
   // Add more operations here and mark them with [OperationContract]
}

Das wichtigste Attribut für den JSON Webserive ist das "AspNetCompatibilityRequirementsAttribute" und dann für WCF typisch das "OperationContractAttribute".

Wenn man das nun so kompiliert und die beiden Services in den ScriptManager einbindet, bekommen wir einen Fehler in unserem WCF Service!

Den "ComplexType" für WCF zugänglich machen

WCF serializiert nur das, was konfiguriert wurde (um es mal einfach auszudrücken). Dafür müssen über dem Typen ein "DataContractAttribute" gesetzt werden und über jeden Member ein "DataMemberAttribute".

Sobald dies gemacht wurde, sieht man im Javascript folgendes:

image

Die serializierten Objekte sind identisch mit der ASMX Variante.

Mehr zum Thema JSON Serializierung: Stand-Alone JSON Serialization.

Zwischenfazit

Läuft schonmal ganz gut und bis auf die Sache mit dem "DataContract/MemberAttributen" sind kaum Unterschiede. Wenn man etwas mehr ins Detail geht, findet man allerdings noch das ein oder andere, was anders ist:

Unterschiede Namespaces:

Wenn man um die ASMX "WebService" Klasse ein Namespace "WebServiceNamespace" zieht, wird aus dem Javascript Wrapper folgendes:

image

Wenn man um den WCF Service einen Namespace zieht, passiert erstmal garnix - warum weiß ich spontan auch leider nicht. Um jedoch den selben Effekt zu erzielen wie bei der ASMX Variante muss man im "ServiceContractAttribute" einen Namespace setzen:

[ServiceContract(Namespace = "ServiceNamespace")]

image

Unterschiede HttpContext / ASP.NET Integration:

In einem WCF Service kann man nicht auf HttpContext zugreifen, sondern auf einen OperationContext. Dies und mehr ist auf der MSDN ganz gut beschrieben:

WCF Services and ASP.NET

Weitere Infos & Fazit:

Auf der MSDN ist noch ein weiteres einfaches Beispiel zu finden.
Hier sind auch noch zwei andere Blogposts zu dem Thema:

WCF Serivces sind eine mächtige Alternative zu den ASMX Webservices und die Integration in eine AJAX Anwendung ist kaum schwieriger als die ASMX Variante - wenn man die Möglichkeit hat und etwas rumexperimentieren möchte, der sollte dies wahrnehmen. Hier noch der Democode:

[ Download Democode ]


Written by Robert Muehsig

Software Developer - from Saxony, Germany - working on primedocs.io. Microsoft MVP & Web Geek.
Other Projects: KnowYourStack.com | ExpensiveMeeting | EinKofferVollerReisen.de