Der Frühling wird bunt! Unser neuer SDX Smart 24.04.2015

Svenja Henß
Svenja Henß, Senior Assistant

Der Frühling wird bunt. Smiley Endlich ist unser SDX Smart fertig und strahlt mit der Sonne um die Wette.

Haltet eure Augen offen in Frankfurt. Sobald ihr den Smart in der Frankfurter City entdeckt, freuen wir uns über einen Facebook Post - natürlich inklusive Foto - von euch.

Foto 5

 

 

 

 


 

 

 

 

Wir suchen Verstärkung für unsere eXpert Teams in Frankfurt und München:

  • Web/Mobile Developer (Xamarin, HTML5,.NET)
  • BI Developer
  • .NET Architekten
  • .NET Developer

Unsere Projekte sind regional begrenzt und übernachtungsfrei – du schläfst abends also zu Hause in deinem eigenen Bett und kannst den Feierabend mit Familie und Freunden genießen.

Wie die eXperts “ticken”, was uns aktuell technologisch beschäftigt und alle weitere Infos findest du unter www.sdx.de

Foto 28Foto 9Foto 13Foto 16

Share |

Das Blau ist mir nicht blau genug! 22.04.2015

Sven Martens
Sven Martens, Chief eXpert
Drei-Schichten-Architektur, WCF Services, ein schickes Frontend in Microsoft Einheitsgrau, alles aus einem Guss nach bewährten und gängigen Mustern entwickelt für die Ewigkeit, so sah meine heile Welt vor noch nicht allzu langer Zeit aus. Wohlbehütet und mit einem zufriedenen Kunden an der Seite dachte ich, mich könne so schnell nichts mehr überraschen. Und dann ganz plötzlich, geradezu hinterrücks, sprang mir aus dem dichten Dickicht des Technologiedschungels das sagenumwobene "Web" in den Nacken.

Fast fünf Monate ist das nun her und ich lerne langsam aber sicher wieder zu laufen. Für alle, die ein ähnliches Schicksal ereilt, habe ich hier die wichtigsten Bereicherungen meines täglichen Lebens noch mal zusammengefasst.

1. Nie mehr graue Maus
"Zu Mainstream!", "Boah das ist voll Retro Du Noob!" ... Worthülsen, die im urbanen Leben zum Standardrepertoire einer ganzen Subkultur gehören, werden Ihre Welt erobern. Seien Sie bereit dafür, zum Hipster der Entwickler aufzusteigen und hecheln Sie neusten technologischen Trends hinterher die schneller aufkommen und wieder verfliegen wie der Furz ein texanischen Sumpfkaninchens am Rande des Río de Sabinas.

2. Erhöhte Frustrationsschwellen
Neigen Sie dazu leicht genervt zu sein? Wutausbrüche gehören zu Ihrem Berufsleben und sind Ihnen peinlich? Web-Development bietet Ihnen die perfekte Lösung! CSS3 ist innerhalb kürzester Zeit erlernbar und hat einen äußerst positiven Aspekt auf die eigenen Frustrationsschwellen. Einmal erlernt lassen sich komplexe Probleme pragmatisch mit einfachsten Mitteln lösen.


Quelle: https://twitter.com/myusuf3/status/317338683852324864

3. Das reichhaltigste Entwicklungsökosystem ever
Gut, böse Zungen würden behaupten, dass es mehr clientseitige Javascript-Frameworks gibt als Apps & Sites zusammen die selbige nutzen, aber Fakt ist nun mal, es gibt fast nichts was es nicht gibt. Sehen Sie es einfach positiv und nutzen Sie die gegebene Vielfalt! Und sei es nur dazu die Beziehung zu Ihren Arbeitskollegen zu verbessern in dem Sie am Ende eines harten Tages folgendes mit Ihren Kollegen tun:

  • Denken Sie sich ein Nomen aus
  • Googeln Sie das Wort "<nomen>.js"
  • Wenn eine Library mit diesem Namen existiert, muss derjenige einen Drink zu sich nehmen
Javascript kann so viel Spaß machen und das Team stärken!

4. Immer die neuste Hardware
Was musste man als "stinknormaler" Entwickler nicht immer betteln, um vom Chef mit den neusten technischen Gadgets ausgestattet zu werden?! Vergessen sind diese Zeiten, als Web Developer ist man diesbezüglich immer Leading Edge! Kein Wunsch wird einem jemals wieder verwehrt bleiben, ganz im Gegenteil, anstatt eines neuen Tablets haben Sie gleich zehn davon, und das mit den unterschiedlichsten Formfaktoren. Der nur geringfügig höhere Testaufwand ist dabei durchaus zu verkraften.


