Welcome to AspAdvice Sign in | Join | Help

manik.net

alles über c# / microsoft asp.net und viel, viel mehr
Setting IIS7 Cache-Control to a custom header, IIS7 Cache-Control header manuell setzen

Nach langem suchen und stöbern habe ich endlich rausgefunden wie man den CacheControl Header manuell setzen kann im IIS7.
Als erstes die staticContent-Config Section freischalten: (Unlock staticContent application config section)

appcmd unlock config /section:staticContent

Dann einfach den CacheControl Header setzen (Then set the cache-control header):

appcmd set config "<WEBSITENAME>/<PATH>" /section:staticContent /clientCache.cacheControlCustom:must-revalidate,proxy-revalidate

 duh. Ach ja, www.babblr.de nähert sich der Version 1.0, das neue Design und ein Großteil der Features sind jetzt online.

P.S. der Blog zieht bald um!

Sponsor
Getting the control that caused the postback / Das Control ermitteln, welches den Postback ausgelöst hat

protected Control GetPostBackControl()
{
System.Collections.Specialized.
NameValueCollection postCollection = System.Web.HttpContext.Current.Request.Form;
string controlID = postCollection.Get("__EVENTTARGET");

if (string.IsNullOrEmpty(controlID) == false)
{
return Page.FindControl(controlID);
}

foreach (string key in postCollection.Keys)
{
Control control = Page.FindControl(key);

if (control is Button)
{
return control;
}
}
return null;
}

Control GetPostBackControl()
{
System.Collections.Specialized.
NameValueCollection postCollection = System.Web.HttpContext.Current.Request.Form;
string controlID = postCollection.Get("__EVENTTARGET");

if (string.IsNullOrEmpty(controlID) == false)
{
return Page.FindControl(controlID);
}

foreach (string key in postCollection.Keys)
{
Control control = Page.FindControl(key);

if (control is Button)
{
return control;
}
}
return null;
}

if (string.IsNullOrEmpty(controlID) == false)
{
return Page.FindControl(controlID);
}

foreach (string key in postCollection.Keys)
{
Control control = Page.FindControl(key);

if (control is Button)
{
return control;
}
}
return null;
}

foreach (string key in postCollection.Keys)
{
Control control = Page.FindControl(key);

if (control is Button)
{
return control;
}
}
return null;
}

if (control is Button)
{
return control;
}
}
return null;
}

Der untere Teil ist notwendig da Button-Controls nicht die Javascript Funktion "__doPostBack" auslösen, sondern das Form im klassischen ASP(HTML) Stil per Form Submit zurück an den Server schicken. Somit sind Buttons nicht als __EVENTTARGET vermerkt. Etwas schlecht gelöst im ASP.net Framework meiner Meinung nach...
 

Sponsor
Firefox + Invalid viewstate / Viewstate corrupted fix!

Welcher ASP Entwickler kennt den Fehler nicht? Der Grund liegt an einem speziellen Caching das nur der Firefox anwendet.

Mehr zu dem Thema gibt's hier: http://developer.mozilla.org/en/Using_Firefox_1.5_caching

Die Lösung gibt's allerdings hier:

Response.Cache.SetNoStore();

Am besten im Page_PreInit anwenden! 

Sponsor
IIS Admin v2.0.0.0

Details and Sourcecode will follow / Details und Quellcode kommen bald.

http://www.dw-c.de/content/iisadmin2.zip

Sponsor
MsSql: Insert multiple rows with a single query/Mehrere Zeilen mit einer einzigen Query einfügen

Eine interessante Frage die mir da gestellt wurde, unter MySql ist das Ganze ja mehr oder weniger einfach:

Insert Into Northwind.Test (Name,Age) Values ('hans',14),('franz',12);

Unter MsSql ist das schon nicht mehr ganz so einfach, aber möglich. Die folgende Query veranschaulicht eine Möglichkeit, mehrere Zeilen in einer Query einzufügen, unter MsSql:

