Dienstag, Dezember 30, 2008

Happy New Year

Ich habe meinen Urlaub gut überstanden, auch die vielen Stunden Flug. Es war wirklich sehr erhohlsam, manchmal etwas langweilig und merkwürdig so alleine. Aber immerhin, ich habe 2 Wochen ohne Rechner durchgehalten und den Rechner erst am 2. Weihnachtsfeiertag benutzt. Ich habe auch einige Bilder gemacht, die wenigsten von mir. ;)

Das Jahr war wirklich anstrengend, es gab richtig viel zu tun und immer kam auch eine Baustelle zu den falschen Zeitpunkten hinzu. Ich habe dieses Jahr nicht sehr viel Urlaub gehabt, was sich nächstes Jahr deutlich zeigen wird.  Leider ist aufgrund der Arbeit auch das bloggen ab und an zu kurz gekommen. Dafür hatte ich aber viele spannende Projekte, Projekte mit den neuen Technologien WCF, WF, LINQ und MVC. Ich hoffe, dass es auch im nächsten Jahr richtig spannend weitergeht. Am Anfang hört sich das schon sehr spannend an mit den 2+ Projekten.

Privat kann ich mich eigentlich an nicht viel erinnern, ich glaube, so wenig habe ich noch nie gemacht. Für mich war das auch gut so, ich habe noch immer an meine Ex gedacht und selbst jetzt ist sie noch nicht aus dem Kopf. Aber ich habe sehr gut Freunde, die mich immer wieder aufheitern.

Auf jeden Fall wünsche ich allen ein gesundes, erfolgreiches, lustiges und glückliches Jahr 2009. Ich hoffe, dass ich im nächsten Jahr wieder reichlich interessante Themen zum bloggen habe und auch dazu komme alles abzuhandeln. Leider war das in diesem Jahr nicht immer so.

Happy Year2009

Sonntag, Dezember 14, 2008

Ab in den Urlaub

Ich verschwinde in den Urlaub, nur noch wenige Stunden bis der Flieger startet. Ich habe die letzten Monate aber auch deutlich gemerkt, dass es bitter notwendig ist. Und so bin ich einfach nur froh, dass das Jahr überstanden ist.

Die nächsten Wochen findet ihr mich in der Dominikanischen Republik ohne jegliches Computer Equipment. Das wird mir schwer fallen, viele Tage ohne Rechner. Aber vielleicht trösten mich die Einheimischen.


View Larger Map

Weihnachtslied

(Ernst Moritz Arndt 1769-1860)

Erklinge, Lied, und werde Schall,
Kling gleich der hellsten Nachtigall,
Kling gleich dem hellsten Lerchenklang
Die ganze, weite Welt entlang.

Kling, Lied, und kling im höchsten Ton:
Es kommt der süße Gottessohn,
Es kommt das helle Himmelskind
Hernieder, wo die Sünder sind.

Er kehrt bei einer Jungfrau ein,
Will eines Weibes Säugling sein,
Der große Herr der ganzen Welt,
Ein Würmlein auf die Erde fällt.

Ein armes Knäblein nackt und bloß,
So liegt er in Marias Schoß;
Der alle Sterne lenken kann,
Fleht eines Weibes Gnade an.

Der eh'r als Erd' und Himmel war,
Das Wort des Vaters rein und klar,
Spricht lieb und freundlich bei uns ein
Und will der Sünder Bruder sein.

So kommt die unermeßne Huld,
Zu tragen unsre schwere Schuld,
Die ewige Liebe steigt von Gott
Zu uns herab für Schmach und Spott.

Des solln wir alle fröhlich sein
Und singen mit den Engelein
Und singen mit der Hirten Schar:
Das ew'ge Heil wird offenbar.

Des solln wir alle fröhlich sein,
Daß Gott will unser Vater sein,
Und daß der süße Jesus Christ
Heut unser Bruder worden ist.

Schönes Fest!

Sonntag, Dezember 07, 2008

Technical Summit 2008 - Nachtrag

Ich bin ein ganzes Stück verspätet, hier sind aber endlich einige Details/Eindrücke vom Technical Summit.