Quelle: “I Heard You Want To Be A Web Developer.” WeKnowMemes

5. Anregende Kommunikation
Was waren die Meetings bzgl. neuer Requirements mit Fachabteilungen doch dröge und langweilig! Haben Sie künftig einfach mehr Spaß mit Ihrem neuen Ansprechpartner ... dem Marketing. Diskutieren sie angeregt und ausgedehnt darüber warum man ein Grafik nicht einen halben Pixel nach links schieben kann, freuen sich sich über stundenlanges Brainstorming welchen Text ein Button haben soll und lernen sie ganz nebenbei mit unscharfen Anforderungen wie z.B. "das Blau ist mir nicht blau genug" umzugehen. Egal wie es kommt, bei einem Punkt können Sie gewiss sein, Sie werden zum wahren Meister in Sachen Kommunikation.

6. Entdeckung eines neuen Selbstbewusstseins
Wie lange schon fühlen Sie sich als kleines Rad im Getriebe eines Großprojekts? Wie lange schon leidet Ihr Selbstbewusstsein daran, einfach austauschbar zu sein? Web Development verhilft Ihnen zu einer nie dagewesenen Stärke, werden Sie sich nach all den Jahren des tristen Entwicklerdaseins Ihrer Wichtigkeit bewusst!

Fazit
Diese Liste ließe sich quasi endlos erweitern, gerade dann, wenn man die Welt durch die Brille eines klassischen "Desktop- und Backendentwicklers" sieht. Und doch möchte ich all denen, die das gleiche Schicksal ereilt keine Angst machen. Eines kann ich zumindest aus meiner Sicht sagen, hat man erst mal die größten Schmerzen hinter sich gebracht, dann bringt die Webentwicklung zweifellos eine Menge Spaß mit sich!

Share |

LINQ Coding Guidelines #10–Methoden richtig verwenden 20.04.2015

Alexander Jung
Alexander Jung, Chief eXpert

Manche LINQ-Funktionen bieten Shortcuts an um zwei Dinge auf einmal zu tun. Keine gute Idee wie ich finde…

Empfehlung: Der Aufruf von LINQ-Methoden sollten sich auf die jeweilige Intention der Methode beschränken.

 

 

Simple Beispiele von Microsoft:

   1: string[] strings = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; 
   2: string startsWithO = strings.First(s => s[0] == 'o'); 

Und:

   1: int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 
   2: int oddNumbers = numbers.Count(n => n % 2 == 1); 

Die Aufgabe von First(), Any(), Count() und anderen Aggregat-Funktionen dürfte klar sein. Leider gibt es auch Überladungen, die ein zusätzliches Filterkriterium übernehmen, was aber über den eigentlichen Sinn und Zweck hinausgeht – für die Filterung ist Where() da. Das ist ein – wie ich finde unnötiger – Shortcut, der das Single Responsibility-Prinzip verletzt.

Ergo sollten die Filterungen in den Bespielen mit Where() erfolgen und die nachfolgenden Aufrufe von First() und Count() ohne weitere Argumente.

Damit diese Aussagen nicht falsch interpretiert werden: Ich bin durchaus ein Freund von Hilfsmethoden die mehrere Aufrufe zusammenfassen. Sie können den Code-Umfang deutlich reduzieren und den Code lesbarer gestalten. Nur sollte die Methode klar ausdrücken, was sie tut. Mit zusätzlichen Methoden AnyWhere(), FirstWhere() oder CountWhere() hätte ich nicht das geringste Problem, die vorhandenen Methoden drücken aber den Zweck nicht vollständig aus.

PS: Es gibt durchaus Funktionen, bei denen ein Prädikat Sinn macht, z.B. TakeWhile(). Also nicht das Kind mit dem Bade ausschütten… ;-)

Share |

Sonniger Office Day im April 15.04.2015

Svenja Henß
Svenja Henß, Senior Assistant

Letzten Freitag war es endlich wieder soweit – der monatliche Office Day stand auf dem Programm. Smiley

Wie immer startete der Tag mir einem ausführlichen Business Update.
Danach wurde es feierlich: Wir freuen uns, dass Alex bereits seit 10 Jahren Teil der SDX eXperts ist. Ein herzliches DANKE für die gute und erfolgreiche Zusammenarbeit!

P1020459