INSERT INTO Northwind.Test (Name,Age)
(SELECT 'hans',14)
UNION
(SELECT 'franz',12);

Und nicht vergessen, Babblr und den Babblr Blog.

Sponsor
Getting a file's mime-type with c# - Den Mime-Typ einer Datei ermitteln mit c#

As always, snippet in english - introduction text in german.

Eine relativ häufig gehörte Frage ist wie man denn in C# den Mime-Typ einer Datei ermitteln kann. Das folgende Snippet zeigt wie es relativ einfach, per Windows-Registry, geht:

private string GetMimeType(string filename)
{
try
{
string ext = Path.GetExtension(Filename);

if (string.IsNullOrEmpty(ext))
return null;

// convert to lowercase as registry has lowercase keys
ext = ext.ToLower();

RegistryKey registryKey = Registry.ClassesRoot.OpenSubKey(ext);

if (registryKey == null || registryKey.GetValue("Content Type") == null)
return null;

return registryKey.GetValue("Content Type") as string;
}
catch (Exception exception)
{
return null;
}
}

 Ach ja, und nicht vergessen www.babblr.de auszuprobieren :)

Sponsor
babblr.de - alle sagen "was geht"!

An dieser Stelle möchte ich mal ein Web 2.0 Projekt von mir Vorstellen, es handelt sich um Babblr. Babblr ist ein deutscher, sehr sympatischer Twitter-Clone. Das Projekt ist seit gestern "live" und es sind noch nicht alle geplanten Features umgesetzt worden, aber es ist schon voll funktionsfähig! Babblr wurde mit Visual Web Developer Express 2008 SP1 entwickelt, und ist gestützt auf den neuen Model-View-Controller - ASP Framework. MVC, ist das was ASP benötigt hat um mich endlich zu beeindrucken, MVC ermöglicht die Entwicklung von sehr schnellen und hoch kompatiblen Webanwendungen. Es gibt endlich keine Postbacks, Viewstates und all den sonstigen, üblichen ASP Overhead nicht mehr. Mit dem MVC Framework haben Entwickler die volle Kontrollere über die Ausgabe des HTMLs u.a können problemlos Unittests auf den "Controller"-Klassen gefahren werden. Klingt wie Propaganda, aber ist es nicht, MVC ist einfach heiss.

Wie auch immer, fang an zu babbln :)

Sponsor
Ganz kurz: WebClient mit eigenem Verbindungs Timeout (WebClient with a custom connection timeout)

English: As I'm having a few english readers I'm going to add english post subjects whenever I'm posting interesting and helpful code-snippets :) The topic says it all, it shows you how to add a timeout to the WebClient class.

...und jetzt wieder auf Deutsch :-) Vorher hat mich die Frage erreicht wie man denn den WebClient mit einem Timeout versehen kann, da es ja für gewöhnlich heisst, man müsse in solchen Fällen auf die HttpWebRequest-Klasse zurückgreifen. Das stimmt jedenfalls nicht, denn man kann eine Klasse von der Klasse WebClient ableiten und dann die Methode GetWebRequest überschreiben und das WebRequest objekt, welches vom WebClient verwendet wird, nach belieben modifizieren.

Im folgenden Code sieht man wie simpel und genial so etwas in C#/.NET aussehen kann:

public class TimeoutWebClient : WebClient
    {
        private int _timeOut = 10000;
        public int TimeOut
        {
            get
            {
                return _timeOut;
            }
            set
            {
                _timeOut = value;
            }
        }

        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest webRequest = base.GetWebRequest(address);
            webRequest.Timeout = _timeOut;
            return webRequest;
        }
    }

Okay, das war's auch schon für Heute :) Viel Spaß mit dem angepassten WebClient!

Sponsor
Linq: DataContext in einer Web-Anwendung? Statisch? Bei jedem Aufruf? Wie denn bitte?!