Windows Live Spaces

Der Technical Summit war interessant und auch aufschlussreich in vielen Bereichen. Von einigen Vorträgen war ich sehr enttäuscht und andere fand ich wieder sehr Klasse. mein Fokus lag auf Parallel Computing und ASP.NET/WCF. Parallel Computing war zu allererst nicht auf der Agenda, da die Key-Note soviel Laune auf mehr gemacht hat, habe ich mir weitere Detail reingezogen. Alles zu Parallelisierung und Microsofts arbeit gibt es auf den Seiten der MSDN. Ich empfehle einfach mal die Parallel Extensions auszuprobieren, es ist sehr einfach seine Anwendung zu modifizieren.

Die Vorträge zu Azure und Cloud Computing fand ich nicht so doll. Hier ist einfach nur alles schwammig, es gibt nichts konkretes und viele Unternehmen, werde wohl nicht sensible Daten außer Haus geben wollen. Ich bin sehr gespannt, wie die Entwicklung im nächsten Jahr weiter geht.

Richtig schlecht fand ich den Vortrag zu ASP.NET-Future, hier war sicherlich auch meine Erwartungshaltung überzogen. Aber es wurde auf so vieles nicht eingegangen, an Themen vorbei geredet und unwesentliches Langgezogen und wesentliches völlig verschluckt. Schließlich wollte der Redner nur noch weg und hat einfach extrem zeitig Schluss gemacht. Der Erfahrungsgehalt des Vortrags war gleich null.

Spass gemacht haben auf jeden Fall die Vorträge von Darius Parys. Es ist sicherlich eine eigenwillige Vortragsart, aber sehr passend für Entwickler.

Technorati-Tags: ,

Visual Studio 2008 - jQuery support

Microsoft hat vor 2 Wochen die Unterstützung für jQuery released. Eine Anleitung für die Installation und Konfiguration des Visual Studio gibt es mal wieder von Scott Guthrie.

Zusammengefasst, muss man folgendes machen:

  1. SP1 für VS2008 installieren
  2. "-vsdoc.js"-Support installieren
  3. jQuery downloaden inkl. Doku und zur Anwendung hinzufügen
  4. Have fun!

Ich bin mir derzeit nicht sicher. ob ich jQuery oder die ASP.NET-Ajax-Komponenten nutzen soll. Ein bisschen überschneiden sich beide Frameworks. Daher werde ich wohl ein einem nächsten Projekt jQuery intensiver nutzen. Ich hatte es bereits in einer Anwendung vor einem halben Jahr drin, habe es dann aber entfernt, da ich nicht 2 Frameworks verwenden wollte.

Jeder der mit Javascript umgeht, sollte sich jQuery einmal anschauen, ob es nicht vieles vereinfacht. ("$"-Operations, ...) Auch bei der Sharepoint-Entwicklung und allen anderen ASP.NET-Projekten kann das Framework eine Menge vereinfachen.

So, nun mal schauen, ob ich noch ein bisschen an meinem Technical Summit Post arbeite. ;)

Sonntag, November 02, 2008

Compression over WCF-Transports

Ich habe mich die letzten Wochen/-enden mit Komprimierung von WCF-Daten beschäftigt. Komprimierung könnte gerade bei der Anbindung von vielen Clients im WAN Bereich positive Ergebnisse erzielen. Im lokalen Bereich schadet es bei heutigen Prozessoren sicherlich wenig und für große Datenmengen ist es in diesen Fällen ideal.

Ich war allerdings nicht so verrückt mich auf eine Neuentwicklung einzulassen. Es musste so etwas bereits geben und so war es auch. In den WCF-Samples ist ein Beispiel enthalten, dies soll vor allem das erstellen von eigenen WCF-Transports demonstrieren. Mit der Lösung habe ich etwas herum gespielt, allerdings habe ich nur eine http-Transport damit zum laufen bekommen. Auf der Suche nach alternativen bin ich auf das CodePlex-Project WCF-Extensions gestoßen. Das Projekt hat bisher kein Release veröffentlicht, allerdings kann man den Code downloaden.

