Dienstag, August 12, 2008

.NET Framework SP1 und Visual Studio Service Pack 1 Released

Endlich gibt es die beiden Service Packs zum download. Alle Informationen sind sehr gut über diese Link verfügbar Visual Studio 2008 SP1 and .NET 3.5 SP1 Release to Manufacturing.

Der Download ist wirklich gewaltig, Visual Studio SP1 sind mal eben 800MB dazu kommt noch das TFS Service Pack 1 und .NET Framework.

Ich werde in den nächsten Tage meine aktuellen VMs anpassen und alle notwendigen Installationen durchführen.

Schnell noch einige Snapshots machen! ...

Sonntag, August 03, 2008

Windows PerformanceCounter Komponente

In der letzten Woche habe ich mich gleich in 2 Projekten mit Windows Performance Countern beschäftigt. Das war nicht das erste mal, so dass mir dabei auch aufgefallen ist, dass in einer früheren Implementierung von mir eine Reihe von Fehlern waren.

Allgemeines

Mittels Performance Countern kann man sehr gut Leistungsdaten eines Server/Anwendung erfassen und protokollieren. Man kann damit zum Beispiel die Anzahl der Aufrufe auf einen Dienst/Webservice/Layer verfolgen oder die Laufzeit. Es gibt wahnsinnig viele interessante Indikatoren in einer Anwendung. Performance Counter sind ebenso extrem wichtig, wenn mit Monitoring Software eine Anwendung überwacht wird. Sehr interessant für die Verwaltung und Administration von Servern ist auch die Möglichkeit Alarme zu definieren. Einen sehr netten Artikel zum Thema Andrew Z. Tabona Windows 2003 Performance Counter.

Coding

Ich wollte eine Komponente erstellen, die von jeder beliebigen Klasse und große Kenntnisse genutzt werden kann. Leider ist es nicht möglich einfach zu sagen, dass man jetzt in Ziel X etwas ausgeben will. Performance Counter müssen vor der Benutzung angelegt werden, zu dem sind für erhöhte Rechte notwendig. Aber Schritt für Schritt:

Prinzipiell ist das Schreiben von Performance-Counter-Werten sehr einfach.

    1 PerformanceCounter c = new PerformanceCounter("myCategory", "CountCalls",false)

    2 c.Increment();

    3 c.Decrement();

    4 c.IncrementBy(5);

Allerdings muss vorher sichergestellt sein, dass der Counter existiert und zugegriffen werden kann. Da Counter immer auch einer Category zugeordnet sind, muss diese ebenso angegeben werden.

    1 CounterCreationDataCollection creation = new CounterCreationDataCollection();

    2 creation.Add(new CounterCreationData(c.CounterName, c.CounterHelp, c.CounterType));

    3 PerformanceCounterCategory.Create(category, "", PerformanceCounterCategoryType.Unknown, creation);

Beim Anlegen eines Counters hat man immer die Wahl, ob ein Multi-Instanz Counter oder ein Single-Instanz Counter angelegt wird. Die Idee dahinter ist relativ simpel. Jede Applikation/Komponente kann bei einem Multi-Instanz Counter verfolgt werden, entsprechend werden für alle Einträge erzeugt. Bei einem Single Instanz Counter werden alle Log-Informationen für den gesamten Server protokolliert. Eine Differenzierung wäre in dem Fall nicht möglich, aber auch nicht immer sinnvoll.

Wird ein Counter vom Typ "PerformanceCounterType.AverageTimer32" angelegt, so muss zusätzlich auch der BaseCounter "PerformanceCounterType.AverageBase" angelegt werden, da es sonst zu Fehler kommt. Bei der Verwendung des Counters soll man die StartZeit in Ticks - der Endzeit der Aktion angeben, ehrlich gesagt, ich habe es noch nicht 100% verifiziert, ob der Counter richtig funktioniert. Ich nutze am meisten den einfachen Zähler oder Aufrufe pro Sekunde.

ABER, ist eine Category angelegt, kann man keine Counter hinzufügen, zumindest nicht mit Managed Code, ich kenne auch keinen anderen Weg, evtl. ist dies über Registry-Manipulation möglich. Aus diesem Grund muss die Category immer gelöscht werden, wenn ein Counter zu der Category hinzugefügt wird. Anschließend werden alle Counter erneut hinzugefügt.