Diese Frage stellt sich wohl jeder wenn man Linq-to-SQL in einer Web-Anwendung verwenden möchte. Microsoft sagt dazu folgendes: "A DataContext shoud be use per unit of work". Das heißt soviel wie, man sollte einen DataContext instanzieren für jede Arbeitseinheit die Anfällt, ich denke mal damit ist jeder INSERT/UPDATE/SELECT Befehl gemeint. Ist allerdings meiner Meinung nach etwas viel da so ein Datacontext einiges an Overhead mit sich bringt.

Also? Was machen wir dann? Lösen wir das Problem in dem wir unseren DataContext in unserer Webanwedung einfach statisch deklarieren? Nein. Das würde einiges an Problemen mit sich bringen, zum Beispiel wenn Objekte "InsertOnSubmit" markiert werden während ein "SubmitChanges" Aufruf ausgeführt wird. Aber das ist auch nur eines der *vielen* Probleme.
Die Lösung ist Simpel, wir verwenden einen DataContext für jeden Request der beim Server ankommt. Mit dieser Methode hatte ich bisher noch keine Probleme und die Last auf dem Server ist auch nicht so gigantisch, da wir den DataContext auch nur instanzieren sollten wenn er gebraucht wird. Zusammen mit der .net 3.x Spracherweiterung mit der wir Instanz-Methoden in vorhanden Klassen "injizieren" können. Die Idee ist, dass wir unseren "Per-Request-DataContext" dann über das "Request"-Objekt aufrufen können. Den passenden Codeschnipsel gibt's natürlich auch:

public static class HttpContextExtension
{
   private static object _key = new object();

   // extend HttpContext class
   public static DataContext GetDataContext(this HttpContext httpContext)
   {
      if (httpContext.Items[_key] == null)
          httpContext.Items[_key] = new DataContext();
      
      return httpContext.Items[_key] as DataContext;
   }
}

Die HttpContextExtension-Klasse sollte dann in einen Namespace der überall dort eingebunden ist, wo man auf die Context.GetDataAcessObject() Methode zugreifen möchte.
Übrigens, das "this" Schlüssel wort am ersten Parameter markiert diese statische Methode als Methode die in die klasse HttpContext injiziert wird.

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Sponsor
70-528? Bestanden!

So, letzten Mittwoch habe ich dann endlich auch die 70-528 Prüfung bestanden und bin jetzt offiziell "Microsoft Certified Technology Specialist" :)

Sponsor
Microsoft Exam: 70-536 - bestanden!

Ich hatte heute Morgen mein erstes Microsoft Examen, das "Microsoft .NET Framework - Application Development Foundation" und habe das natürlich bestanden ;).

Vorbereitet habe ich mich mit der üblichen, von Microsoft vorgeschlagenen, Lektüre. Als nächstes steht das 70-526 an, welches ich im Laufe der nächsten Wochen absolvieren möchte.

In der nächsten Zeit werde ich außerdem etwas, naja eher viel, über Linq-to-SQL schreiben.
Wieso? Weil ich Linq zum ersten mal in einer echten n-Tier Webapplikation mit kompletten "Business Object Layer" eingesetzt habe. Alles in allem muss ich sagen Linq ist "heiss". Zusammen mit dem .NET Framework 3.5 habe ich da ein paar echt nützliche Techniken entwickelt, für jeden den das Thema Linq in Kombination mit ASP.net interessiert sollte das ein "must-read" werden.
Für diejenigen die es interessiert: das Projekt ist eine Musik-Suchmaschine a la google, momentan noch nicht ganz fertig - wird aber bald live geschaltet.

Sponsor
C# und ASP.Net Performance Optimierungen

Hier mal in Stichworten ein paar Grundsätze die man beim Entwicklen beachten sollte, wenn Laufzeit-Performance eine Rolle spielt:

C# Performance Optimierungen:

  • Lieber wenig große wie viel kleine Assemblys
  • „sealed“-Klassen wann immer möglich bei geerbten Typen mit vielen virtuellen Funktionen. Die virtuellen aufrufe werden dann „inline“ teil der Klasse.
  • „Equals“ überschreiben bei Value-Types  ( struct valuetype ) , vermeidet Reflektionskosten
  • Propertys sind teurer wie öffentliche Attribute. Bei einfach switches eher öffentliche Attribute verwenden (z.B: public bool Debug).
  • Boxing vermeiden! Fixe Typen wann es geht.
  • for anstatt foreach
  • so wenig funktionsaufruf in einem loop wie möglich, d.h. auch rekursive Aufrufe vermeiden - auch Property zugriffe sollte so gut wie möglich vermieden werden 
  •  „jagged arrays“ anstelle von multidimensionalen arrays.
    string[][] jaggedArray = new string[2][];
    jaggedArray[0] = new string[4];
    jaggedArray[1] = new string[1];
    jaggedArray[0][1] = “performance!”;

    MSIL kann eindimensionale Arrays besser optimieren wie mehrdimensionale.
    Auf MSIL ebene sieht man den unterschied:

    int [,] secondarr = new int[1, 2];
    secondarr[0, 0] = 40;

    MSIL:

    IL_0029: ldc.i4.s   40
    IL_002b: call instance void int32[0...,0...]::Set(int32, int32,in32)

    Mit einem Jaggedarray sieht das ganze dann auf MSIL so aus:

    IL_001c:  ldc.i4.s   40
    IL_001e:  stelem.i4

    stelem = „store an element“

    Bei mehrdimensionalen Arrays wird der ganze „Generic Type“-Kram also betrieben was einiges an Overhead erzeugt.
  • Bei string vergleichen wo Groß-/Kleinschreibung ignoriert werden soll CompareTo anstatt a.ToLower()==b.ToLower() da die ToLower operation zusätzliche strings erzeugt
  • (try/catch)-blöcke vermeiden, lieber den Code sicher schreiben (null checks etc.) und gut prüfen