Statt den Code auszuprobieren, habe ich erst mal den Code erweitert. Ich habe zusätzlichen Komprimierungsalgorithmus hinzugefügt, so dass es nun ganze 5 Möglichkeiten gibt.

   1:  public enum CompressionAlgorithm
   2:  {
   3:      GZip,
   4:      Deflate,
   5:      BZip2,
   6:      Zip,
   7:      GZip2
   8:  }

Die letzten 3 Algorithmen verwenden die SharpZibLib zur Komprimierung. Ich finde die ZibLib eine super Implementierung, wer es noch ausgefallener/besser möchte kann dies auch noch um Xceed Zip for .Net aufbohren.

Nach den Anpassungen habe ich zumindest mal einige Tests mit dem integrierten Client gemacht. Es hat sich schnell gezeigt, dass geringe Datenmengen, < 200 Bytes, durch die Komprimierung sogar minimal vergrößert wurden. Die besten Ergebnisse habe ich immer mit BZip2 erhalten. Einen genauen Vergleich der Komprimierungsraten und der Verhalten im WCF-Streams habe ich nicht bis zu Ende durchgeführt. Mir hat es gereicht, nach dem es funktionierte.

So, nun aber zum schwierigeren Teil, die Konfiguration in der app.config. Ich hatte mich eine ganze Weile durch verschiedene MSDN-Pages gesucht und die Konfiguration zusammen zu frickeln. Ich bin froh, dass beim Startup der Anwendung die Konfiguration geprüft wird. Meine erste Konfiguration sah wie folgt aus:

   1:    <system.serviceModel>
   2:      <extensions>
   3:        <bindingElementExtensions>
   4:          <add name="compression" type="WcfExtensions.ServiceModel.Configuration.CompressionElement, WcfExtensions.ServiceModel"/>
   5:        </bindingElementExtensions>
   6:      </extensions>
   7:      <behaviors>
   8:        <serviceBehaviors>
   9:          <behavior name="CustomerService">
  10:            <serviceDebug httpHelpPageUrl="http://localhost:55557/Provisioning/CustomerService"
  11:              includeExceptionDetailInFaults="true" />
  12:            <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:55556/Provisioning/CustomerService" />
  13:            <serviceTimeouts transactionTimeout="00:00:40" />
  14:          </behavior>
  15:        </serviceBehaviors>
  16:      </behaviors>
  17:      <bindings>
  18:        <customBinding>
  19:          <binding name="tcpCompressed" openTimeout="00:00:20" receiveTimeout="00:00:40"
  20:            sendTimeout="00:01:30">
  21:            <compression algorithm="BZip2" level="Normal"/>
  22:            <transactionFlow />
  23:            <binaryMessageEncoding maxReadPoolSize="32"
  24:                              maxWritePoolSize="32"
  25:                              maxSessionSize="4096">
  26:              <readerQuotas
  27:                  maxArrayLength="8000"
  28:                      maxBytesPerRead="4096"
  29:                      maxDepth="32"
  30:                      maxNameTableCharCount="16384"
  31:                      maxStringContentLength="65536" />
  32:            </binaryMessageEncoding>
  33:            <tcpTransport hostNameComparisonMode="WeakWildcard" transferMode="StreamedResponse"
  34:            maxReceivedMessageSize="1048576000">
  35:            </tcpTransport>
  36:          </binding>
  37:        </customBinding>
  38:      </bindings>
  39:      <services>
  40:        <service behaviorConfiguration="CustomerService" name="DE.CapeVision.Windows.Services.AgentService.Components.ProjectProvisioningWorkflow.CustomerService">
  41:          <endpoint address="net.tcp://localhost:20102/Provisioning/CustomerService"
  42:            binding="customBinding" bindingConfiguration="tcpCompressed"
  43:            contract="DE.CapeVision.Common.Contracts.Customers.ICustomerService" />
  44:        </service>
  45:      </services>
  46:    </system.serviceModel>

