Freitag, Juli 11, 2008

ASP.NET MVC Authorization (EntLib Rules)

Das letzte Wochenende habe ich mich mal wieder mit ASP.NET MVC beschäftigt, diesmal schon das 2. Wochenende mit der Autorisierung  von Benutzern. Ich glaube, die Variante über den Enterprise Library Rules Provider ist der beste Weg.

Hier mal kurz die verschiedenen Möglichkeiten

Also man hat sehr viele Möglichkeiten und wer sich mal durch die Links durchgeklickt hat, der wird auch mitbekommen haben, dass es sehr viele Diskussionen über den richtigen Weg gibt. Den Ausschlag für die Enterprise Library bei mir hat den Einsatz sowohl in Services, Web-Apss und WinForms  mit externer Konfiguration gegeben. Man kann auch einige andere der oben aufgeführten Konzepte damit verbinden.

Aus meiner Sicht ist es schon sehr Vorteilhaft, wenn man ein Konzept einsetzt, dass über den Applikationstyp hinweg eingesetzt werden kann. Zum anderen bietet die Enterprise Library durch das Konfigurationstool eine einfache grafische Möglichkeit der Konfiguration, so dass Administratoren nicht unbedingt in die Tiefen eingeführt werden müssen. Aber bestimmt für Administratoren, die auf Sicherheit sehr viel Wert legen ist die Möglichkeit den AzMan einzusetzen. Hierbei handelt es sich um eine Konfiguration die im Active Directory abgelegt werden kann. Der Rules-Provider kann so verwendet werden, dass Geschäftsvorfälle als Regeln dienen. Es gibt noch eine Reihe weiterer netter Vorteile, also durchaus mal nachlesen.

Nun genug zu den Vorteilen, hier noch ein Verwendungsbeispiel:

    1 <securityConfiguration defaultAuthorizationInstance="" defaultSecurityCacheInstance="">

    2   <authorizationProviders>

    3     <add type="Microsoft.Practices.EnterpriseLibrary.Security.AuthorizationRuleProvider, Microsoft.Practices.EnterpriseLibrary.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

    4       name="ProjectRuleProvider">

    5       <rules>

    8         <add expression="R:Administrators OR  R:Chef"

    9           name="CreateProject" />

   12         <add expression="R:Administrators OR  R:Chef"

   13           name="DeleteProject" />

   16         <add expression="R:Administrators OR  R:Chef"

   17           name="EditProject" />

   18       </rules>

   19     </add>

   20   </authorizationProviders>

   21 </securityConfiguration>

Wie zu sehen ist, sind die einzelnen Regeln nur eine Zeile lang. Aber keine Angst, die Syntax ist einfach und es gibt zu dem das Konfigurationstool in dem sich noch weitere Sachen einfach zusammen klicken und testen lassen. Zur Konfiguration gehört eine solche Abfrage (die Zeilennummer sind nicht von oben):

   26 IAuthorizationProvider auth = AuthorizationFactory.GetAuthorizationProvider("ProjectRuleProvider");

   27 return auth.Authorize(principal, "EditProject");

Bei Prinzipal handelt es sich um ein IPrinzipal-Objekt, dass zum Windows Nutzer oder zum Web-Nutzer gehört.