ASP .Net Optimierungen:

  • Roundtrips vermeiden: Server.Transfer anstelle von Response.Redirect  (es wird kein Responseheader zurück geschickt der einen Redirect auf dem Clienten verursacht, d.h. die Url im Browser ändert sich auch nicht, allerdings spart man dadurch einen Roundtrip)
  • Lange Control.ID zuweisungen vermeiden
  • Tiefe Control Schachtelungen vermeiden
  • Viewstate ausschalten wenn nicht benötigt, am besten nicht per Control sondern per page
    wann?
    -> die seite macht kein postback mit informationen verschiedener controls (5 textboxen auf einer seite)
    -> controls haben keinen dynamischen inhalt oder werden mit jedem postback neu befüllt
  • Page.IsPostBack property verwenden um eine mehrfach initialisierung zu vermeiden
  • Page.DataBind() vermeiden -> jedes control das DataBinding unterstützt wird dann gebundend. -> notwendige controls einzeln binden
  • Eval(##) vermeiden lieber explizit Casten und Daten von hand auswerten, spart Reflektionskosten

     

Sponsor
(Child) Controls finden auf der gesamten Page

Leider bietet ja FindControl nur die möglichkeit das/die aktuelle Control/Page zu durchsuchen. Was ist aber wenn man ein Childcontrol eines nachbar Controls sucht auf das die aktuelle Page/Control kein Zugriff hat? Man braucht eine Funktion mit der man rekursiv nach einer Control.ID suchen kann. Diese Funktion gibts nicht direkt im .net Framework aber auf meinem Blg und zwar hier:

   1: T FindControl<T>(string id, Control root) where T : class
   2: {
   3:  
   4:     if (root == null)
   5:         return null; 
   6:  
   7:     Control control = root.FindControl(id); 
   8:  
   9:     if (control != null)
  10:         return control as T; 
  11:  
  12:     foreach (Control childControl in root.Controls)
  13:     {    
  14:         if (childControl == null)
  15:             continue; 
  16:  
  17:         if (childControl.Controls.Count <= 0)
  18:             continue; 
  19:  
  20:         T match = FindControl<T>(id, childControl); 
  21:  
  22:         if (match != null)
  23:             return match;
  24:     }
  25:     return null;
  26: }
  27:  

Wenn wir ein Updatepanel von einem Customcontrol aus auf der gesamten Page suchen mit dem namen "MyAjaxPanel" dann würde die Funktion wie folgt gerufen:

UpdatePanel updatePanel = FindControl<UpdatePanel>("WatchlistAjaxPanel", this.Page)

Man übergibt also den Typen des Controls das man sucht, das erspart uns überflüssiges type-casting. Falls der Typ unbekannt ist kann man aber auch ganz einfach nach dem "Control" Typen suchen, also:

FindControl<Control>("WatchlistAjaxPanel", this.Page)
Sponsor
Ajax UpdatePanel.Refresh() ausl&#246;sen per Javascript

Vor ein paar Tagen musste ich ein Ajax UpdatePanel.Refresh() per JavaScript auslösen, der Haken daran war es mussten auch noch Zusatzinformationen an den Server übertragen werden.
Die Lösung ist man ruft die JavaScript Funktion "__doPostBack" aus und übergibt als EventTarget die ClientID des UpdatePanel das zu refreshen gilt. Die Zusatzinformationen werden dem EventArgument Parameter der "__doPostBack" Funktion übergeben.
Wenn man keine Parameter bzw. Zusatzinfos an den Server übertragen möchte - also nur das Updatepanel refreshen ist das ganze ziemlich einfach.

Javascript Code:

function refreshUpdatePanel( updatePanelClientID )
{ 
    __doPostBack( updatePanelClientID, '' );
}

Dazu der passende C# Code:

button.OnClientClick = 
    "BLOCKED SCRIPTrefreshUpdatePanel( '" + updatePanel.ClientID + "' );"+
    "return false;";
Zu beachten ist hier lediglich das "return false" am ende des OnClientClick JavaScripts.
Dieses verhindert einen asp.net postback zurück an den server.

Wenn man Parameter/Zusatzinformationen an den Server zurück schicken möchte wird das ganze allerdings etwas komplizierter.

Javascript Code:

function refreshUpdatePanelParameterized( updatePanelClientID, eventArgumentName )
{
    // do javascript stuff to populate event arguments here, like get all checked checkboxes
    // or other clientside control content and send it back to the server
    // i'll use "nothing" as argument placeholder here
    var eventArguments = 'nothing';
    
    __doPostBack( updatePanelClientID, eventArgumentName + '_#_' + eventArguments);
}

C# Code:

protected void Page_Init(object sender, EventArgs e)
{
    if ( Request.Params["__EVENTTARGET"] == updatePanel.ClientID )
    {
        string[] eventsArgs = Request.Params["__EVENTARGUMENT"].Split( new string[]{"_#_"}, StringSplitOptions.RemoveEmptyEntries );
        string argumentName = eventsArgs[0];
        string argumentValue = eventsArgs[1];
 
        System.Diagnostics.Debug.WriteLine(string.Format("Recieved command {0} with values {1}.", argumentName, argumentValue ) );
    }             
}
 
protected void Page_Load(object sender, EventArgs e)
{
    button.OnClientClick = "BLOCKED SCRIPTrefreshUpdatePanelParameterized( '" + updatePanel.ClientID + "', 'arg1' ); return false;";
}

Wie das geschulte Auge erkennt werden hier zwei Parameter übertragen, ArgumentName und ArgumentValue. Der "refreshUpdatePanelParameterized" wird der ArgumentName (hier: "arg1") übergeben und die Funktion selbst ermittelt dann noch im JavaScript die ArgumentValue (hier: "nothing" ) und schickt die Informationen als Postback an den Server.
Der Serverseitige C# code "entschlüsselt" diese Informationen wieder in argumentName und argumentValue und verwertet diese anschließend.

Sponsor
Performance Timing mit ‚using’ und delegates mit „Conditional“ Methoden

Letztens musste ich die (zeitliche) Performance einer sehr CPU/IO lästigen Operation verbessern.
Da die Operation in diverse kleinere Vorgänge unterteilt war musste ich die Vorgänge ermitteln, welche am meisten Zeit in Anspruch nahmen und das meiste Optimierungspotential hatten. Dafür hab ich eine Klasse entworfen die mit dem „using“-Mechanismus die Dauer einer Operation ermittelt. Dabei bin ich auch darauf gestoßen, dass der C# Compiler Methoden die das „Conditional“ Attribut haben nicht delegates zuweisen kann.

Also, nun zu der Klasse:

public class PerformanceTiming : IDisposable
{
 public delegate void LogPerformance(string value);
 
 private DateTime _startTime;
 private string _name;
 private LogPerformance _logPerformanceMethod;
 
 private const string _startMessage = "PerformanceTiming={0}: Start={1}";
 private const string _endMessage = "PerformanceTiming={0}: End={1} Duration={2}msec";
 
 public PerformanceTiming(string name)
 {
  // cannot directly use Debug.WriteLine(string) as it has the "Conditional" attribute.
  //  _logPerformanceMethod = System.Diagnostics.Debug.WriteLine(value);
  // have to use wrapper function
  _logPerformanceMethod = DebugWriteLineWrapper;
  _name = name;
  _startTime = DateTime.Now;
 
  _logPerformanceMethod(string.Format(_startMessage, _name, _startTime.ToLongTimeString()));
 }
 
 public PerformanceTiming(string name, LogPerformance logPerformanceMethod)
 {
  _logPerformanceMethod = logPerformanceMethod;
  _name = name;
  _startTime = DateTime.Now;
 
  _logPerformanceMethod(string.Format(_startMessage, _name, _startTime.ToLongTimeString()));
 }
 public void DebugWriteLineWrapper(string value)
 {
  System.Diagnostics.Debug.WriteLine(value);
 }
 
 #region IDisposable Members
 public void Dispose()
 {
  _logPerformanceMethod(string.Format(_endMessage, _name, DateTime.Now.ToLongTimeString(), (DateTime.Now - _startTime).TotalMilliseconds));
 }
#endregion
}


Mehr oder weniger simple, ja, aber es ist eigentlich auch die Idee bzw. die Anwendung der Klasse was die Sache so sinnvoll macht.

using (PerformanceTiming performanceTiming = new PerformanceTiming("HeavyWork"))

      System.Threading.Thread.Sleep(2000);

}