Nach soviel Kleinigkeiten, hier mal ein ganzer Code-Block:

   63 private static readonly IDictionary<string, IDictionary<string, PerformanceCounter>> categoryDict =

   64     new Dictionary<string, IDictionary<string, PerformanceCounter>>();

   65 private PerformanceCounter GetCounter(string category, string counterName, bool useInstance, PerformanceCounterType type)

   66 {

   67     if (string.IsNullOrEmpty(category) || string.IsNullOrEmpty(counterName))

   68         return null;

   69     try

   70     {

   71         return EnsureWithCategory(category, counterName, useInstance, type, false);

   72     }

   73     catch (Exception ex)

   74     {

   75         System.Diagnostics.Trace.WriteLine("Get counter failed! " + ex.ToString(), "EnterpriseLoggingService.GetCounter");

   76     }

   77     return null;

   78 }

   79 private static ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

   80 

   81 private PerformanceCounter EnsureWithCategory(string category, string counterName, bool useInstance, PerformanceCounterType type, bool deep)

   82 {

   83     PerformanceCounter counter;

   84     cacheLock.EnterUpgradeableReadLock();

   85     try

   86     {

   87         if (categoryDict.ContainsKey(category))

   88         {

   89             IDictionary<string, PerformanceCounter> perf = categoryDict[category];

   90             return EnsureCounter(category, counterName, useInstance, type, perf);

   91         }

   92         else

   93         {

   94             if (deep)

   95                 return null;

   96             return EnsureCounter(category, counterName, useInstance, type, null);

   97 

   98         }

   99 

  100     }

  101     finally

  102     {

  103         cacheLock.ExitUpgradeableReadLock();

  104     }

  105 }

  106 

  107 private static PerformanceCounter EnsureCounter(string category, string counterName, bool useInstance, PerformanceCounterType type, IDictionary<string, PerformanceCounter> perf)

  108 {

  109     PerformanceCounter counter;

  110     if (perf != null && perf.ContainsKey(counterName))

  111     {

  112         counter = perf[counterName];

  113     }

  114     else

  115     {

  116         cacheLock.EnterWriteLock();

  117         try

  118         {

  119             bool exists = false;

  120             Dictionary<string, PerformanceCounter> counterDict;

  121             CounterCreationDataCollection creation = new CounterCreationDataCollection();

  122             if (PerformanceCounterCategory.Exists(category))

  123             {

  124                 exists = PerformanceCounterCategory.CounterExists(counterName, category);

  125                 PerformanceCounterCategory pc = new PerformanceCounterCategory(category);

  126                 PerformanceCounter[] counters;

  127                 if (pc.CategoryType == PerformanceCounterCategoryType.MultiInstance)

  128                     counters = pc.GetCounters(AppDomain.CurrentDomain.FriendlyName);

  129                 else

  130                     counters = pc.GetCounters();

  131 

  132                 foreach (PerformanceCounter c in counters)

  133                 {

  134                     creation.Add(new CounterCreationData(c.CounterName, c.CounterHelp, c.CounterType));

  135                 }

  136                 if (!exists)

  137                 {

  138                     if (perf != null)

  139                     {

  140                         foreach (PerformanceCounter p in perf.Values)

  141                         {

  142                             p.Dispose();

  143                         }

  144                         perf.Clear();

  145                         perf = null;

  146                     }

  147                     PerformanceCounterCategory.Delete(category);

  148                 }

  149             }

  150             if (!exists)

  151             {

  152                 creation.Add(new CounterCreationData(counterName, "", type));

  153                 if (type == PerformanceCounterType.AverageTimer32)

  154                     creation.Add(new CounterCreationData(counterName + "Base", "", PerformanceCounterType.AverageBase));

  155                 PerformanceCounterCategory.Create(category, "", PerformanceCounterCategoryType.Unknown, creation);

  156             }

  157             counterDict = new Dictionary<string, PerformanceCounter>(creation.Count);

  158             foreach (CounterCreationData ccd in creation)

  159             {

  160                 counter = new PerformanceCounter(category, ccd.CounterName, useInstance ? AppDomain.CurrentDomain.FriendlyName : "", false);

  161                 counterDict.Add(ccd.CounterName, counter);

  162             }

  163             counter = counterDict[counterName];

  164             perf = counterDict;

  165             if (categoryDict.ContainsKey(category))

  166                 categoryDict[category] = counterDict;

  167             else

  168                 categoryDict.Add(category, counterDict);

  169         }

  170         finally

  171         {

  172             cacheLock.ExitWriteLock();

  173         }

  174     }

  175     return counter;

  176 }

  177 public void NumberCounter(string category, string counterName, bool useInstance, int incrementValue)

  178 {

  179     PerformanceCounter c = GetCounter(category, counterName, useInstance, PerformanceCounterType.NumberOfItems32);

  180     if (c != null)

  181     {

  182         if (incrementValue == 1)

  183             c.Increment();

  184         else

  185         {

  186             if (incrementValue == -1)

  187                 c.Decrement();

  188             else

  189                 c.IncrementBy(incrementValue);

  190         }

  191     }

  192 }

  193 

  194 public void RateCounter(string category, string counterName, bool useInstance, int incrementValue)

  195 {

  196     PerformanceCounter c = GetCounter(category, counterName, useInstance, PerformanceCounterType.RateOfCountsPerSecond32);

  197     if (c != null)

  198     {

  199         if (incrementValue == 1)

  200             c.Increment();

  201         else

  202         {

  203             if (incrementValue == -1)

  204                 c.Decrement();

  205             else

  206                 c.IncrementBy(incrementValue);

  207         }

  208     }

  209 }

  210 

  211 public void AvarageCounter(string category, string counterName, bool useInstance, long incrementValue)

  212 {

  213     PerformanceCounter c = GetCounter(category, counterName, useInstance, PerformanceCounterType.AverageTimer32);

  214     if (c != null)

  215     {

  216         PerformanceCounter cBase = GetCounter(category, counterName + "Base", useInstance, PerformanceCounterType.AverageTimer32);

  217         c.IncrementBy(incrementValue);

  218         cBase.Increment();

  219     }

  220 }

  221 

  222 public void RemoveCurrentCounterInstance(string category, string counterName)

  223 {

  224     cacheLock.EnterReadLock();

  225     try

  226     {

  227         if (categoryDict.ContainsKey(category) && categoryDict[category].ContainsKey(counterName))

  228         {

  229             if (PerformanceCounterCategory.CounterExists(counterName, category) && PerformanceCounterCategory.InstanceExists(AppDomain.CurrentDomain.FriendlyName, category))

  230             {

  231                 PerformanceCounter c = categoryDict[category][counterName];

  232                 c.InstanceName = AppDomain.CurrentDomain.FriendlyName;

  233                 c.RemoveInstance();

  234             }

  235 

  236         }

  237     }

  238     finally

  239     {

  240         cacheLock.ExitReadLock();

  241     }

  242 }

  243 

  244 public void SetNumberCounterValue(string category, string counterName, bool useInstance, int newValue)

  245 {

  246     PerformanceCounter c = GetCounter(category, counterName, useInstance, PerformanceCounterType.NumberOfItems32);

  247     if (c != null)

  248     {

  249         if (useInstance)

  250             c.InstanceName = AppDomain.CurrentDomain.FriendlyName;

  251         c.RawValue = newValue;

  252     }

  253 }

  254 public void SetAvarageCounterValue(string category, string counterName, bool useInstance, long newValue)

  255 {

  256     PerformanceCounter c = GetCounter(category, counterName, useInstance, PerformanceCounterType.AverageTimer32);

  257     if (c != null)

  258     {

  259         PerformanceCounter cBase = GetCounter(category, counterName + "Base", useInstance, PerformanceCounterType.AverageTimer32);

  260         if (useInstance)

  261             c.InstanceName = AppDomain.CurrentDomain.FriendlyName;

  262         c.RawValue = newValue;

  263         if (newValue == 0)

  264             c.RawValue = 0;

  265     }

  266 }

  267 public void SetRateCounterValue(string category, string counterName, bool useInstance, int newValue)

  268 {

  269     PerformanceCounter c = GetCounter(category, counterName, useInstance, PerformanceCounterType.RateOfCountsPerSecond32);

  270     if (c != null)

  271     {

  272         if (useInstance)

  273             c.InstanceName = AppDomain.CurrentDomain.FriendlyName;

  274         c.RawValue = newValue;

  275     }

  276 }

Deployment

Beim Deployment sollte man die Empfehlung von MS usw. berücksichtigen und bei der Installation alle Counter anlegen. Counter zu nutzen ist nicht sehr "teuer", allerdings ist es sehr aufwendig und langsam, wenn Counter angelegt werden. Zu mal immer alle Counter inklusive Category gelöscht wird.

Der einfachste Weg bei der Installation ist es eine Installer-Klasse zu erstellen, die alles Counter der Anwendung anlegt. hierfür ist ein bisschen Coding notwendig und anschließend einfach "installutil" aufrufen.

Technorati-Tags: ,,,

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 ;))