Außerdem standen noch die folgenden Themen auf der Agenda:

  • How To use Azure
  • Bericht zum iSAQB Seminar
  • Vorstellung nBI Testframework für BI Projekte
  • Einführung in Xamarin
  • Einführung in Xamarin.Forms anhand eines Kundenbeispiels

Die Mittagspause verbrachten wir bei strahlendem Sonnenschein auf der Dachterrasse des Westhafenhauses und konnten die Sandwiches mit Blick auf die Skyline genießen.Smiley

Mittag3Mittag6Mittag12Mittag15

Share |

Präsentieren von Code mit Visual Studio 14.04.2015

Max Jäger
Max Jäger, Senior eXpert
Nicht nur an unseren Office-Days wird oftmals Code in Visual Studio gezeigt. Dabei ist es für diejenigen, die etwas weiter weg sitzen, nicht immer leicht, alles gut zu erkennen. Doch Visual Studio bietet mehrere Möglichkeiten, die Darstellung zu verbessern.

Anzeige im Vollbildmodus

Zunächst kann die Anzeige und Anordnung der einzelnen Elemente der Entwicklungsumgebung für den Vollbildmodus optimiert werden. Hierzu bietet Visual Studio die Möglichkeit zur Aktivierung des Vollbildmodus. Dieser kann über [Alt]+[Shift]+[Enter] aktiviert werden. Hierdurch werden beispielsweise die Taskbar und die Titelleiste ausgeblendet und in der Standardeinstellung nur das Codefenster und das Menü angezeigt.
Ausgeblendete Elemente können über das Menü selbstverständlich wieder eingeblendet werden. Visual Studio merkt sich die getätigten Einstellung, sodass diese beim erneuten Aktivieren des Vollbildmodus wiederhergestellt werden.

Präsentationsmodus aktivieren

Eine Möglichkeit, alle Elemente von Visual Studio zu vergrößern, bietet der Präsentationsmodus, der in den Productivity Power Tools enthalten ist. Um diesen Modus zu (de-)aktivieren, muss zunächst mit [Strg]+[Q] die Quick Launch Bar fokussiert werden. Danach kann dort über die Eingabe von Present der Präsentationmodus ein- oder ausgeschaltet werden. Nach der Aktivierung dieses Modus werden alle Elemente, und nicht nur das aktive Code-Fenster, vergrößert dargestellt.

QuickLaunchBar

Zoomen

Um die Schriftart des Codefensters zu verändern, kann ein Zoomfaktor eingestellt werden. Dies kann entweder über die Combobox, die sich am unteren linken Rand des Fensters befindet, erfolgen, oder über das Halten der [Strg]-Taste und Drehen am Scrollrad der Maus. Nachteil hierbei ist jedoch, dass die Einstellung bei dieser Vorgehensweise nur für das Fenster gilt, in dem dies durchgeführt wird. Zeigt man eine andere Datei in einem neuen Fenster an, so wird dieser Faktor wieder zurückgesetzt.
Aber auch hierfür bieten die Productivity Power Tools eine schnell erreichbare Option. Über die Quick Tasks TxtFont+ und TxtFont- kann die Schriftgröße für das Codefenster vergrößert oder verkleinert werden. Dies ist eine globale Einstellung, sodass auch neu geöffnete Dateien die entsprechende Schriftgröße erhalten. Die Vergrößerung der Schriftart ist zwar ein eingebautes Feature in Visual Studio, lässt sich aber erst durch die Powertools schnell (und ohne den Gang über die Einstellungen) verändern.

Fazit

Die Darstellung von Code in Visual Studio bei Präsentationen lässt sich mit wenigen einfachen Mitteln verbessern. Mit dem Vollbildmodus und dem Zoomen in einem Code-Fenster sind bereits zwei Optionen direkt integriert, und durch die Productivity Power Tools (die ich jedem nur sehr ans Herz legen kann) kann die Darstellung nochmals verbessert und schnell (de-)aktiviert werden.

Share |

Testen von Datenbanken - 4 Testfälle in zur Laufzeit gemounteter Datenbank 10.04.2015

Alexander Kabisch
Alexander Kabisch, Principal eXpert

Bei diesem Vorgehen werden die Testfälle gegen eine eigene Testdatenbank ausgeführt, die erst zur Laufzeit gemountet und angesprochen wird. Als Voraussetzung benötigt man eine gefüllte Datenbankdatei und den MS SQL Server Express.