Es funktionierte mit der Konfiguration recht gut, allerdings kam irgendwann die Stelle, die ein weitersuchen erforderte, ich hatte keine Authentifizierung. Aber ich wollte unbedingt WindowsSecurity für die Anwendung haben. Es handelt sich um eine kleine Intranet-Anwendung. Authentifizierung ist ein wesentliches Merkmal zur minimalen Absicherung des Dienstes. Ein halber Tag und viele Kämpfe mit der MSDN Doku, habe ich es geschafft. Am Ende fehlten die beiden Elemente vor dem Transport (dick hervorgehoben 33-36), die Konfiguration sah so aus:

   1:    <system.serviceModel>
   2:      <extensions>
   3:        <bindingElementExtensions>
   4:          <add name="compression" type="WcfExtensions.ServiceModel.Configuration.CompressionElement, WcfExtensions.ServiceModel"/>
   5:        </bindingElementExtensions>
   6:      </extensions>
   7:      <behaviors>
   8:        <serviceBehaviors>
   9:          <behavior name="CustomerService">
  10:            <serviceDebug httpHelpPageUrl="http://localhost:55557/Provisioning/CustomerService"
  11:              includeExceptionDetailInFaults="true" />
  12:            <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:55556/Provisioning/CustomerService" />
  13:            <serviceTimeouts transactionTimeout="00:00:40" />
  14:          </behavior>
  15:        </serviceBehaviors>
  16:      </behaviors>
  17:      <bindings>
  18:        <customBinding>
  19:          <binding name="tcpCompressed" openTimeout="00:00:20" receiveTimeout="00:00:40"
  20:            sendTimeout="00:01:30">
  21:            <compression algorithm="BZip2" level="Normal"/>
  22:            <transactionFlow />
  23:            <binaryMessageEncoding maxReadPoolSize="32"
  24:                              maxWritePoolSize="32"
  25:                              maxSessionSize="4096">
  26:              <readerQuotas
  27:                  maxArrayLength="8000"
  28:                      maxBytesPerRead="4096"
  29:                      maxDepth="32"
  30:                      maxNameTableCharCount="16384"
  31:                      maxStringContentLength="65536" />
  32:            </binaryMessageEncoding>
  33:            <security authenticationMode="SspiNegotiatedOverTransport"
  34:                   requireSecurityContextCancellation="false">
  35:            </security>
  36:            <windowsStreamSecurity protectionLevel="EncryptAndSign"/>
  37:            <tcpTransport hostNameComparisonMode="WeakWildcard" transferMode="StreamedResponse"
  38:            maxReceivedMessageSize="1048576000">
  39:            </tcpTransport>
  40:          </binding>
  41:        </customBinding>
  42:      </bindings>
  43:      <services>
  44:        <service behaviorConfiguration="CustomerService" name="DE.CapeVision.Windows.Services.AgentService.Components.ProjectProvisioningWorkflow.CustomerService">
  45:          <endpoint address="net.tcp://localhost:20102/Provisioning/CustomerService"
  46:            binding="customBinding" bindingConfiguration="tcpCompressed"
  47:            contract="DE.CapeVision.Common.Contracts.Customers.ICustomerService" />
  48:        </service>
  49:      </services>
  50:    </system.serviceModel>

Die Clientseitige Konfiguration unterscheidet sich nur minimal von der gegebenen, es sollte sich schnell erstellen lassen. Hier der Vollständigkeit die Client-Seite:

   1:    <system.serviceModel>
   2:      <extensions>
   3:        <bindingElementExtensions>
   4:          <add name="compression" type="WcfExtensions.ServiceModel.Configuration.CompressionElement, WcfExtensions.ServiceModel"/>
   5:        </bindingElementExtensions>
   6:      </extensions>
   7:      <bindings>
   8:        <customBinding>
   9:          <binding name="tcpCompressed" openTimeout="00:00:20" receiveTimeout="00:00:40"
  10:            sendTimeout="00:01:30">
  11:            <compression algorithm="GZip2" level="Fast"/>
  12:            <transactionFlow />
  13:            <binaryMessageEncoding maxReadPoolSize="32"
  14:                              maxWritePoolSize="32"
  15:                              maxSessionSize="4096">
  16:              <readerQuotas
  17:                  maxArrayLength="8000"
  18:                      maxBytesPerRead="4096"
  19:                      maxDepth="32"
  20:                      maxNameTableCharCount="16384"
  21:                      maxStringContentLength="65536" />
  22:            </binaryMessageEncoding>
  23:            <security authenticationMode="SspiNegotiatedOverTransport"
  24:                   requireSecurityContextCancellation="false">
  25:            </security>
  26:            <windowsStreamSecurity protectionLevel="EncryptAndSign"/>
  27:            <tcpTransport hostNameComparisonMode="WeakWildcard" transferMode="StreamedResponse"
  28:            maxReceivedMessageSize="1048576000"/>
  29:          </binding>
  30:        </customBinding>
  31:      </bindings>
  32:      <client>
  33:        <endpoint address="net.tcp://localhost:20102/Provisioning/CustomerService" binding="customBinding" bindingConfiguration="tcpCompressed" contract="DE.CapeVision.Common.Contracts.Customers.ICustomerService" />
  34:      </client>
  35:    </system.serviceModel>

