.NET aus der Sicht eines Java-Entwicklers

Die letzten beiden Tage war ich damit beschäftigt, ein paar Evaluierungen für die .NET-Plattform zu machen. Eines unserer Projekte greift mit Hilfe eines (zugegebenermaßen ziemlich coolen) WPF-Frontends über WCF auf einen SOAP-Service zu, der die Verbindung zu einer MS SQL-Datenbank herstellt.

Logging auf die Konsole

Unter Java bzw. innerhalb eines Application-Servers ist es kein Problem, ein simples System.out.println() oder eben log.debug() zu benutzen. Die Ausgaben erscheinen dann jeweils in der Konsole der Entwicklungsumgebung. Wer jetzt denkt: so etwas geht doch sicherlich auch mit ASP.NET, der irrt gewaltig. Wenn innerhalb einer ASP.NET-Anwendung – hier zähle ich unseren WCF-Service einfach mal dazu – auf die Konsole geloggt wird (per log4nets Log.Debug oder aber Console.Write()), verschwinden die Ausgaben im Nirvana. Grund dafür ist, dass der IIS keine Konsole zur Verfügung stellt, in die überhaupt geloggt werden kann.

Entweder man benutzt nun die Trace-Methoden oder aber man schreibt die Logging-Ausgaben von log4net in eine Datei und setzt auf diese dann ein tail ab.

Lebenszyklus eines WCF-Service

Als JEE-Entwickler weiß ich, dass ein Servlet mit dem Start der Anwendung einmalig initalisiert wird und das Servlet dann so lange im Arbeitsspeicher gehalten wird, bis die Applikation heruntergefahren wird.

Bei WCF wird das Attribut InstanceContextMode benutzt, um die Gültigkeit eines Objekts zu beschreiben. Für einen WCF-Service gilt die Standardeinstellung, dass das Objekt (der Service) so lange vorgehalten wird, bis die gegenwärtige Session beendet wird (Einstellung PerSession).

Reihenfolge der Initalisierung von Servlets / Initialkonfiguration

In Anwendungen, die ich nicht mit Spring geschrieben habe, habe ich in meiner web.xml immer ein Servlet definiert, dass nichts weiter macht, als das die Standardeinstellungen gesetzt und z.B. der Logger initalisiert wird.

Bei .NET gibt es so etwas in der Art nicht (sollte ich falsch liegen, bitte ich um baldige Korrektur). Stattdessen muss man über eine selbst definierte ServiceHostFactory diese Konfiguration durchführen. Möglich wäre so etwas:

namespace My.Namespace.Service
{
    public class CustomServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
    {
        private Boolean isInitialized = false;

        public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
        {
            initalize();
            return base.CreateServiceHost(constructorString, baseAddresses);
        }

        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            initalize();
            return base.CreateServiceHost(serviceType, baseAddresses);
        }

        private void initalize()
        {
            if (isInitialized)
            {
                return;
            }

            log4net.Config.BasicConfigurator.Configure();
           // Weitere Initialisierungen
        }
    }
}

In der jeweiligen .svc-Datei muss dann die Instanzierung des Service über die CustomServiceHostFactory geschehen:

<%@ ServiceHost Language="C#" Debug="true" Factory="My.Namespace.Service.CustomServiceHostFactory" Service="My.Namespace.Service" CodeBehind="Service.svc.cs" %>

Service-Verbindungen im Client

Hinten über bin ich gefallen, als ich in der app.config des Clients probierte, das Suffix des Service-Endpoints einmalig über

<add key="httpBaseAddress" value="http://localhost" />

zu definieren. Das Attribut httpBaseAddress ist nur auf der Serverseite verfügbar und führt dazu, dass Redundanzen durch doppelte URIs entstehen.
Korrekt schaut das Beispiel nun so aus:

<configuration>
    <appSettings>
    </appSettings>
    <system.serviceModel>
      <!-- CusomtBinding benutzen, da mit wsHttpBinding NTLM nicht korrekt funktioniert -->
        <bindings>
          <customBinding>
            <binding name="Binding_Service">
              <mtomMessageEncoding />
              <security authenticationMode="SecureConversation">
                <secureConversationBootstrap authenticationMode="SspiNegotiated" />
              </security>
              <httpTransport authenticationScheme="Ntlm"/>
            </binding>
          </customBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:52264/Service.svc"
              binding="customBinding" bindingConfiguration="Binding_Service"
              contract="My.Namespace.Service.IService" name="Binding_IService">
            <identity>
              <dns value="localhost" />
            </identity>
          </endpoint>
        </client>
    </system.serviceModel>
</configuration>

Im Client wird dann über

ChannelFactory ServiceFactory = new ChannelFactory("Binding_IService");

der Proxy des Services geladen.

Die obige Konfiguration (customBinding) zeigt übrigens den Zugriff auf den Web-Service via NTLM. Mit dem Binding wsHttpBinding funktioniert NTLM nämlich nicht.

Leave a reply

Your email address will not be published.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>