Ihm kann man im ConnectionString über den Befehl "AttachDBFilename" eine Datenbankdatei mitgeben, die er dann mountet. Leider wird der Platzhalter |DataDirectory| nur in ASP.Net Applikationen richtig gefüllt, da hier der Paramater in der AppDomain überhaupt gefüllt ist. Man könnte diese per Code setzen, was nicht so sauber ist.

  • AppDomain.CurrentDomain.SetData("DataDirectory", ...);

Ich empfehle dringend während des Testlaufs eine Kopie der Datei zu mounten, denn Veränderungen über SQL werden persistiert! Ich habe mich in meinem Beispiel dafür entschieden die Datei nach C:\Temp zu kopieren und sie von dort zu mounten.

Erstellung und Bearbeitung der Testdaten

Zum Erstellen oder Befüllen der Datenbankdatei kann man Visual Studio zur Hilfe nehmen. Bei einer neuen Datenbank muss man unbedingt für den aktuellen Login einen User auf der Datenbank erstellen. Ansonsten kommt es beim späteren Mounten zu einem Fehler. Der MS SQL Server Express wirft dann eine nichts sagende "Datei korrupt" Fehlermeldung.

4 - 1 DB

Sobald man alle Testdaten erstellt hat könnte man die Datenbankdatei sogar in ein Source Control System einchecken. Sie ist ein Teil des Projektes. Auch hier enthält die Datenbank alle Testdaten. Man könnte sogar für jedes Testszenario eine eigene Datenbank verwenden. Dies wäre nur sehr aufwendig.

Testablauf am Beispiel der Referenzanwendung

  1. Die Testdatenbankdatei wird kopiert.
  2. Eine Verbindung zum MS SQL Server Express wird geöffnet, dabei ist im ConnectionString der Pfad zur Testdatenbankdatei enthalten.
  3. Es wird davon ausgegangen dass die Daten in der Tabelle [dbo].[Source] zum 1.1.2014 vorliegen.
  4. Die Tabelle [dbo].[Target_Sum] wird gelöscht.
    • [dbo].[Delete_Target_Sum_ByCalcDate]
  5. Die Tabelle [dbo].[Target_Sum] wird befüllt.
    • [dbo].[Insert_Target_ByCalcDate]
  6. Es wird geprüft ob die Daten wie erwartet in der Tabelle [dbo].[Target_Sum] vorliegen.
    • Die Daten werden vom Test in eine Datei exportiert.
    • Die Datei wird mit einer erwarteten Datei verglichen.
[TestInitialize]
public void Init()
{
    //AppDomain.CurrentDomain.SetData("DataDirectory", ...);
 
    //Was ist richtig(er)?
 
    //Environment.CurrentDirectory;
    //AppDomain.CurrentDomain.BaseDirectory;
            
    // Achtung: Wenn Shadowcopy aktiv wird es komliziert. In NUnit standardmäßig aktiviert!
    //(typeof(UnitTest1)).Assembly.CodeBase // wird mit file:/// geliefert
    //(typeof(UnitTest1)).Assembly.Location 
}
 
[TestMethod]
public void DALTest_MountDB()
{
    DBManagerTarget mgr = new DBManagerTarget();
    DateTime calcDate = new DateTime(2014, 01, 01);
    mgr.DeleteTargetSum(calcDate);
    mgr.FillTarget(calcDate);
 
    DBManager testDatenmanger = new DBManager("DB", false);
 
    string filename = "DALTest_MountDB_UnitTest1.txt";
    testDatenmanger.ReadToFile(
        DIRTARGET + filename,
        "SELECT * FROM dbo.Target_Sum WHERE CalcDate='2014-01-01';");
 
    CompareHelper.AssertAreEual(DIRSOURCE + filename, DIRTARGET + filename);
}
<connectionStrings>
  <clear/>
  <!--add name="DB" connectionString="Server=(localdb)\Projects;AttachDbFilename=|DataDirectory|\DB\DBTesting.mdf..."/ -->
  <add name="DB" connectionString="Server=(localdb)\Projects;AttachDbFilename=C:\TEMP\DBTesting.mdf;Database=DBTesting;Trusted_Connection=Yes;" .../>
</connectionStrings>

Ergebnis: Erfolgreich

SELECT * FROM dbo.Target_Sum WHERE CalcDate='2014-01-01';
 
CalcDate(datetime),ID(int),Value(float)
 
01.01.2014 00:00:00,1,10
01.01.2014 00:00:00,2,20
01.01.2014 00:00:00,3,30
01.01.2014 00:00:00,4,40

Sollte jedoch die Tabelle [dbo].[Target_Code] schon mit anderen Werten befüllt sein, so wären abweichende technische IDs das Ergebnis und der Testfall wäre nicht erfolgreich.