Hier natürlich noch der manipulierte Source Code der WCF-Extensions: Download. Um es produktiv einzusetzen muss allerdings noch etwas geschraubt werden, damit Performance Informationen abgegriffen werden können. Bis auf die 2 Trace-Ausgaben ist derzeit nichts enthalten.

UPDATE: Ich habe heute eine kleine Unschönheit gefixed, der Code in der Quelle ist entsprechen aktualisiert. Die Channels werden nicht sauber durch gereicht, so dass es unter Umständen zu einer Cast-Exception in der CompressionChannelBase kommt.

Technorati-Tags: ,,

Montag, Oktober 27, 2008

Visual Studio 2010 und .NET 4.0 download

Seit heute gibt es auf der Connect-Site von Microsoft die CTP-Version von VS 2010 und .NET 4.0 download. Ich denke, ich werde bestimmt auch bald die Installation auf mich nehmen.

Technorati-Tags: ,,

Sonntag, Oktober 26, 2008

Upgrade zu ASP.NET MVC Beta

Ich habe mich heute an das Upgrade meiner ASP.NET MVC Applikation auf die Beta-Version gestürzt. Mit dem Beta-Status ist die MVC-API als relativ stabil zu bezeichnen, so dass mit der nächsten und ggf. übernächsten Version kaum Änderungen zu erwarten sind.

Den Anfang der heutigen Aktion machte der Download des Beta-Releases und anschließend der Download des Future-Assembly. (Im Zweifel beides immer über Codeplex ASP.NET zu erreichen)

Der nächste Schritt war das lesen des Blog-Posts von ScottGu, so dass man wieder auf dem Laufendem ist. Allerdings sind soviele neue Features drin, die ich nicht wirklich nutzen kann/will. Einiges ist sehr cool, aber deutlich zu spät für mich.

Als erstes mussten alle Namespaces in der Config angepasst werden. Es kommen 2 neue Namespaces hinzu:

  • <add namespace="System.Web.Mvc.Ajax"/>
  • <add namespace="System.Web.Mvc.Html"/>

Anschließend musste die Html.Form<>-Helper aufrufe umgeschrieben werden, allerdings zum Glück nur durch die Methode Html.BeginForm<> zu ersetzen. Doch Achtung, die Generic-Versionen sind nur im Future-Assembly zu finden. Ist dieses nicht referenziert, so wird die Methode nicht gefunden.

Der 3. Knackpunkt, der mich kurz aus der Bahn geworfen hat, war eine Änderung am OutputCaching. Dieses muss genau die gleiche Angaben enthalten, wie es bei der Page-Direktive notwendig ist. ([OutputCache(Duration=20, VaryByParam=*)] oder [OutputCache(Duration=20, VaryByParam=none)])

Anschließend war "alles" schick und es lief wieder. Zumindest sind mir derzeit keinerlei weitere Probleme entgegen gekommen.

Technorati-Tags: ,,

Sonntag, Oktober 19, 2008

WCF Project Layout