An dem Artikel habe ich jetzt eine Woche "gedoktort", dadurch ist er nicht besser geworden. :( MVC macht immer noch sehr viel Spass, aber nun muss ich auch mal an die Dokumentation ran, damit auch mal ein anderer das Zeug pflegen kann. Testfälle formulieren fehlt auch noch. :(

Sonntag, Juni 29, 2008

Der Juni Monat

Ist nun schon eine Weile her, dass ich rein privates geschrieben habe. Daher nun mal ein kleiner Rückblick auf den Juni.

Der Juni ist immer ein Familienfeiermonat, meine Schwägerin hatte Geburtstag, ich hatte Anfang des Monats Geburtstag und schließlich auch noch mein Neffe. (übrigens, ähnlich war auch der Mai) Um nicht zu viel zu feiern, haben wir wenigstens 2 Geburtstage zusammen gelegt (1Tag vor meinem eigentlichen Geburtstag gefeiert). Es war aber schon eine ganze Menge zu tun, Salate, Fleisch einkaufen und noch bisschen kochen. Das Wetter war richtig Klasse und sehr warm, so hat es bei meinem Bruder im Garten richtig Spass gemacht. Meinen Geburtstag habe ich anschließend mit meinen besten Freunden in Berlin verbracht. Wir (inkl. 2 kleinen Kindern) haben schön Kaffee gegessen und anschließend ging es noch etwas durch die Stadt und anschließend zum grillen. War angenehm mit den 4 den Geburtstag zu verbringen.

Im Juni war dieses Jahr außerdem noch ein 4 tägiges Firmenevent. Um alle aus der Arbeit herauszureißen ging es etwa 150km Richtung Norden in den Robinson Club. In den 4 Tagen habe ich massig gegessen, es ist unglaublich, was in den Bauch alles so rein geht. Das nette an dem Event war, dass auch die Familien mit-/nachkommen durften, bei mir gab es niemanden, aber war interessant auch mal die Kinder der Kollegen zu sehen. Insgesamt ein schönes Event, das mit einem Sportlichen und Erholsamen Sonntag echt schön war. Anschließend hatte ich auch noch 3 Tages Muskelkater.

Den restlichen Monat habe ich nur mit Arbeit verbracht, fast nur. Ich bin zur Zeit mal wieder auf Dienstreisen und treibe mich in Deutschland rum. Ein Tag war noch Kirschernte bei meinen Bruder, natürlich nicht nur ernten sondern auch entspannen im Garten.

Mit einigen Kollegen wollten wir schon seit Anfang Mai endlich mal wieder Volleyball spielen. Aber wie die Welt so spielt, wir haben es einfach nicht hinbekommen. Aber der nächste Anlauf folgt demnächst.

Ansonsten ist bei mir aber nix los. Viele Freunde habe ich schon lange nicht mehr gesehen und ....

Nun noch schön auf das Deutschland-Spiel heute Einstimmen, die Fan-Meile ist wohl schon geschlossen und ich muss morgen gegen 5:00 aufstehen. Wir werden endlich mal wieder Europameister!

UPDATE: Leider war es nix mit dem Titel, aber wenigstens war das Fußballspiel ansehnlich. Spanien war im ganzen Spiel besser, zumindest mit den klar besseren Chancen. Gratulation! Nun ins Bett!

Sonntag, Juni 22, 2008

Kämpfen mit ASP.NET MVC

Ich habe mir dieses Wochenende mal wieder etwas Zeit für ein Projekt genommen, in dem das ASP.NET MVC-Framework zum Einsatz kommt. Das Arbeiten mit dem MVC-Framework macht mir richtig Spass. Aber dennoch gibt es einige Hacken, die einem den nerv rauben.

MVC Routing Trouble

Allgemein ist es wichtig, dass Routen nach der Reihenfolge des hinzufügen ausgewertet werden. Meistens sollte dass auch mit der Lesereihenfolge in der Global.asax übereinstimmen.

Meine erste Baustelle war das Routing bzw. die Übergaben von Parametern. Mir ist noch nicht klar, warum die Standardwerte in der Global.asax nicht weitergegeben werden. Meine Nullable-Parameters haben erhalten nicht den Wert. Für das Problem habe ich 2 mögliche Lösungsszenarien, zum einen kann man die Parameter nicht Nullable machen oder innerhalb der Methode ggf. den Standardwert setzen.

Bei einer meiner Routen wurden einer der 1. Parameter nur ab und zu gesetzt. Zur besseren Darstellung mal ein Beispiel:

Die Route:

new Route("Projects/Results/{client}/{customerName}/{projectName}/{page}/{pageSize}/{order}", new MvcRouteHandler())
{
    Defaults = new RouteValueDictionary(new { controller = "Projects", action = "Results", client = ClientEnum.SDC, customerName = "", projectName = "", page = 1, pageSize = 20, order = "Default" }),
}

Signatur:

public void List(ClientEnum client, long page, long pageSize, string order)
{
    this.Results(client, null, null, page, pageSize, order);
}

Die Url:

http://localhost/Projects/search/customerPart/myProjectPart

In meinen Routen soll eigentlich die nicht mehr die Default.aspx aufgerufen werden, hierfür habe ich noch nicht die Lösung gefunden. Das Verhalten muss ich mir noch mal zu Gemüte führen und die Videos dazu anschauen, Vielleicht schon morgen im Zug.

SQL Paging Trouble

In der Anwendung greife ich auf einen Sql Server oder Sql Server Express mittels Paging zu, leider ist es kompliziert eine ordentliche Abfrage dafür zu erstellen. Normalerweise soll man für Paging-Abfragen RowNumber oder Rank benutzen, alternativ wird oft über Temp-Tables das Select erstellt. Allerdings ist das mit dem Sql Express nicht möglich, in dem Fall ist ein anderer Weg notwendig. Ich bin auf eine recht einfache Lösung im Internet gestoßen, dabei wird Top verwendet um die Ergebnismengen zu reduzieren. Das Grundprinzip für die Abfrae verwendet TOP mit unter Queries und könnte so aussehen:

SELECT top(ANZAHL) * 
FROM (
    SELECT top(OFFSET + ANZAHL) * 
    FROM MyTable
    ORDER BY xyz ASC
) as maxLimit 
ORDER BY xyz DESC

SETUP

Wer das MVC unter IIS6 benutzen will, sollte mal in die Blog-Einträge How to enable pretty urls with Asp.Net MVC and IIS6 oder Using ASP.NET MVC on IIS 6 without the .MVC Extension schauen. Besser kann man das nicht mehr beschreiben. Bei beiden Techniken wird Url-Rewriting eingesetzt. Ich würde aber den IIS7 empfehlen, da sind solche Umwege nicht mehr notwendig.

Donnerstag, Juni 19, 2008

Subversion 1.5 ist da!

Endlich, Endlich ist Subversion 1.5 verfügbar. Ich warte schon so lange auf das Release und nun kann man sich auf die vielen neuen Features freuen. Ursprünglich war das Release mal für Ende 2007 angekündigt, aber es zog sich dann. Ich finde es aber nicht so schlimm, dass es länger gebraucht hat, wenn die Qualität stimmt. Die wichtigsten Features sind das Merge Tracking und WebDav transparent wirte-through proxy, alle anderen Features sind in den Release notes aufgeführt.

Natürlich habe ich gleich den Download (Source) begonnen, allerdings gibt es aktuell noch keine Binaries, aber die kommen sicher in den nächsten Stunden. (Binaries download) Die 2. wichtige Komponente TortoiseSVN wird sicher ebenfalls in den nächsten Stunden oder Tagen released werden.

Ich werde bei uns das update unseres Versions-Servers in den nächsten Wochen durchführen. Es ist nur immer problematisch Sicherzustellen, dass es nicht zu Seiteneffekten mit anderen Anwendungen kommt. Ich kann es ehrlich gar nicht mehr erwarten damit zu arbeiten.

Noch ein ganz anderes Thema: Ich habe heute von einem Projekt gehört, dass Nunit für Team Builds verfügbar macht. Das Projekt nennt sich Nunit4MSBuild. Wer fleißig mit Nunit und TFS arbeitet, sollte sich das Projekt durchaus mal anschauen. Ich bin allerdings eher ein SVN-/TRAC-/TortoiseSVN-/NUnit-/CCNET-Fan.

Mittwoch, Juni 18, 2008

Endlich MCSD

Puuuh, ich habe es geschafft, seit dem 12.6. bin ich nun ein Microsoft Certified Solution Developer und auch ein Microsoft Certified Technology Specialist. Das interessante ist, dass der MCSD rückwirkend zum 5.3. ausgestellt wurde und der Technology Specialist zum 12.6.. (Transcript download)

Die BizTalk-Prüfung war nicht so schwer, wie meine anderen Prüfungen. Das Ergebnis von mir und auch einem Kollegen in der Prüfung, zeigt, dass es sehr einfach war. Man musste einige Sachen noch einmal Prüfen und nachlesen, aber dann sollt die Prüfung zu schaffen sein. Besonders hilfreich sind die prüfungsähnlichen Anwendungen.

Nun mal die nächsten Schritte überlegen. Es gibt leider so viele Prüfungen.

Dienstag, Juni 03, 2008

Die Beste Band der Welt

Vorgestern war Ärzte Konzert in der Kindl Bühne Wuhlheide, den Title "Beste Band" haben sie sich selber gegeben, das ist nicht meine Idee. Ich kann mir ein Ärzte Konzert außerhalb der Wuhlheide gar nicht vorstellen, dass ist einfach der beste Ort dafür. Heute bin ich dadurch etwas KO, aber mal von Anfang an.

Letztes Jahr habe ich 2 Karten für das Konzert gekauft, damals hatte ich noch eine Freundin und somit hatte ich eine Karte zuviel und wußte bis wenige Tage vorher nicht, wen ich mit hinnehme. Ich habe letztendlich einen Freund gefragt, den ich lange nicht gesehen hatte. Eine Freundin, die ich mehrfach gefragt hatte, wollte/konnte nicht so richtig. Ich fand es schon ziemlich schade, dass ich nicht mit meiner Freundin hingehen konnte. Der Zug war aber schon lange abgefahren.

Als Vorbereitung auf das Konzert hieß es zu erst mal ordentlich eincremen. Es war extrem heiß, aber besser als zu kühl. Nach dem eincremen ging es los zur Tram und wie kann es anders sein, ich schnappe die falsche und verträume das aussteigen. Aber alles kein Problem, in Berlin fährt nicht nur eine Bahn. Durch die Hitze sind alle total begeistert vom Tram fahren. Angekommen, hieß es anschließend noch auf den Rest warten um zum Eingang der Bühne zu marschieren. Wir waren nicht die einzigen die hinein wollten. ;)

Diesmal war das Konzert ohne Vor-Band, die Ärzte fingen direkt an. Direkt heißt dann auch mal Eben mit einer Verspätung von über 1h. Statt 19:00 nach 20:00. Aber das Konzert entschädigt einfach für alles und hat sehr viel Spass gemacht. Interessant waren die Leute im inneren Kreis, die massig Staubwolken produziert haben und das freiwillig. Manchmal sind massen wie die Bekloppten aufeinander zu gerannt. Ich bin kein Fan des "poken", daher für mich auch völlig unverständlich.

Die Staubwolken wurden am Ende für jeden nochmal ein Genuß, beim herausgehen wurden unweigerlich Unmengen davon provoziert und jeder mußte es einatmen. Die Weg in der Wuhlheide sollten mal mit Licht ausgestattet werden. Total finstere Wege die zum Glück von vielen Handy-Displays ausgeleuchtet wurden. Ich habe mich an Ausgang entschlossen den langen Weg zur Tram zu nehmen, was ein längerer Fußmarsch war als ich dachte. Am Ende war ich gegen 00:30 zu Hause, schnell noch geduscht und versuchen etwas schlaf zu finden. Übrigens, ich war anschließend seit 5:45 unterwegs Richtung Kassel und dort war dann arbeiten bis 19:00.

Den Post hatte ich eigentlich schon gestern geschrieben, aber nicht mehr online published.

Sonntag, Juni 01, 2008

Fehlende Zeit, aber kurz ...

Ich habe zur Zeit nicht so richtig einen Schimmer, über was ich am besten bloggen soll. Gibt einiges an interessanten Themen, momentan komme ich nicht so richtig dazu mit den neuen Sachen zu spielen. Ich bereite mich mal wieder auf eine Prüfung vor.

Ein andere Grund für meine mangelnde Zeit ist das Schauen von Web- oder Screencasts von David Hayden. Ich habe nun schon beim 2. Projekt mit der Web Client Software Factory zu tun. Das Konzept gefällt mir recht gut, wobei ich MVC noch ein ganzes Stück besser finde. Mittels der WCSF kann man super moduare Webseiten erstellen und pflegen. Außerdem ist es (zumindest nach den bisherigen Erfahrungen) relativ einfach die Logik auf einen Smart Cient (SCSF) zu portieren oder sogar zu nutzen. Ich glaube, ich würde meine ASP.NET Projekte nie wieder ohne WCSF oder MVC erstellen.

In einem meiner aktuellen Projekte versuche ich Kollegen in die Lage zu versetzen, mittel .NET die Vision der IT umzusetzen. Dabei muss ein Framework realisiert werden und die Kollegen fit in .NET werden. Einzige umgesetzt. Nach meinen bisherigen Erfahrungen ist das die schlechteste Entscheidung. Ich finde, es gibt so viele Mängel mit der Sprache:

  • Aufblähen des Codes (Generics IList(of Object), Properties)
  • Kein Refactoring Support
  • Namespaces verhalten sich etwas anders (Ordner)
  • Attribute müssen auf der gleichen Zeile sein, wie deren Bezugspunkt
  • Man muss überlegen ob nun Inheritance (Klasse) oder Implements (Interface) eingesetzt werden soll.
  • Man muss dem Projekt erst sagen, dass es sicher sein soll
  • Formatierungen sind extrem wichtig
  • Case-Insensitiv
  • IL-Code wird größer ->langsamer?
  • Arrays sind schwerer zu erkennen, da sie nur mit Klammer () merkiert werden, ebenso die Indexer
  • kein Using-Statement!
  • Übergabe von Parametern ist verwirrend (Function MyFunc(ByVal o as Object) as Boolean)

Hier noch ein Link zum Thema VB/C# Visual Basic .NET & C# Die Qual der Wahl?.

Ich habe jetzt schon auf 3 oder 4 Rechnern versucht das Visual Studio 2008 SP1 zu installieren, leider immer ohne Erfolg. Dabei war einer wirklich nur rudimentär installiert. Das Framework 3.5 war dagegen problemlos zu installieren und ich habe damit wenig sorgen.

Wir haben in 2 Wochen ein Firmenevent (SD&C/CapeVision) über 4 Tage, ich bin mal gespannt, was da so alles passiert. Am Anfang habe ich überlegt, ob ich da wirklich mit hin will. Irgendwie bringt jeder Freundin oder Familie mit. Irgendwie werde ich das schon überstehen. Zurück aus dem Firmenevent und gleich weiter zum Projekt Reisen, so wird es laufen. Wobei es sich wenig von den letzten Wochen unterscheidet. Momentan hört man bei fast allen Kollegen und Leuten in der IT das gleiche, es gibt zu viel zu zu tun und alle sind momentan mit Projekten überfüllt.

Übrigens das ist mein erster Post mit Live Writer, ich bin darauf gespannt, was wirklich auf der Seite erscheint. Das Tool ist wirklich sehr nett und es macht Spass damit zu schreiben. Vorher habe ich die Posts als Emails in Outlook erstellt, das war schon nett, aber nicht der Renner.

Sonntag, Mai 18, 2008

MSBuild to ccnet updated

Ich habe heute mir endlich mal die Zeit genommen einige gravierenden Fehler im MSBuild-Logger gefixt, den ich angepasste hatte. Außerdem hatte das Xslt einige bösen Macken. Ich habe die aktualisierte Version wieder auf meine HP geladen. Der Link aus meinem alten Artikel zu dem Thema ist gleich geblieben, soll schließlich niemand das fehlerhafte Gelumpe laden.

Mit der neuen Version wird das MSBuild-File nun korrekt in die Build-Logs gemerged und das Xslt wertet die Ergebnisse korrekt aus. Es ist ein Logger für 2.0 und 3.5 enthalten.

Samstag, Mai 10, 2008

Arbeit / Stress / Unzufriedenheit

Ich habe schon lange nichts mehr Privates geschrieben. Ein bisschen, weil ich nix neues habe, aber auch weil ich mir keine Zeit nehmen wollte oder konnte für das Bloggen. Hier nur einige wenige Informationen was alles so in den letzten Wochen passiert ist.

Eigentlich habe ich gerade 2 sehr interessante Projekte, die mir zumeist Spaß machen weil die Technologie einfach spannend ist. Leider laufen auch noch „Projektleichen“ nebenher, unteranderem auch eins, bei dem ich wieder mehr machen soll bis Ende des Jahres. L Durch die kleinen Leichen und die eigentlich nicht vorhandene Zeit, ist auch eine Menge Stress da. Ein anderer Kollege hat leider genau die gleichen Probleme und den gleichen Stress, in der letzten Woche sogar noch einiges mehr. Aber momentan ist das organisatorische das Schlimmste! Wenn ich mal in der Lage fühle Details dazu aufzuschreiben, dann wird es vielleicht auch verständlich. Momentan kotzt mich das richtig an, ich werde vielleicht nächste Woche mit einem meiner Chefs reden müssen. Ein bisschen Spannung mit einem Externen habe ich in einem Projekt, wo „alte“ auf „neue“ Philosophie trifft und die Technologieverständnisse schon deutlich unterschiedlich sind.

Privat ist mit mir immer noch gar nichts los, naja vielleicht fast gar nichts. In den letzten 2 Wochen war ich auf 2 Kindergeburtstagen (2 und 5), die sehr schön waren. Ich habe auch gelernt, dass man bei 6 5-jährigen Mädels am besten viel Ohropax dabei haben sollte. Ich habe mich außerdem mit 2 Ex-Kolleginnen mit deren Kindern getroffen. Wir haben uns einfach einen ruhigen und angenehmen Tag im Friedrichshain gemacht. Leider vermisse ich immer noch meine Ex-Freundin sehr, sie fehlt mir halt. Ich frage mich öfters, was sie wohl macht, wie es ihr geht. Im Frühling ist es richtig kacke, ich sehe überall nur Verliebte, Paare, Familien … Aber ich habe zum Glück soviel Arbeit, dass ich wenig Zeit finde (oder auch nehme), dass ich wenig Menschen sehe.

Nun ist erst mal Pfingsten ein bisschen Leichen wegräumen versuchen, Eltern besuchen, vielleicht Freunde treffen und noch Brüderchen besuchen. Es gibt einiges zu tun, also bis bald und schöne Feiertage.

Sonntag, Mai 04, 2008

MSBuild directory listing / batching

Immer wenn ich mit den Standard Boardmitteln von MSBuild versuche alle Verzeichnisse aufzulisten, so könnte ich verzweifeln. Immer die Verzeichnisse mittels DIR-Befehl in eine Datei schreiben und anschließend mittels ReadLine-Task auslesen.

    1   <Exec Command = "dir &quot; $(RefItemsPath) &quot; /b /A:D /S> &quot; $(SourceFolder)\refFolders.bbb &quot; " Condition = "'$(RefItemsPath)'!='' and '$(ReferencePath)'==''" />

    2   <Exec Command = "echo $(RefItemsPath)>> &quot; $(SourceFolder)\refFolders.bbb &quot; " Condition = "'$(RefItemsPath)'!='' and '$(ReferencePath)'==''" />

    3   <CreateItem Include = "$(SourceFolder)\refFolders.bbb" Condition = "'$(ReferenceFolder)'!='' and '$(ReferencePath)'==''" >

    4       <Output TaskParameter = "Include" ItemName = "refs" />

    5   </CreateItem>

    6   <ReadLinesFromFile File = "@(refs)" Condition = "'$(ReferenceFolder)'!='' and '$(ReferencePath)'==''" >

    7       <Output TaskParameter = "Lines" PropertyName = "readedLines" />

     8   </ReadLinesFromFile>

Aus meiner Sicht nicht wirklich schön, wenn man nur Verzeichnisse haben möchte, daher habe ich (endlich) einen neuen Task erstellt, da ich immer noch keinen in den CommunityTools gefunden habe. Der Task ist so super einfach, erspart mir aber immer einige Hacks, außerdem werden Build-Files verständlicher.

    3   using System;

    4   using System . Collections . Generic;

    5   using System . Text;

    6   using Microsoft . Build . Framework;

    7   using Microsoft . Build . Utilities;

    8   using System . Xml;

    9   using System . IO;

   10   using System . Collections;

   11   using System . Diagnostics;

   12  

   13   namespace DE . CapeVision . MSBuild . Tasks

   14  {

   15      public class DiretoryListingTask : Task

   16      {

   17          private string baseFolder;

   18          public bool IncludeSubFolders { get ; set ; }

   19          public string SearchPattern { get ; set ; }

   20          [ Output ]

   21          public string [] FolderContent { get ; set ; }

   22          [ Required ]

   23          public string BaseFolder

   24          {

   25              get { return baseFolder; }

   26              set { baseFolder = value ; }

   27          }

   28          public override bool Execute()

   29          {

   30   //#if(DEBUG)

   31   //            Debugger.Launch();

   32   //#endif

   33              if ( ! Directory . Exists( this . baseFolder))

   34                  return true ;

   35              string [] folders = Directory . GetDirectories(

   36                  this . baseFolder

   37                  , string . IsNullOrEmpty( this . SearchPattern) ? "*" : this . SearchPattern

   38                  , this . IncludeSubFolders ? SearchOption . AllDirectories : SearchOption . TopDirectoryOnly

   39                  );

   40              this . FolderContent = folders;

   41              return true ;

   42          }

   43      }

   44  }

Das 2. Thema, was ich in dem Zuge angehen wollte, war das Batching mal zu überprüfen. MsBuild ist in der Lage Befehle die mit TaskItems (array) aufgerufen werden zu parallelisieren, bzw. den Befehl immer wieder auszuführen.  Das Batching wird mittels des „%“-Operators angekündigt und alles weitere und ggf. Typenkonvertierungen für den Task macht MsBuild. Man kann das sehr gut mit einem einfachen Message-Task testen und nachstellen. Leider hatte meine Ausgabe nie wirklich geklappt, immer wenn 2 Listen verknüpft werden sollten, so kam folgendes raus:

  iis () --- BROWSER_VIEW
  iis () --- LOG_VIEW   iis () --- FILE_VIEW
  iis () --- CHANGESET_VIEW
  iis () --- TICKET_ADMIN
  iis () --- MILESTONE_ADMIN
  iis () --- ROADMAP_ADMIN
  iis () --- REPORT_ADMIN
  iis () --- WIKI_ADMIN
  iis () --- TIMELINE_VIEW
  iis () --- SEARCH_VIEW
  iis () --- TRAC_ADMIN
  iis (D:\Cache\Msdn\AdvancedBasics) ---
  iis (D:\Cache\Msdn\ContinuousIntegration) ---
  iis (D:\Cache\Msdn\DataPoints) ---
  iis (D:\Cache\Msdn\DependencyInjection) ---
  iis (D:\Cache\Msdn\ExtremeASPNET) ---
  iis (D:\Cache\Msdn\Foundations) ---
  iis (D:\Cache\Msdn\MVCFramework) ---
  iis (D:\Cache\Msdn\OfficeSpace) ---
  iis (D:\Cache\Msdn\TestRun) ---

   12   <Message Importance = "high" Text = "iis (%(folder.FullPath)) --- %(permission.Identity)" ></Message>

Nach etwas recherche fand ich auch ähnlich Probleme und irgendwo auch eine Lösung (Gute Erklärung http://blogs.msdn.com/aaronhallberg/archive/2006/09/05/msbuild-batching-generating-a-cross-product.aspx). Man muss die Ausgabe mittels MsBuild-Task in 2 Targets teilen, so dass der MsBuild-Tasks als Batch durchlaufen wird und anschließend der 2. Taks. Mein Skript sieht komplett so aus:

    1   <?xml version = "1.0" encoding = "utf-8" ?>

     2   <Project DefaultTargets = "Test" xmlns = "http://schemas.microsoft.com/developer/msbuild/2003" >

    3     <UsingTask AssemblyFile = "DE.CapeVision.MSBuild.Tasks.dll" TaskName = "DE.CapeVision.MSBuild.Tasks.DiretoryListingTask" />

    4     <ItemGroup>

    5       <permission Include = "BROWSER_VIEW;LOG_VIEW;FILE_VIEW;CHANGESET_VIEW;TICKET_ADMIN;MILESTONE_ADMIN;ROADMAP_ADMIN;REPORT_ADMIN;WIKI_ADMIN;TIMELINE_VIEW;SEARCH_VIEW;TRAC_ADMIN" />

    6     </ItemGroup>

    7     <Target Name = "Test" >

    8       <DiretoryListingTask BaseFolder = "D:\Cache\Msdn" >

    9         <Output TaskParameter = "FolderContent" ItemName = "folder" />

   10       </DiretoryListingTask>

   11       <Message Importance = "high" Text = "iis (%(folder.FullPath)) --- %(permission.Identity)" ></Message>

   12       <Message Importance = "high" Text = "second:" ></Message>

   13       <MSBuild Projects = "$(MSBuildProjectFile)" Targets = "InternalAddPermission" Properties = "Folder=%(folder.FullPath)" />

   14     </Target>

   15     <Target Name = "InternalAddPermission" >

   16       <Message Importance = "high" Text = "third:" ></Message>

   17       <Message Importance = "high" Text = "trac-admin $(folder) permission add tester %(permission.Identity)" ></Message>

    18     </Target>

   19   </Project>

Gebraucht habe ich beides um die Berechtigungen an den TRAC-Repositories anzupassen, die wir haben. (Dann natürlich nicht mit Message-Task ;))

Dienstag, April 29, 2008

Workflow Foundation Erkenntnisse (Hosting TODOs)

Ich habe die letzten Wochen Beratung unter anderem zum Thema Windows Workflow Foundation gemacht. Dabei sollte das Workflow Designer-Control für die Anpassung der WFs genutzt werden. Im Großen und Ganzen ein super Tool und sehr interessant und mächtig, allerdings hatte die Demo, die wir benutzt haben einige ärgerliche Probleme. Eine Version mit ausführlicher Anleitung kann beim Artikel „Windows Workflow Foundation: Everything About Re-Hosting the Workflow Designer“ heruntergeladen werden.

1.       Bei der Verwendung von Umlauten, bekommt die Anwendung arge Probleme, hier müssen einige Methoden in der Loader-Klasse angepasst werden.

In der Methode PerformFlush sollte das Encoding für den Writer gesetzt werden, hier der Auszug:

   244   if (rootActivity != null )

  245  {

  246      using ( XmlWriter xmlWriter = XmlWriter . Create(

  247          this . xoml

  248          , new XmlWriterSettings () { Encoding = Encoding . UTF8, OmitXmlDeclaration = false }))

  249      {

  250          WorkflowMarkupSerializer xomlSerializer = new WorkflowMarkupSerializer ();

  251          xomlSerializer . Serialize(xmlWriter, rootActivity);

   252      }

  253  }

Außerdem habe ich noch die Methode PerformLoad angepasst, hier der Auszug:

  193   Activity rootActivity = null ;

  194   using ( StreamReader sr = new StreamReader ( this . xoml, Encoding . UTF8, true ))

  195  {

  196      using ( XmlReader reader = XmlReader . Create( this . xoml))

  197      {

  198          WorkflowMarkupSerializer xomlSerializer = new WorkflowMarkupSerializer ();

  199          try

  200          {

  201              rootActivity = xomlSerializer . Deserialize(serializationManager, reader) as Activity ;

  202          }

  203          catch ( Exception ex)

  204          {

  205              Trace . WriteLine(ex);

  206          }

  207      }

  208      if (rootActivity != null && designerHost != null )

  209      {

  210          AddObjectGraphToDesignerHost(designerHost, rootActivity);

  211          Type companionType = rootActivity . GetValue( WorkflowMarkupSerializer . XClassProperty) as Type ;

  212          if (companionType != null )

  213              SetBaseComponentClassName(companionType . FullName);

   214      }

  215  }

Bei der letzen Änderung bin ich mir nicht sicher, aber dennoch der Hinweis. Die Methode GetRootActivity in der Helper-Klasse könnte ebenfalls zu Problemen führen, auch hier eine kleine Anpassung:

   52   internal static Activity GetRootActivity( string fileName, IServiceProvider serviceProvider)

   53  {

   54      Activity rootActivity = null ;

   55      using ( StreamReader sr = new StreamReader (fileName, Encoding . UTF8, true ))

   56      {

   57          using ( XmlReader reader = XmlReader . Create(sr

   58              , new XmlReaderSettings () { XmlResolver = new System . Xml . XmlUrlResolver () }))

   59          {

   60              WorkflowMarkupSerializer xomlSerializer = new WorkflowMarkupSerializer ();

   61              DesignerSerializationManager ser = new DesignerSerializationManager (serviceProvider);

   62              ser . CreateSession();

   63              rootActivity = xomlSerializer . Deserialize(ser, reader) as Activity ;

    64          }

   65      }

   66      return rootActivity;

   67  }

2.       Die Nutzung einer Konfigurationsdatei für die möglichen Controls finde ich mehr als ungünstig, hier sollte man die AddToolboxEntries-Methode der ToolboxService-Klasse anpassen, in dem alle Assemblies (aus dem aktuellen und dessen Unterverzeichnis) durchlaufen werden und geprüft wird, ob Klassen mit der Basis-Klasse „Activity“ vorhanden sind. Außerdem nicht vergessen die Standard Activities zu laden. Das Durchlaufen der Assemblies im Ordner ist in den Code-Zeilen nicht vorhanden.

   546   Assembly assembly = typeof (System . Workflow . Activities . ActiveDirectoryRole ) . Assembly;

  547  LoopAssemblyTypes(lb, assembly);

  548  assembly = typeof ( TerminateActivity ) . Assembly;

  549  LoopAssemblyTypes(lb, assembly);

  550   //VS2008 required

  551  assembly = typeof ( SendActivity ) . Assembly;

   552  LoopAssemblyTypes(lb, assembly);

Hier die aufgerufene Methode (Außerdem wird der Assembly-Name als Titel verwendet):

   555   private void LoopAssemblyTypes( ListBox lb, Assembly assembly)

  556  {

  557      Type [] assemblyTypes = assembly . GetTypes();

  558      bool groupSet = false ;

  559      foreach ( Type assemblyType in assemblyTypes)

  560      {

  561          if (assemblyType . IsAbstract || ! assemblyType . IsClass || ! assemblyType . IsSubclassOf( typeof ( Activity )))

  562          {

  563              Debug . WriteLine( string . Format( "Class '{0}' not a valid Activity!" , assemblyType . FullName), "ToolboxService.LoopAssemblyTypes" );

  564          }

  565          else

  566          {

  567              Trace . WriteLine( string . Format( "Loading '{0}' into toolbox!" , assemblyType . FullName), "ToolboxService.LoopAssemblyTypes" );

  568              SelfHostToolboxItem item = new SelfHostToolboxItem (assemblyType, assemblyType . FullName);

  569              tbItems . Add(item);

  570              if ( ! groupSet)

  571              {

  572                  lb . Items . Add(assembly . FullName . Split( new char [] { ',' }, 2 )[ 0 ]);

  573                  groupSet = true ;

  574              }

  575              lb . Items . Add(item);

   576          }

  577      }

  578  }

3.       Übergeben der Typen an den TypeProvider dazu die Methode Initialize des Loaders erweitern. (Wird der TypeProvider nicht erweitert mit den Assemblies und dieser auch überall übergeben, so kann das der Grund sein, warum definierte Workflows nicht geladen werden. (Es gibt auch keine Fehlermeldung)

  102   foreach ( SelfHostToolboxItem toolboxItem in toolbox . Items)

   103  {

  104      typeProvider . AddAssembly(toolboxItem . Compon entClass . Assembly);

  105  }

  106  host . AddService( typeof ( ITypeProvider ), typeProvider, true );

4.       Außerdem habe ich einen ToolTip in die Anwendung integriert, der das DescriptionAttribute auswertet und ausgibt. Dazu muss kräftig in die ToolboxService-Klasse eingegriffen werden.

Die erste Änderung in der OnListBoxMouseMove

  692   int currentIndex = listBox . IndexFromPoint( new Point (e . X, e . Y));

  693   if (currentIndex >= 0 )

  694  {

  695      SelfHostToolboxItem item = listBox . Items[currentIndex] as SelfHostToolboxItem ;

  696      if (item == null || string . IsNullOrEmpty(item . Tooltip))

  697      {

  698          this . tooltip = null ;

  699  

  700          currentHighlightItemIndex = Int32 . MinValue;

  701      }

  702      else

  703      {

  704          {

  705              // ToolTip setzen

  706              if (currentHighlightItemIndex != currentIndex)

  707              {

  708                  this . tooltip = item . Tooltip;

  709                  currentHighlightItemIndex = currentIndex;

  710                  this . tp . Show(tooltip, this , new Point (e . X, e . Y));

   711              }

  712          }

  713      }

  714  }

Auf Klassenebene sind die 3 Fields definiert

  717   ToolTip tp = new ToolTip ();

  718   string tooltip;

   719   int currentHighlightItemIndex;

Außer den beschriebenen Anpassungen kann man sich durchaus auch noch kräftig in der OnDrawItem-Methode austoben, hier kann man die Anzeige den eigenen Wünschen anpassen.

Man muss/sollte in seinen Workflows-Assemblies unbedingt das XmlnsDefinitionAttribute verwenden und einen für sich eindeutigen Namespace definieren. Mittels des Attributes wird die Auflösung der Namepsaces im Xml (Xoml-Workflow) und der CLR gesteuert. Kommt es zu Problemen beim Laden der Workflows im Designer, obwohl der TypeProvider überall mitgegeben wird, so kann man sich mit einem Trick weiterhelfen. Das XmlnsDefinitionAttribute wird auf den CLR-Namespace gesetzt, zum Beispiel xmlns:cv="clr-namespace:DE.CapeVision.Workflow.MainActivities;assembly=CapeVisionMainActivityLib". Viel mehr Details dazu unter XAML Namespaces and Namespace Mapping.