Vorteile

  • Daten sind exakt auf die Tests zugeschnitten
    • Entwicklungsdaten werden nicht geändert
    • Sogar IDENTITY Spalten können exakt getestet werden
  • Testdaten (Datenbankdatei) kann mit in einem Source Control System abgelegt werden
    • Nachvollziehbarkeit steigt
    • Einfache Verteilung der Testfälle für alle Entwickler
  • MS SQL Server Express ist in der Standardinstallation vom Visual Studio enthalten
  • Pro Testszenarien eigene Datenbankdatei möglich
  • Test läuft komplett lokal ab

Nachteile

  • Weitere Umgebung die aktuell gehalten werden muss
  • MS SQL Server Express ist kein MS SQL Server, dessen Dateien können nicht im MS SQL Server gehostet werden
    • Abweichendes Feature Set
    • Abweichende Verarbeitung (Performance)
  • Aufwand steigt mit Anzahl der in Datenbankdateien separierten Testszenarien

Bei der Verwendung von nur einer Datenbankdatei gibt es kaum Vorteile zum vorherigen Vorgehen. Lediglich das hier die Datei unter Source Code Verwaltung gestellt werden kann. Jedoch können durch die Verwendung mehre Datenbankdateien verschiedene Testszenarien getrennt werden und sich so gegenseitig nicht mehr beeinflussen.

zur Übersicht

Share |

Herzlich Willkommen im SDX Flurfunk! 09.04.2015

Svenja Henß
Svenja Henß, Senior Assistant

Hallo!
Schön, dass du unseren Flurfunk besuchst.
Als eXperts unterstützen wir Unternehmen wie Commerzbank, DekaBank, Lufthansa, Airbus, Fresenius und Co. bei der Realisierung von attraktiven Win-/Web-/Mobile- und BI-Lösungen auf der Microsoft Application Platform.
Uns unterscheidet dabei von anderen IT-Dienstleistern, dass unsere Projekte regional begrenzt – und somit übernachtungsfrei – sind. Denn wichtig für uns alle ist ein gut funktionierendes Familien- und Privatleben.
Schlägt dein Herz auch für gute Software auf Basis der Microsoft Application Plattform?
Wünschst du dir Kollegen auf Augenhöhe?
Liebst du die Herausforderungen von attraktiven Projekten bei Enterprise Kunden?
Ja? Dann freuen wir uns, dich als neuen eXpert in unserem Team begrüßen zu können. Wir suchen für Frankfurt oder München:
      • Mobile – / Web-Developer (.NET, Xamarin, HTML)
      • .NET Software Architekten
      • .NET Principal Developer
      • .NET Enterprise Developer
      • BI-Developer (Microsoft SQL Server)
      • SharePoint Spezialisten
      • Business Development Manager
Schau dich einfach mal in unserem Flurfunk um. Folgende technische Themen werden im eXperts Team heiß diskutiert

- Xamarin Forms im Praxiseinsatz: Einleitung
- Xamarin Forms im Praxiseinsatz: Unterschiede zwischen Xamarin.Forms XAML  u. Microsoft XAML
- Hackathon mit AngularJS
- Besonderheiten in der Komprimierung auf Heap Tabellen
- Automatisierung des SSAS Builds
- Dependency Injection und WCF Service Interface
- Sichere Erkennung von seriellen Ports
- ASP.NET MVC Internationalisierung


Smart_hinten_Montage_02Smart_Seite_Montage_03Smart_vorne_Montage_02
Du willst wissen wie die eXperts “ticken”? Schau mal auf unserer Facebook Seite.
Einen guten Eindruck über die Arbeit im eXpert Team bekommst du auf kununu.com.
Hier gibt’s mehr Infos über die SDX AG oder unser aktuelles Jobangebot.
Gerne kannst du mich auch direkt unter 069/247518-22 anrufen oder deine Bewerbung bzw. dein Xing Profil direkt an Svenja.Henss@sdx-ag.de senden.
Viele Grüße
Svenja
Share |

LINQ Coding Guidelines #9–Die richtigen Methoden verwenden 08.04.2015

Alexander Jung
Alexander Jung, Chief eXpert

Ähnlich ist nicht gleich, und auch wenn manche Aufrufe in LINQ ähnlich aussehen, können sie in Sonderfällen durchaus relevante Unterschiede mit sich bringen.

Empfehlung: In LINQ-Abfragen sollten Methoden verwendet werden, die der Intention entsprechen.

 

 