Für das Layout von WCF-Projektstrukturen gibt es relative klare Empfehlungen, leider sind die in den Visual Studio Templates nicht abgebildet. Im Gegenteil, es wird von vielen Architekten und Entwicklern im WCF-Umfeld empfohlen, komplett auf diese Templates zu unterstützen. Es wird einfach zu viel gemacht, dass gar nicht notwendig ist und das eine saubere Trennung erschwert. Die Empfehlung für WCF-Projekte sieht vor, dass Contracts, egal ob Data- oder ServiceContract[s] in einem separaten Assembly (Class-Library) abgelegt werden. Die Implementierung der Services und weiterer Schichten kann dann, wie bei WebService oder anderen Projekten durchgeführt werden.

Assemblies:

  • Contract
  • Service (evtl. als Facade), ggf. als IIS-Projekt
  • Datalayer

Oft wird auch oft empfohlen, dass man eine Fassaden-Architektur einzieht, wobei dass sehr umstritten ist. In der Fassade wird die spezielle Behandlung von FaultContract(s) durchgeführt, Artikel zu WCF-Exception Handling "Exception Handling in WCF using Fault Contract". Außerdem kann man die Fassade sehr gut dazu benutzen, Exception-Informationen von der Außenwelt zu verbergen.

Sind die Contracts sauber in dem separatem Assembly programmiert, kann dieses Assembly in .NET-Clients verwendet werden. Dadurch spart man sich die "hässliche" Generierung von Services auf dem Client. In allen Projekten, die ich durchgeführt habe, hat sich das als der beste Weg herausgestellt. Allerdings ist darauf zu achten, dass nur ein Vertrag in dem Assembly eingeführt wird. Die Implementierung von Logik hat in den Verträgen nichts zu suchen und würde die Unabhängigkeit von Client und Service verhindern. Ist das der Fall, hat man eine bessere .NET Remoting Lösung erstellt. Nur noch einmal zur Klarstellung, dass Contract-Assembly muss nicht genutzt werden, aber es ist sauberer für .NET Clients.

Technorati-Tags: ,

Sonntag, Oktober 05, 2008

ASP.NET Ajax: Scripte zusammenfassen

Ich bin etwas spät, aber bevor ich es niemals mehr blogge kommt heute der Artikel zu JS. Ich habe mir endlich mal wieder Zeit für das Bloggen genommen und bin auch froh. Meine Arbeitslage hat sich sehr plötzlich etwas entspannt.

Der ScriptManager aus dem .NET Framework 3.5 kann JavaScripte auf dem Server zusammenfassen. Das bedeutet, dass eine Anzahl von Scripten in ein größeres Script zusammengefasst wird. Es gibt mehrere Vorteile, die durch das zusammenfassen entstehen:

- weniger Server-Roundtrips für den Download der Script

- optimierte Script-Files (entfernte Kommentare, entfernte Zeilenumbrüche)

Das schwierige für das Zusammenfassen von Scripten ist allerdings herauszubekommen, was alles geladen werden muss. Hierfür existiert allerdings auch ein passendes Tools, mittels dem der Konfigurationsblock sehr einfach erzeugt werden kann. Der ScriptReferenceProfiler ist ein Server-Control, dass einfach auf der Webseite eingebunden wird. Beim Aufruf der Webseite erhählt man einen Script-Block als Ausgabe. Der Script-Block wird 1:1 in die ASP.NET-Seite eingefügt und zwar beim eingebundenen Script-Manager. Eine sehr gute Anleitung kann unter video-296.aspx angeschaut werden.

Ursprünglich eingebundener ScriptManager:

<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>

Eingefügte Scripte, die zusammengefasst werden:

<asp:ScriptManager ID="ScriptManager1" runat="server">
    <CompositeScript>
        <Scripts>
            <asp:ScriptReference Name="MicrosoftAjax.js" />
            <asp:ScriptReference Name="MicrosoftAjaxWebForms.js" />
            <asp:ScriptReference Name="”AjaxControlToolkit.Common.Common.js”" Assembly="”AjaxControlToolkit,"
                Version="3.0.20229.17016," Culture="neutral," PublicKeyToken="28f01b0e84b6d53e”" />
            <asp:ScriptReference Name="”AjaxControlToolkit.ExtenderBase.BaseScripts.js”" Assembly="”AjaxControlToolkit,"
                Version="3.0.20229.17016," Culture="neutral," PublicKeyToken="28f01b0e84b6d53e”" />
        </Scripts>
    </CompositeScript>