Damit wird der folgende Output in den Debugoutput stream geschrieben:

PerformanceTiming=HeavyWork: Start=13:12:16
PerformanceTiming=HeavyWork: End=13:12:18 Duration=1999,6672msec

Es wird also die Start-/Endzeit der Operation ausgegeben, plus die Dauer in Millisekunden.
Die Ausgabe kann natürlich ohne Probleme angepasst werden.
Man kann auch eine Methode an das PerformanceTiming Objekt übergeben, welcher dann die Ausgabestrings übergeben werden, so z.B.
Console.Writeline.

using (PerformanceTiming performanceTiming = new PerformanceTiming("HeavyWork", Console.WriteLine))

      System.Threading.Thread.Sleep(2000);
}

Man könnte  natürlich auch jede andere Funktion die nur einen (string) Parameter hat übergeben, z.B. StreamWriter.WriteLine um Direkt in eine Datei zu loggen.

Conditional Methoden und delegates, wie macht man das? Man schreibt einfach eine Wrapper funktion die nicht das [Conditional("DEBUG")] Attribut besitzt.

So wird aus:

_logPerformanceMethod = System.Diagnostics.Debug.WriteLine;

Das:

_logPerformanceMethod = DebugWriteLineWrapper; 
public void DebugWriteLineWrapper(string value) 

            System.Diagnostics.Debug.WriteLine(value); 
}

So, das war nun mein erster mehr oder weniger Sinnvoller Blogeintrag :-)

EDIT: Wie postet man eigentlich code-snippets hier?

EDIT2: I now know the answer thanks to my friend Brendan from Orcs, Goblins and .NET, I will blog about it in a few days.

Sponsor
More Posts Next page »