Beispiele aus der Praxis:

   1: static string GetOSVersion()
   2: {
   3:     var name = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem")
   4:         .Get()
   5:         .OfType<ManagementObject>()
   6:         .Select(x => x.GetPropertyValue("Caption"))
   7:         .First();
   8:     return name != null ? name.ToString() : "Unknown";
   9: }

Und:

   1: User user = userRep.GetAll().Where(u => u.UserName == userName).SingleOrDefault();

Was ist das Problem?

Die Methoden First() und Single() sind zwar ähnlich, aber eben nicht identisch. Analoges gilt für
Cast<>() und OfType<>(). Technisch hat das – im Regelfall – keine Konsequenzen (von kleinen Effizienzgewinnen mal abgesehen), aber die inhaltliche Aussage ist eine andere:

  • Wenn ich First() aufrufe will ich das erste Element einer Collection, implizit das einzige, wenn mein Filter und das Datenmodell das vorsehen. Wenn ich hingegen Single() aufrufe, dann will ich zusätzlich verifizieren, dass das tatsächlich das einzige Element meiner Abfrage ist.

    Problem dabei: Eine Prüfung der Datenkonsistenz ist nicht die Aufgabe einer normalen Abfrage (abgesehen davon, dass da sicher mehr zu tun wäre). So etwas muss in der Datenbank über Constraints sichergestellt oder von der Geschäftslogik beim Schreiben verifiziert werden.
  • Wenn ich Daten in einer (aus Sicht von LINQ) untypisierten Collection habe, dann nutze ich Cast<>() um das zu casten. Wenn ich hingegen OfType<>() aufrufe, dann gehe ich davon aus, dass in der Collection auch Objekte anderen Typs enthalten kann, die ich ausfiltern möchte. Denkbar, aber eher selten und auf den Sonderfall von Ableitungshierarchien im Resultset beschränkt.

    Problem: Im Regelfall ist das ohne Auswirkung. Bis sich tatsächlich mal ein Fehler einschleicht, der durch die Wahl der falschen Methode nicht erkannt wird und Datenmüll produziert.

Ergo: Wenn es ähnliche Funktionen gibt, hat das normalerweise einen Grund. Man sollte hier genau hinschauen, welche Methode die richtige ist. Schlampigkeit rächt sich irgendwann.

Share |

Dependency Injection und WCF Service Interface 01.04.2015

Matthias Straßer
Matthias Straßer
Chief eXpert

In diesem Beitrag geht es um das Dependency Injection Pattern und die Fallstricke bei der Verwendung eines WCF Services und dessen Interface als abhängige Komponente. Die Nutzung eines WCF Services in einem Programm, nachdem man sich einen Proxy erzeugt hat, folgt in der Regel dem Schema a) Proxy-Klasse erzeugen, b) Service-Methode aufrufen und c) dann den Channel wieder sauber aufräumen.

   1: var client = new SampleWcfServiceReference.SampleWcfServiceClient("tcpEndpoint");
   2: var message = "DirectServiceCall";
   3: try
   4: {
   5:   var result = client.Method(message);
   6:   client.Close();
   7: }
   8: catch (Exception ex)
   9: {
  10:   client.Abort();
  11: }

Zu beachten ist hier, dass die Methoden Close und Abort durch die Proxy-Klasse, die durch svcutil erzeugt wird, implementiert werden, genauer durch die Basis-Klasse ClientBase<T>. Und im Fehlerfall sollte die Methode Abort aufgerufen werden.

Wird nun in einem Projekt auch das DI-Pattern genutzt und dafür das Castle Windsor Framework verwendet, dann ist im Sinne von Abhängigkeiten der WCF Service eine zu injizierende Abhängigkeit für eine Businesskomponente:

   1: public class LocalBusinessService : ILocalBusinessService
   2: {
   3:   ISampleWcfService _service;
   4:  
   5:   public LocalBusinessService(ISampleWcfService service)
   6:   {
   7:     _service = service;
   8:   }
   9:  
  10:   public void Process(string message)
  11:   {
  12:
  13:     var result = _service.Method(message);
  14:
  15:   }
  16: }

Das Bootstrapping und die Verwendung sehen wie folgt aus:

   1: var container = new Castle.Windsor.WindsorContainer();
   2: container.Register(Component.For<ISampleWcfService>()
   3:   .ImplementedBy<SampleWcfServiceClient>()
   4:   .DependsOn(Dependency.OnValue("endpointConfigurationName", "tcpEndpoint")));
   5: container.Register(Component.For<ILocalBusinessService>()
   6:   .ImplementedBy<LocalBusinessService>());
   7: var localService = container.Resolve<ILocalBusinessService>(); 
   8: try
   9: {
  10:   localService.Process("CastleWindsorCall");
  11: }
  12: catch (Exception ex)
  13: {
  14:   Console.WriteLine(ex.Message);
  15: }
  16: container.Dispose();