</asp:ScriptManager>

Eigentlich ist das alles super einfach, allerdings kann es passieren, dass durch sehr viele Scripte die URL zu lang wird. Als Fehler kommt die Meldung "The resource URL cannot be longer than 1024 characters. If using a CompositeScriptReference, reduce the number of ScriptReferences it contains, or combine them into a single static file and set the Path property to the location of it." Das ganze hört sich wesentlich dramatischer an, als es ist. Um das Problem zu lösen, muss man die Scripte aufteilen. Dazu werden ScriptManagerProxy-Controls verwendet. Die Proxy-Instanzen sind ursprünglich für die Verwendung auf Unterseiten gedacht, um weitere Scripte oder Services einzufügen.

<asp:ScriptManager ID="ScriptManager1" runat="server">
    <CompositeScript>
        <Scripts>
            <asp:ScriptReference Name="MicrosoftAjax.js" />
            <asp:ScriptReference Name="MicrosoftAjaxWebForms.js" />
        </Scripts>
    </CompositeScript>
</asp:ScriptManager>
<asp:ScriptManagerProxy ID="proxy1" runat="server">
    <CompositeScript>
        <Scripts>
            <asp:ScriptReference Name="”AjaxControlToolkit.Common.Common.js”" Assembly="”AjaxControlToolkit,"
                Version="3.0.20229.17016," Culture="neutral," PublicKeyToken="28f01b0e84b6d53e”" />
            <asp:ScriptReference Name="”AjaxControlToolkit.ExtenderBase.BaseScripts.js”" Assembly="”AjaxControlToolkit,"
                Version="3.0.20229.17016," Culture="neutral," PublicKeyToken="28f01b0e84b6d53e”" />
        </Scripts>
    </CompositeScript>
</asp:ScriptManagerProxy>

Ein sehr guter Artikel zu dem Thema wurde von Samir Bellouti geschrieben.

Technorati-Tags: ,

Dienstag, September 09, 2008

ASP.NET MVC Preview 5 - Arrrggghhh

An diesem Wochenende war nun wirklich mal wieder beschäftigen mit ASP.NET MVC dran, das Preview 5 wollte ich natürlich nicht länger hinaus schieben, also wurde das installiert. Vorher habe ich die Release Notes studiert, ob es etwas gibt, dass bei mir Probleme machen könnte. Eigentlich sollte die Migration von Preview 4 nach Preview 5 ohne Komplikationen verlaufen, dachte ich. Übrigens eine nette Übersicht über die Änderungen gibt es unter http://www.coderjournal.com/2008/08/aspnet-mvc-preview-release-5/.

Ich habe den halben Tag damit zugebracht nur eine kleine Funktion in meiner Anwendung zu fixen, allerdings war das mehr Arbeit als ich dachte. Die "Html.RenderUserControl" wurde aus dem Html-Helper entfernt, allerdings ohne Kommentar und Ersetzungsempfehlung. Ein UserConrol kann über den klassischen Weg mit Register und anschließend hinzufügen auf der Seite einbinden. Was macht man allerdings mit den Daten und die Parameter, die alle übergeben wurden. Nunja, ein UserControl habe ich über den klassischen Weg abgebildet, doch die anderen .... Also der Klassiker googlen, ich bin mit Sicherheit nicht der einzige, der auf diese Problem gestoßen ist. Die beste Hilfe war das ASP.NET Forum, in dem wenigstens einer auch den Lösungsweg aufzeigte. Letztendlich muss man sein Control und den Aufruf umschreiben. Bei der Verwendung der RenderPartial-Methode können nicht die Parameter die Control-View als AnonymousType übergeben werden. Ich habe meine Lösung anschließend so angepasst, dass alle Parameter in den Model-Daten übergeben werden und mittels der Eval-Methode extrahiert werden. Der Aufwand war schon recht groß, nun bin ich erstmal froh, dass alles funktioniert.

Wichtig ist bei der Umstellung auf das Preview 5 die Versionsnummern in der web.config entsprechend den Release Notes anzupassen, da durch das Final-Release des SP1 einige Versionsnummern final sind und zu dem auch noch niedriger als in den Test-Builds.

Außerdem muss man bei den ErrorHandler-Attribute aufpassen. Man muss CustomErrors in der Konfiguration aktivieren, damit der ErrorHandler arbeitet, andernfalls werden die Fehlerdetails ausgegeben.

Technorati-Tags: ,,

Dienstag, September 02, 2008

Lost in Work, But still alive

Der letzte Post ist schon verdammt lange her und wenn ich mal den kurzen Beitrag zum SP1 herauslasse, dann ist inzwischen fast 1 Monat vergangen. Ich bin die Tage einfach nicht zum bloggen gekommen, nach 10+h auf Arbeit inkl. Wochenende, hatte ich einfach keine Kraft/Lust mehr zum bloggen. Ich konnte auch kaum Themen neben der Arbeit angehen, geschweige denn die Wohnung aufräumen. Der große hektische Brocken ist geschafft, jetzt droht "nur" das übliche Kundengeschäft. Ich habe jetzt wieder Zeit viel Zeit an jemanden zu denken. Es fällt mir noch immer schwer, wie dumm!

Letzten Monat haben ein Kollege und ich, partiell mit weiterer Unterstützung, einen Prototypen für eine Tourenanwendung auf Basis von SCSF erstellt. Wie der Prototyp angekommen ist, habe ich leider noch nicht erfahren, ich hoffe, dass die Arbeit sich gelohnt hat und entsprechend auch in weitere Entwicklungsarbeit endet. Geklärt ist die Aufwandsentschädigung noch nicht, aber ich gehe davon aus, dass sich das Thema in den nächsten Tage/Wochen klärt.

Der Ausblick auf meine Projekteinsätze sieht jetzt schon extrem schlecht aus. Zumindest richtig schlecht, wenn die Kunden alle "Drohungen" wahr machen und entsprechend beauftragen. Aus irgendeinem Grund gibt es noch kein Klonen. Aber andererseits würde das bei mir auch nicht helfen, da dann 2 herumrennen würden, die die gleiche Frau vermissen.

Meine ursprüngliche Überlegung war mal den Urlaub im September abzufackeln, aber daraus wird mit Sicherheit nichts mehr. Bisschen frage ich mich, ob dieses Jahr noch der Urlaub unter zu bekommen ist. Vermutlich muss ich darüber mit meiner GF reden, denn verfallen lassen will ich den Urlaub nicht.

Ich muss mich in der nächsten Zeit mal wieder auf mein MVC-Projekt stürzen, da dieses für meine Ziele enorm wichtig ist. Außerdem ist damit der Umstieg auf die neuen Versionen von Trac und SVN verbunden, bei beiden Projekten gibt es super Interessante Features, die ich einsetzen möchte.

Das nächste Thema, in das ich mich endlich mal einarbeiten möchte ist WPF und Composite WPF. Ich bin so gespannt, wie sich die Performance im Vergleich zu SCSF sich verhält.

Montag, September 01, 2008

ASP.NET MVC Preview 5

Am Wochenende wurde vom MVC-Projektteam das PR5 veröffentlicht!

Etwas überrascht war ich im August, dass das MVC nicht Bestandteil des Frameworks wurde. Ich habe eigentlich stark gehofft, allerdings wäre der Zeitplan extrem sportlich geworden. Nun ja, ich find es wiederum auch gut, dass lieber die Qualität stimmen musste, bevor man das Fx v

eröffentlicht.

Ich bin noch nicht zur Umstellung meiner Version gekommen, hoffe allerdings das es diesmal weniger Probleme gibt, als beim letzten mal. Da habe ich 2 Tage zum Modifizieren und Testen benötigt. Bin gespannt auf die Posts zu den aktuellen Themen von Phil und Scott. Evtl. geht es auch mit der Screencast Serie weiter.

Download: http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=16775

Technorati-Tags: ,,

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: ,,,