Problem an dieser Stelle ist, dass das Interface nicht die Close und Abort Methoden anbietet und der Lifecycle des Proxy vom DI Container gemanaged wird und somit das Aufräumen des Proxy von diesem vorgenommen wird.
Die Basisklasse BaseClient<T> implementiert auch das IDisposable Interface, welches vom Castle Windsor Container genutzt wird, wenn die Komponente aufgeräumt wird, es wird also ClientBase<T>.Dispose aufgerufen. Innerhalb von Dispose wird die Close Methode aufgerufen. Ist jedoch der CommunicationState Faulted, so wirft die ClientBase<T>.Dispose Methode bzw. die CommunicationObject.Close eine CommunicationObjectFaultedException mit der Nachricht "The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state". Um genau zu sein, tritt dieses Szenario ein, wenn die Kommunikation mit dem WCF Service über net.tcp läuft und die Exception in der Methode keine im Contract definierte FaultException ist. Letztendlich ist dies allerdings sehr unschön, da diese Exception an einer Stelle auftritt, die nicht mehr kontrolliert werden kann. Welche Möglichkeiten gibt es nun, dieses Problem einzufangen?

1. Ansatz: Castle Windsor Möglichkeiten

Für den Abbau von Objekten bietet Castle Windsor bei der Registrierung die Hinterlegung einer Funktion über die OnDestroy Methode an. Allerdings heißt es hier auch im Abschnitt zu IDisposable

„Note that if your class implements IDisposable, then Dispose will automatically be called on the object before your custom destroy function is invoked.”

Damit scheidet dieser Ansatz aus.

2. Ansatz: Wrapping des WebServices-Proxies

Es wird nicht die generierte Service-Proxy als Implementierung des WCF-Interfaces genutzt, sondern eine Klasse, die das WCF-Interface, das IDisposible Interface implementiert und intern die generierte Service-Proxy Klasse nutzt. Die WCF-Interface Methoden werden 1:1 an den Proxy weitergeleitet und im Dispose-Fall wird in Abhängigkeit des CommunicationState Abort oder Close am Proxy aufgerufen.

   1: public class SampleWcfServiceWrapper : ISampleWcfService, IDisposable
   2: {
   3:   readonly SampleWcfServiceClient _client;
   4:  
   5:   public SampleWcfServiceWrapper()
   6:   {
   7:     _client = new SampleWcfServiceClient("tcpEndpoint");
   8:   }
   9:  
  10:   ~SampleWcfServiceWrapper()
  11:   {
  12:     Dispose(false);
  13:   }
  14:  
  15:   public string Method (string message)
  16:   {
  17:     return _client.Method(message);
  18:   }
  19:  
  20:   public void Dispose()
  21:   {
  22:     Dispose(true);
  23:     GC.SuppressFinalize(this);
  24:   }
  25:  
  26:   private void Dispose(bool isDisposing)
  27:   {
  28:     if (isDisposing)
  29:     {
  30:       if (_client.State == System.ServiceModel.CommunicationState.Faulted)
  31:         _client.Abort();
  32:       else
  33:         _client.Close();
  34:     }
  35:   }
  36: }

Das Bootstrapping sieht dann wie folgt aus:

   1: var container = new Castle.Windsor.WindsorContainer();
   2: container.Register(Component.For<ISampleWcfService>()
   3:   .ImplementedBy<SampleWcfServiceWrapper>());
   4: container.Register(Component.For<ILocalBusinessService>()
   5:   .ImplementedBy<LocalBusinessService>());
   6:  
   7: var localService = container.Resolve<ILocalBusinessService>();
   8: try
   9: {
  10:   localService.Process("CastleWindsorCallWithWrapper");
  11: }
  12: catch (FaultException<SampleWcfServiceReference.FaultInformation> ex)
  13: {
  14:   Console.WriteLine(ex.Detail.Message);
  15: }
  16: catch (Exception ex)
  17: {
  18:   Console.WriteLine(ex.Message);
  19: }
  20:  
  21: container.Dispose();

Dieser Ansatz funktioniert wunderbar, doch ist es allerdings mühevoll hier ein Klasse anlegen zu müssen, die mehr oder weniger eine 1:1 Kopie der Proxy-Klasse ist.

3. Ansatz: Bridging Ansatz

Um weniger Code schreiben zu müssen und trotzdem die Sicherheit zu haben, kann ein Bridge Ansatz gewählt werden. Zunächst kann ein generisches Interface definiert werden, welches das WCF-Interface als generischen Parameter hat, dieses Interface als einzige Property anbietet und von IDisposable ableitet.

   1: public interface IWcfServiceBridge<T> : IDisposable
   2: {
   3:     T Interface { get; }
   4: }

Die Implementierung des Interfaces sieht dann wie folgt aus

   1: public class WcfServiceBrigde<T> : IWcfServiceBridge<T> where T : class
   2: {
   3:   private System.ServiceModel.ClientBase<T> _client;
   4:  
   5:   public WcfServiceBrigde(System.ServiceModel.ClientBase<T> client)
   6:   {
   7:     _client = client;
   8:   }
   9:  
  10:   ~WcfServiceBrigde()
  11:   {
  12:     Dispose(false);
  13:   }
  14:  
  15:   public T Interface
  16:   {
  17:     get { return _client as T; }
  18:   }
  19:  
  20:   public void Dispose()
  21:   {
  22:     Dispose(true);
  23:     GC.SuppressFinalize(this);
  24:   }
  25:  
  26:   private void Dispose(bool isDisposing)
  27:   {
  28:     if (isDisposing)
  29:     {
  30:       if (_client.State == System.ServiceModel.CommunicationState.Faulted)
  31:         _client.Abort();
  32:       else
  33:         _client.Close();
  34:     }
  35:   }
  36: }

Zu beachten ist hier, dass im Konstruktor die Basisklasse ClientBase<T> als Parameter erwartet wird, also eine Service-Proxy Klasse. Das Interface wird über die Proxy-Klasse geliefert und die Dispose Methode des IDisposable Interfaces kann auf den CommunicationState des Proxy reagieren. Eine weitere Anpassung ist am lokalen BusinessService noch notwendig, damit dieser nicht mehr das WCF-Interface als Abhängigkeit erwartet, sondern das Bridge-Interface.

   1: public class LocalBusinessServiceUsingBridge : ILocalBusinessService
   2: {
   3:   ISampleWcfService _service;
   4:  
   5:   public LocalBusinessServiceUsingBridge(IWcfServiceBridge<ISampleWcfService> service)
   6:   {
   7:     _service = service.Interface;
   8:   }
   9:  
  10:   public void Process(string message, bool callFaultException)
  11:   {
  12:
  13:     var result = _service.Method(message);
  14:
  15:   }
  16: }

Bootstrapping und Verwendung dieses Ansatzes sieht wie folgt aus

   1: var container = new Castle.Windsor.WindsorContainer();
   2: container.Register(Component.For<ISampleWcfService>()
   3:   .ImplementedBy<SampleWcfServiceClient>()
   4:   .DependsOn(Dependency.OnValue("endpointConfigurationName", "tcpEndpoint"))
   5:   .Named("SampleService"));
   6: container.Register(Component.For<IWcfServiceBridge<ISampleWcfService>>()
   7:   .ImplementedBy<WcfServiceBrigde<ISampleWcfService>>()
   8:   .DependsOn(Property.ForKey<ClientBase<ISampleWcfService>>().Is("SampleService")));
   9: container.Register(Component.For<ILocalBusinessService>()
  10:   .ImplementedBy<LocalBusinessServiceUsingBridge>());
  11:  
  12: var localService = container.Resolve<ILocalBusinessService>();
  13: try
  14: {
  15:   localService.Process("CastleWindsorCallWithBridge");
  16: }
  17: catch (FaultException<SampleWcfServiceReference.FaultInformation> ex)
  18: {
  19:   Console.WriteLine(ex.Message);
  20:   Console.WriteLine(ex.Detail.Message);
  21: }
  22: catch (Exception ex)
  23: {
  24:   Console.WriteLine(ex.Message);
  25: }
  26:  
  27: container.Dispose();

Fazit

Bei der Verwendung von WCF-Services und dem DI-Pattern ist das Lifecycle Management des DI-Containers zu beachten. Probleme können beim Aufräumen der Komponenten im Container entstehen. Wie sie umgangen werden können, wurde hier gezeigt. Persönlich würde ich den Bridging Ansatz wählen, weil damit kein Nachbau des WCF-Interfaces notwendig ist und dieser Ansatz somit auch stabil gegenüber Änderungen am Interface in der Entwicklungsphase ist.

Share |