Jag bloggar numera på http://blog.dileno.com ».

Prenumerera på RSS

Arkiv för ASP.NET:

Twitter-modul i ASP.NET för nedladdning

Om du har letat efter en Twitter-modul för ASP.NET så behöver du inte leta längre. Jag har skrivit ihop en modul i C# för att konsumera ett givet Twitter-flöde. Du kan ladda ner modulen och använda enkelt i ditt projekt.

Användning

Twitter-modulen fungerar som så att man väljer att visa ett eller flera meddelanden från ett givet Twitter-flöde. Det enda man behöver göra är att ange ett användarnamn som finns på Twitter och antalet meddelanden som ska visas.

Här är ett kodexempel för hur du visar fem Twitter-meddelanden i en Repeater.

Code-behind:

string userName = "dileno";

List<TwitterFeed> messages = TwitterFeed.Fetch(userName, 5);

if (messages.Count>0)
{
rptTwitterMessages.DataSource = messages;
rptTwitterMessages.DataBind();
}

Repeater i aspx/ascx:

<asp:Repeater ID="rptTwitterMessages" runat="server">
<HeaderTemplate><ul></HeaderTemplate>
<FooterTemplate></ul></FooterTemplate>
<ItemTemplate><li><%#((TwitterFeed)Container.DataItem).Message %> (<a href="<%#((TwitterFeed)Container.DataItem).Link %>"><%#((TwitterFeed)Container.DataItem).PubDate %></a>)</li></ItemTemplate>
</asp:Repeater>

Ladda ner Twitter-modulen

Du kan ladda ner Twitter-modulen med komplett källkod och kodexempel för att testa och använda själv:

Ladda ner Twitter-modulen (zip-fil, 13 kB)

Korta instruktioner för implementation

  • Lägg in klassen TwitterFeed.cs i ditt projekt.
  • Nyttja koden i Default.aspx.cs för att visa Twitter-meddelanden som du vill.
  • I appSettings.Config finns nyckeln TwitterFeedUrl som du behöver kopiera till din appSettings.Config.

Hur du visar modalt popupfönster vid postback med jQuery och ASP.NET

Jag skrev nyligen om hur du kan använda dig av jQuery och SimpleModal för att smidigt skapa modala popupfönster. Jag visade även hur du skulle integrera SimpleModal med ASP.NET Web Forms. Nu tänkte jag visa dig hur du gör för att visa ett modalt popupfönster vid sidladdning i ASP.NET, vid exempelvis en postback.

Om du vill att ett modalt popupfönster ska visas direkt vid postback så behöver du köra ett script i metoden som anropas vid postback. Det finns flera sätt att göra det på, exempelvis genom att använda dig av ClientScriptManager.RegisterClientScriptBlock eller helt enkelt visa en tidigare dold PlaceHolder med JavaScript i sig.

Det viktiga när du kör scriptet är att du använder dig av jQuerys $(document).ready()-funktion. Om du inte gör det, så kommer du att få ett Operation Aborted-fönster som leder till ett Sidan kan inte visas-meddelande i Internet Explorer.

Så här använder du $(document).ready() för att undvika problem i Internet Explorer:

$(document).ready(function () {
$('.modalwindow').modal({ position: ["15%", "25%"] });
});

Modala popupfönster med SimpleModal och ASP.NET

Jag skrev tidigare om hur man använder SimpleModal och jQuery för att skapa modala popupfönster. Vill du använda dig av SimpleModal-pluginet i en ASP.NET Web Forms-applikation så kan du stöta på problem.

Det SimpleModal gör är att lägga till kod för modalt popupfönster till body, vilket inte fungerar med ASP.NET Web Forms-tänket. Bland annat så slutar postbacks att fungera. Detta har att göra med att det modala popupfönstret helt enkelt ligger utanför <form runat="server">-taggen.

För att lösa detta problem behöver man gå in i källkoden för SimpleModal och ändra så att modala popupfönster läggs till form-taggen i stället för body-taggen.

Jag själv använder den packade varianten av SimpleModal (hittas på nedladdningssidan för pluginet) och det enda jag behövde göra var en sök och ersätt där jag ersatte body med form.

Undvik att RequiredFieldValidator hindrar andra formulär från att skickas

Om du har flera formulär på samma ASP.NET-sida och använder dig av RequiredFieldValidator så är sannolikheten stor att valideringen körs för samtliga formulär på sidan. Detta innebär att formulär helt enkelt inte skickas.

Detta kan man undvika genom att använda sig av ValidationGroup-egenskapen på sina kontroller och på så sätt tala om vilka valideringskontroller som är knutna till vilka formulär. Så här:

<asp:textbox id="tb1" runat="server" />
<asp:requiredfieldvalidator id="rfv1" ... validationgroup="Group1" runat="server"/>

Varje grupp av formulärfält ska tillhöra olika valideringsgrupper.

Notera också att en knapp som inte tillhör någon valideringsgrupp kommer att validera alla kontroller som inte heller tillhör någon valideringsgrupp.

Här är ett mer komplett exempel med de två valideringsgrupperna UserInfo och Search som används för att åtskilja vilket formulär som skickas vid vilket knapptryck:

Namn: <asp:textbox id="tbName" runat="server" />

<asp:requiredfieldvalidator
id="rfvTbName"
controltovalidate="tbName"
display="Dynamic"
text="Skriv in namn."
validationgroup="UserInfo"
runat="server" />

<asp:button
id="btnSubmit"
text="Skicka"
validationgroup="UserInfo"
runat="server" />

Sök: <asp:textbox id="tbSearch" runat="server" />

<asp:requiredfieldvalidator
id="rfvTbSearch"
controltovalidate="tbSearch"
display="Dynamic"
text="Fyll i en sökterm."
validationgroup="Search"
runat="server" />

<asp:button id="btnSearch"
text="Sök"
validationgroup="Search"
runat="server" />

.NET Framework Cleanup Tool avinstallerar .NET-ramverk åt dig

När jag försökte installera .NET Framework 4 Beta 1 så fick jag ett felmeddelande som talade om för mig att jag inte kunda installera beta:n av nya ramverket - på grund av att jag hade .NET 1.0 installerat.

Jag tittade i Kontrollpanelen vilka program jag hade installerade och .NET Framework 1.0 syntes inte till över huvud taget. Efter några sökningar så hittade jag en bloggpost om .NET Framework Cleanup Tool. Med hjälp av detta lilla verktyg kunde jag avinstallera .NET Framework 1.0 och sedan påbörja installationen av nya Framework 4 Beta 1.

Du kan ladda ner .NET Framework Cleanup Tool på ovanstående länk. Du kan även ladda ner verktyget härifrån.

Så aktiverar du ASP.NET och IIS i Windows Vista - enkel checklista

Det är onödigt krångligt att aktivera de moduler som behövs för att få ASP.NET och IIS att köra i Windows Vista. Här är en enkel checklista över det du behöver göra nästa gång du vill få igång ASP.NET och IIS 7 i operativsystemet:

De Windows-funktioner som ska aktiveras är följande:

  • Internet Information Services
  • Under World Wide Web Services, kryssa i Application Development Features, så att .NET Extensibility, ASP.NET, ISAPI Extensions och ISAPI Filters är ikryssade.

Se bild:

Aktivera Internet Information Services och Application Development Features

Använd inte Visual Studios inbyggda webbserver!

Visual Studios inbyggda webbserver, även kallad Cassini Web Server, är en lättviktig webbserver som är tänkt att användas för att testa ASP.NET-applikationer i en utvecklingsmiljö. Webbservern är dock väldigt begränsad och det finns ett antal anledningar till att inte använda Cassini alls.

Webbservern är riktad till dem som inte har IIS på sin dator (exempelvis Windows XP Home-användare), men används ofta i professionell utsträckning också. Detta är inte speciellt bra, med tanke på att det finns ett otroligt mycket bättre alternativ: IIS.

Cassini ska aldrig ersätta IIS

Cassini är ingen IIS och ska definitivt inte ersätta IIS - inte ens i utvecklingsmiljö enligt mig. Den främsta anledningen till detta är att en ASP.NET-applikation i ett antal fall inte beter sig likadant i Cassini som i IIS. En webbapplikation kan fungera bra i Cassini - men när man går live med applikationen på en IIS så fungerar den inte alls.

Dessutom: Cassini är betydligt långsammare än IIS. Det finns också dokumenterade problem och lösningar (för både XP och Vista) gällande Cassinis långsamhet i Firefox.

Vad jag vill säga är att Cassini inte är en riktig webbserver och att den absolut inte ska användas professionellt. IIS finns av en anledning - och är en riktig webbserver.

Läs gärna Microsofts informationssida om Cassini.

Att felsöka MissingMethodException i ASP.NET

Om du får MissingMethodException på din webbplats så beror detta på att det är en binär som orsakar problemet.

Här är några tips gällande MissingMethodException:

Tredjepartskomponenter?

Har du nyligen installerat tredjepartskomponenter på webbplatsen? Undersök om dessa kan ha upphov till problemet.

Jämför assemblys

I Web.Config (under <runtime>) finner du alla assemblys och deras versioner. Jämför dessa med de motsvarande fysiska binärerna som finns i bin-mappen.

Vilken metod saknas var?

ASP.NET-errorsidan talar om vilken metod som saknas. Errorsidan talar även om var metoden anropas någonstans. Ta en noggrann titt här.

Loggning

Om du använder loggning - ta en titt i loggfilen. log4net är ett utmärkt verktyg för loggning. Jag har skrivit om log4net tidigare.

Reflector

Använd Reflector för att undersöka olika versioner av samma binär. Detta verktyg är absolut oumbärligt för att göra en djupdykning i vad som faktiskt sker i metoder, med mera.

Övriga tips

Debugging a MissingMethodException, MissingFieldException, TypeLoadException

I mitt fall så fick jag MissingMethodException på grund av att en metod anropades från en klass som inte ens användes. Det jag gjorde var att radera binären som klassen fanns i - och detta löste problemet.

Lös problemet Token StartElement in state Epilog i ASP.NET

Om du jobbar med ASP.NET och XML så kan du stöta på följande felmeddelande:

Token StartElement in state Epilog would result in an invalid XML document.

Detta felmeddelande dyker oftast upp när något av följande inträffar:

  • Du har inget startelement i ditt XML-dokument
  • Du stänger inte XML-element ordentligt

Följande metoder ska du använda dig av så fort du skriver till XML-dokument på något sätt:

  • WriteStartDocument
  • WriteEndDocument
  • WriteStartElement
  • WriteEndElement

Samtliga metoder finns i XmlWriter-objektet.

Var noga med att öppna och stänga XML-dokumentet och element i själva dokumentet, så kommer du att lösa problemet.

ASP.NET, XML och teckenkodning

Jag gjorde en djupdykning i ASP.NETs XML-hantering när jag skulle skrev inlägget om hur du skapar ett RSS-flöde i ASP.NET. Jag stötte på problem gällande framför allt teckenkodningen i XML-dokument och tänkte nu dela med mig av mina erfarenheter.

XmlTextWriter ger dig UTF-16

Använder du dig av XmlTextWriter tillsammans med StringWriter, så kommer XML-dokumentet du skapar att få teckenkodningen UTF-16, i stället för UTF-8 som oftast är önskvärt.

Detta beror på att en sträng i .NET alltid är UTF-16.

XML-deklarationen för ett XML-dokument som har teckenkodningen UTF-16 ser ut så här:

<?xml version="1.0" encoding="utf-16"?>

Det finns flera problem med UTF-16. Till att börja med renderas XML-dokument med UTF-16 ironiskt nog inte alls i Internet Explorer 7, utan ger i stället Feed code error. Detta är bra att ha i minnet, för att spara tid vid framtida felsökning.

Tittar man sedan närmre på StringWriter inser man att den ärver från TextWriter, som XmlTextWriter accepterar som indata. TextWriter har egenskapen Encoding för att hantera teckenkodning – men denna egenskap är read-only, vilket gör att man inte kan sätta en teckenkodning.

Detta lämnar två alternativ för att få teckenkodningen UTF-8:

  • Använd MemoryStream
  • Skapa en klass som ärver från StringWriter och tillåter att Encoding-egenskapen sätts

MemoryStream och StreamWriter

Med MemoryStream och StreamWriter kan du skicka ut XML-data med UTF-8. Det finns två metoder som fungerar bra för detta, och en metod som inte fungerar lika bra.

Alla tre metoder går ut på att man instansierar en MemoryStream och en StreamWriter och sedan använder sig av StreamWritern tillsammans med XmlTextReader:

MemoryStream ms = new MemoryStream();
StreamWriter objWriter = new StreamWriter(ms);

XmlWriter objXml = XmlWriter.Create(objWriter, settings);

Skillnaden är sedan metoden för att skriva ut själva XML-dokumentet. Här är följande tre metoder som alla skriver ut innehållet i MemoryStream-strömmen:

  1. ToBuffer

    Encoding.UTF8.GetString(ms.ToBuffer())

  2. ToArray

    Encoding.UTF8.GetString(ms.ToArray())

  3. StreamReader

    StreamReader objReader = new StreamReader(ms);
    ms.Seek(0, SeekOrigin.Begin);
    string rssFeed = objReader.ReadToEnd();

ToBuffer()-metoden kan till en början verka fungera, men rekommenderas inte. Den skapar tomt utrymme i XML-datat och gör så att XML-dokumentet inte validerar – och i Internet Explorer 7 inte ens visas!

För att XML-datat ska visas bör du använda ToArray() i stället, eller i vissa fall när du närmre vill specifiera vad som ska visas från XML:en – använda dig av en StreamReader.

Modiferad StringWriter-klass med Encoding

En snitsig lösning på problemet med att kunna sätta teckenkodning för en StringWriter är att helt enkelt skapa en egen klass som ärver från StringWriter och tillåter att en specifik teckenkodning sätts. Så här ser den klassen ut:

public sealed class StringWriterWithEncoding : StringWriter
{
private readonly Encoding encoding;

public StringWriterWithEncoding(Encoding encoding)
{
this.encoding = encoding;
}

public override Encoding Encoding
{
get { return encoding; }
}
}

Glöm inte att lägga till using-direktiv för System.IO och System.Text. Du använder sedan StringWriterWithEncoding-objektet så här:

StringWriterWithEncoding objWriter = new StringWriterWithEncoding(Encoding.UTF8);

Mer läsning

Det finns en hel del intressant läsning om ASP.NET, XML och teckenkodning. Nedanstående rekommenderas för närmre läsning:

Skapa ett RSS-flöde i ASP.NET

Att skapa ett RSS-flöde i ASP.NET är inga problem. Däremot finns det en mängd olika tillvägagångssätt och det är inte alltid lätt att veta hur man ska skapa sitt flöde. Jag visar dig hur du skapar ett lättviktigt RSS-flöde genom att använda en ashx-handler i ASP.NET.

Kort om ashx-handlers

När man använder sig av en aspx-sida för att visa innehåll så sker ett antal anrop, bland annat till Web.Config och IIS:en. När det är HTML som ska visas på en webbsida så är det naturligt att använda sig av en vanlig aspx-sida, men aspx-sidor behövs inte för att visa exempelvis XML - och det är här den mer lättviktiga ashx-handlern kommer in. En ashx-handler skapar helt enkelt inte lika många anrop som en aspx-sida och är därför bättre att använda sig av för att visa exempelvis simpel XML-data.

Skapa en ashx-handler för RSS-flödet

I Visual Studio, högerklicka på ditt projekt, välj Add new item, gå till Web och välj Generic Handler. Döp filen till Rss.ashx. Se bild.

Två metoder skapas, ProcessRequest och IsReusable. Det är i ProcessRequest som koden sedan körs för en ashx handler, motsvarande Page_Load för vanliga aspx-sidor.

RSS-specifikation och exempel

För att vårt RSS-flöde ska tolkas korrekt av webbläsare och andra klientet som läser av RSS-flödet så behöver flödet vara ett XML-dokument med ett antal element som krävs av RSS-formatet. För att få grundläggande teori kring RSS-formatet föreslår jag att du läser RSS-specifikationen och kikar på hur själva XML-dokumentet ska se ut.

Använd XmlWriter i ASP.NET för att bygga RSS-flödet

För att bygga vårt RSS-flöde kommer vi att använda oss av XmlWriter-objektet i ASP.NET. XmlWriter är riktigt smidigt att jobba med och har allt vi behöver.

För att vara säkra på det verkligen är XML som skrivs ut i vår ashx-handler och att det är rätt format så behöver vi först lägga till två rader i ProcessRequest-metoden:

HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ContentType = "text/xml";

Vi rensar Response-bufferten och talar om att utdatat ska vara i XML-format. Sedan kombinerar jag MemoryStream och StreamWriter för att börja skriva ut RSS-flödet med hjälp av XmlWriter-objektet. Så här:

MemoryStream ms = new MemoryStream();
StreamWriter objWriter = new StreamWriter(ms);

XmlWriter objXml = XmlWriter.Create(objWriter, settings);

objXml.WriteStartDocument();
objXml.WriteStartElement("rss");
objXml.WriteAttributeString("version", "2.0");
objXml.WriteStartElement("channel");
objXml.WriteElementString("title", _siteTitle);
objXml.WriteElementString("link", _siteUrl);
objXml.WriteElementString("description", "RSS för tio senaste inläggen på " + _siteTitle);
objXml.WriteElementString("copyright", "&copy; Martin Söderlund");
objXml.WriteElementString("ttl", "15");

Ovanstående kod skapar själva huvudet för RSS-flödet, med XML- och RSS-deklaration, samt grundläggande information om vad RSS-flödet innehåller. Det mesta är ganska så självbeskrivande. ttl talar om hur ofta det ska vara tillåtet för RSS-läsare att läsa av ditt RSS-flöde. I ovanstående exempel är detta satt till 15 minuter.

Det som dock utgör huvuddelen av RSS-flödet måste kopplas samman med en datakälla, såsom en XML-fil eller oftare en databas som innehåller det vi vill skriva ut i RSS-flödet.

Hämta information från databas och skriv till RSS-flödet

För att hämta data från databasen använder jag mig av SqlDataReader, loopar igenom resultatet jag får från min SQL-fråga och skriver XML till RSS-flödet med hjälp av XmlWriter. Notera att jag använder mig av WriteCData-metoden när jag skriver ut själva inlägget. Detta är för att HTML ska visas korrekt i RSS-flödet och slutligen i olika RSS-läsare.

Kod för att hämta tio inlägg från en databas och lägga till i RSS-flödet:

string strSQL = "SELECT TOP 10 id,heading,intro,entry,url,dt FROM tblEntries ORDER BY id DESC";
SqlConnection objConn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbEntries"].ConnectionString);
SqlCommand objCmd = new SqlCommand(strSQL, objConn);

try
{
objConn.Open();
SqlDataReader sdr = objCmd.ExecuteReader();

while (sdr.Read())
{
objXml.WriteStartElement("item");
objXml.WriteStartElement("title");
objXml.WriteCData(sdr.GetValue(1).ToString());
objXml.WriteEndElement();
objXml.WriteElementString("link", _siteUrl + "entry.aspx?id=" + sdr["id"].ToString());
objXml.WriteStartElement("description");
objXml.WriteCData(sdr["intro"].ToString() + sdr["entry"].ToString());
objXml.WriteEndElement();
objXml.WriteElementString("guid", _siteUrl + "entry.aspx?id=" + sdr["id"].ToString());
objXml.WriteElementString("pubDate", Convert.ToDateTime(sdr["dt"]).ToString("R"));
objXml.WriteEndElement();
}
}

catch { }
finally
{
objConn.Close();
}

Cacha RSS-flödet

Det är en god idé att cacha RSS-flödet för att öka prestanda. Som du såg tidigare kan man ange hur ofta en RSS-läsare ska läsa av ditt flöde, men detta är ingen garanti för god prestanda. I stället bör man använda sig av ASP.NETs inbyggda cachning.

RSS-flödet cachar du enkelt med följande kodsnutt, där rssFeed innehållet det vi ska cacha:

HttpContext.Current.Cache.Insert(_feedCacheName, rssFeed, null, DateTime.Now.AddHours(8), TimeSpan.Zero);

Vi skapar ett cacheobjekt som heter RssFeedCache och talar om att cachen ska gälla i åtta timmar. Sedan kan du använda följande kod när du vill läsa från cachen:

Object objFeed = context.Cache.Get(_feedCacheName);

if (objFeed == null) CreateFeedCache();

HttpContext.Current.Response.Write(context.Cache.Get(_feedCacheName).ToString());

Det kan också vara en god idé att använda sig av Remove-metoden för att radera cacheobjektet. Remove är användbart när nytt innehåll lagts till som du vill ska hamna i RSS-flödet. Du når metoden genom Cache.Remove("RssFeedCache").

Komplett kodlistning och nedladdning

Komplett kodlistning för ashx-handlern, med vissa modifikationer för smartare implementation, kan du titta på hos pastebin och ladda ner:

Så skriver du ut HTML i Flash med WriteRaw-metoden

Man vill ofta att Flash-filmer ska vara flexibla och hämta data från en dynamisk XML-fil. När man jobbar med Flash och XML kan man dock stöta på problem vad gäller HTML-formatterad text som ska hämtas från en XML-fil.

Jag har tidigare skrivit om hur du skriver ut HTML-kod som ligger i XML-block. Det man gör är att använda sig av CDATA och bädda in HTML-kod i CDATA-blocket. Det fungerar dock inte alltid.

I mitt fall byggde jag en ashx-handler som returnerade XML-data. En del av XML-datan skulle vara HTML-formaterad text som skulle visas i Flash-filmen. Jag använde mig av XmlWriter och WriteCData-metoden i ASP.NET för detta ändamål - precis som man ska - men HTML-formateringen i Flash-filmen uteblev och i stället renderades alla taggar tillsammans med texten.

Efter en stunds testning kom jag fram till att man kan använda sig av WriteRaw-metoden i stället. Exempel:

_xmlWriter.WriteStartElement("slide");
_xmlWriter.WriteStartElement("message");
_xmlWriter.WriteRaw("<p>Test:</p><ul><li>First item</li></li>Second item</li></ul>");
_xmlWriter.WriteEndElement();
_xmlWriter.WriteEndElement();

Notera att det är viktigt att XML-datat är korrekt formatterat och validerar när du använder dig av WriteRaw-metoden. Annars är risken för problem överhängande.

Förbättra prestanda i ASP.NET genom att inte använda DataBinder.Eval

DataBinder.Eval-metoden är välanvänd för att skicka in ett argument och returnera ett resultat. Det DataBinder.Eval gör är att casta Container.DataItem till dess verkliga typ. Så här:

<%# DataBinder.Eval(Container.DataItem, "Heading") %>

DataBinder.Eval använder sig av .NET reflection för att avgöra vilken typ Container.DataItem ska castas till. Användandet av reflection gör att DataBinder.Eval är mer prestandakrävande än vad som egentligen behövs.

För att öka prestandan kan du i stället explicit casta Container.DataItem till dess specifika typ. Så här:

<%# ((DbDataRecord)Container.DataItem)["Heading"] %>

Så börjar du använda namespaces och controls i Web.Config

I ASP.NET-sidor och användarkontroller kan du använda dig av Import och Register-direktiven för att hantera namespaces direkt på sidan och på så sätt få intellisense. Har du många sidor så är det dock smidigare att använda Web.Config för detta ändamål. Jag visar dig hur.

Import Namespace och Register Namespace

Du har säkert använt dig av följande kod för att importera ett namespace eller en kontroll och få intellisense direkt på en ASP.NET-sida i Visual Studio:

<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Register Namespace="Dileno.Web.Controls" TagPrefix="Dileno" Assembly="CustomControls" %>

Det fungerar bra ifall om du har få namespaces att importera och registera, men ju fler - desto jobbigare att underhålla och speciellt om du har flera sidor i din webbapplikation. Då finns det en lösning på problemet - att använda sig av Web.Config-magi!

Web.Config, namespaces och controls till räddningen

I stället för att ange samma namespaces på många olika sidor, behöver du bara ange namespaces en gång i Web.Config - och sedan fungerar intellisense på övriga sidor i webbapplikationen. Det du behöver göra är att använda dig av <namespaces> och <controls> i Web.Config, så här:

<system.web>
<pages>
<namespaces>
<add namespace="System.Data.SqlClient" />
<add namespace="System.IO" />
<add namespace="Dileno.Classes" />
</namespaces>
<controls>
<add namespace="Dileno.Web.WebControls" TagPrefix="Dileno" Assembly="CustomControls" />
</controls>
</pages>
</system.web>

Grymt smidigt och en tidsbesparare.

Så hämtar du alltid en sida från servern i stället för cachen

Vill du att en sida alltid ska hämtas från servern och aldrig ska cachas i webbläsaren så är det lätt att stöta på problem. Jag visar dig hur du löser problemet för samtliga webbläsare.

Enbart meta-taggar fungerar inte

Jag tänkte lösa det genom att använda två meta-taggar - men det fungerade först inte alls, och sedan inte i alla webbläsare.

Dessa meta-taggar fungerar inte i om du vill att en sida alltid ska hämtas från servern (både IE7 och Firefox hämtar från cachen i stället):

<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="pragma" content="no-cache" />

När jag upptäckte att det inte fungerade så försökte jag i stället att lägga till no-cache på serversidan i Response-headern och som cache-direktiv. Det fungerade i Internet Explorer 7, men inte i Firefox.

Lösningen för att alltid hämta sidor från servern

Det som behövs är att även använda Response.Cache.SetNoStore, så slutar sidan att cachas i samtliga webbläsare.

Den kompletta lösningen:

Response.AddHeader("pragma","no-cache");
Response.CacheControl = "no-cache";
Response.Cache.SetNoStore();

Använd Task List i Visual Studio för uppgiftshantering

I Visual Studio finns en inbyggd uppgiftshantering som är mycket användbar. När du programmerar kan du använda dig av kommentaren TODO för att spara uppgifter som du vill göra senare. Du kan även använda HACK och UNDONE. Så här:

// TODO: Improve cache performerance.
// HACK: This works for now but need adjustments before launch.
// UNDONE: Deleted some code here.

För att se en lista på uppgifterna så väljer du View->Task List i Visual Studio och ser till att Comments är valt i dropdownrutan:

Task List i Visual Studio

Du kan ändra på inställningar för Task List under Tools->Options->Environment->Task List. Exempelvis kan du ändra namn på kommentartyperna och inbördes prioritering.

Vad gäller kommentarerna HACK och UNDONE ställer jag mig mer tvivelaktig till dem. Fulhack bör inte finnas från början och versionshantering av projekt gör att UNDONE är onödig.

Observera att Visual Studio-versioner tidigare än Visual Studio 2008 kan ha problem med dessa kommenterar. Då kan du testa att skriva exempelvis //TODO i stället, utan mellanslag i början.

Icke desto mindre är Task List ett hjälpmedel jag rekommenderar dig att börja använda redan nu!

Använd DefaultButton för att lösa problemet med submit-knappen i .NET

Om du använder dig av flera formulär på en ASP.NET-sida så har du troligtvis stött på problemet med att submit-knappen för ett formulär alltid triggar submit-knappen för standardformuläret på sidan. Det vill säga, fel formulär skickas när du klickar på en submit-knapp.

Det finns en lösning för detta: att använda DefaultButton tillsammans med en Panel som formuläret ligger i. Så här:

<asp:Panel ID="pnlLogin" DefaultButton="btnLogin" runat="server">
<fieldset>
<asp:TextBox ID="tbUsername" runat="server" />
<asp:TextBox ID="tbPassword" TextMode="Password" runat="server" />
<asp:Button ID="btnLogin" OnClick="btnLogin_Click" Text="Logga in" runat="server" />
</fieldset>
</asp:Panel>

Det du gör är alltså att sätta DefaultButton till namnet på submit-knappen i formuläret som ska skickas. I ovanstående fall blir DefaultButton btnLogin.

DefaultButton kan specifieras på form och Panel i .NET.

Fotnot: En Panel-kontroll i .NET renderar en extra <div> i HTML-källkoden. Det är inte snyggt, men hellre det än att ha ett formulär som inte fungerar som det ska.

 

Så åtgärdar du felmeddelandet "The type exists in both" i ASP.NET

Det kan hända att du i din utveckling stöter på detta felmeddelande:

CS0433: The type "Namespace.Classname" exists in both "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\6efa403a\2cc0cf04\App_Code.jllmzhc2.dll"
and "c:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\6efa403a\2cc0cf04\assembly\dl3\4a879c72\ed0e1de2_dc6dc901\Namespace.DLL"

Om du får felmeddelandet CS0433: The type [] exists in both [] and [] så löser du problemet så här:

  • Flytta ut din klass från mappen App_Code i ditt projekt till någon annan mapp.
  • Alternativt, kör kommandot Build->Clean Solution i Visual Studio.

Problemet beror på att om du har en klass i App_Code-mappen så kommer den klassen att kompileras två gånger - först i codebehind och sedan i runtime.

Använd log4net för loggning till filer i .NET

log4net är ett open source-bibliotek för loggning och kan användas för loggning till ett antal olika källor i ASP.NET. I denna artikel visar jag dig hur du loggar till filer.

Ladda ner log4net

För att kunna använda log4net behöver du först ladda ner zip-filen innehållandes dll-filen vi behöver för vårt projekt:

Ladda ner log4net från Apaches hemsida

Lägg till en referens till log4net i ditt projekt

För att kunna använda log4net behöver du lägga till log4net-binären i ditt projekt. I zip-filen du laddade ner bör log4net.dll som du ska lägga till finnas här: \log4net-1.2.10\bin\net\2.0\release.

Kopiera över log4net.dll till din bin-mapp och lägg till referensen:

  • Högerklicka på ditt projekt i Visual Studio, välj Add Reference.
  • Gå till fliken Browse, leta upp log4net.dll och klicka på Ok.

Lägg till log4net-konfiguration i Web.config

I Web.config, lägg till följande i <configSections>:

<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>

Lägg även till en referens till filen log4net.config som vi ska använda oss av, nedanför <configSections>, så att det ser ut så här:

<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net configSource="log4net.config" />

Skapa filen log4net.config

Lägg till en ny konfigurationsfil i roten för ditt projekt och döp den till log4net.config. log4net.config ska sedan se ut så här:

<log4net>
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<file value="c:\logs\applogg.txt" />
<appendToFile value="true" />
<maximumFileSize value="1024KB" />
<maxSizeRollBackups value="20" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="ERROR" />
<appender-ref ref="RollingFile" />
</root>
<logger name="TestLogger">
<level value="DEBUG" />
<appender-ref ref="RollingFile" />
</logger>
</log4net>

I log4net pekar vi ut var loggfilen för applikationen ska finnas (c:\logs\applogg.txt). Vi talar även om att en loggfil får vara max 1024 kB och om den överstiger denna filstorlek så skapas en ny loggfil med namnet applogg.log.1 ända upp till 20. I <root> anger vi rotinställningar, men man kan skapa en loggning med annorlunda egenskaper, som TestLogger ovan. Du kan även ange loggningsnivå, de som finns (i prioriterad ordning) är dessa:

  • ALL
  • DEBUG
  • INFO
  • WARN
  • ERROR
  • FATAL
  • OFF

Loggning på sidnivå

För att du ska kunna logga på sidnivå behöver du lägga till följande i Application_Start i Global.asax:

log4net.Config.XmlConfigurator.Configure();

I sidan du ska använda loggningen på måste du först importera log4net via using-direktivet och sedan lägga till följande variabel:

ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

Ovanstående variabel går mot rotinställningarna för loggningen. Vill du använda de inställningar du har specifierat själv (exempelvis loggen med namnet TestLogger, se tidigare kod) så anropar du bara loggnamnet så här:

ILog log = log4net.LogManager.GetLogger("TestLogger");

För själva loggningen använder du sedan någon av följande metoder:

log.Debug("...");
log.Error("...");
log.Fatal("...");
log.Info("...");
log.Warn("...");

log4net-manual

Detta är ett exempel på hur du loggar till filer med log4net. Vill du bygga vidare på din loggning finns massa nyttig information i log4net-manualen.

Få bättre koll på konfigurationen av din webbapplikation genom att använda configSource i Web.Config

All konfiguration för en ASP.NET-applikation lagras i standardläget i Web.Config. När applikationen växer tenderar dock Web.Config att växa och det blir svårare att hålla reda på informationen i config-filen. Då är det en god idé att nyttja configSource-attributet och separera konfigurationsfiler från Web.Config.

Några fördelar med att separera konfigurationsfiler i Web.Config:

  • Du får mer struktur på din konfiguration - du vet var du ska leta om något behöves ändras i en konfigurationsfil.
  • Du slipper ladda upp en ny Web.Config varje gång du har gjort en förändring i konfigurationen.

De flesta Web.Config-filer har ett appSettings-element och ett connectionStrings-element. Det kan se ut så här:

<configuration>
...
<appSettings>
<add key="KeyName" value="Key value" />
</appSettings>
<connectionStrings>
<add name="connString" connectionString="Data Source=sqlserver2008;Initial Catalog=dbExempel;User ID=user;Password=pwd" providerName="System.Data.SqlClient"/>
</connectionStrings>
...
</configuration>

Det man kan göra i stället är att ange configSource-attributet för connectionStrings och appSettings och länka in externa konfigurationsfiler, så här:

<configuration>
...
<appSettings configSource="appSettings.Config" />
<connectionStrings configSource="connectionStrings.Config" />
...
</configuration>

Då måste konfigurationsfilerna som länkas in se ut enligt följande:

appSettings.Config:

<appSettings>
<add key="KeyName" value="Key value" />
</appSettings>

connectionStrings.Config:

<connectionStrings>
<add name="connString" connectionString="Data Source=sqlserver2008;Initial Catalog=dbExemple;User ID=user;Password=pwd" providerName="System.Data.SqlClient"/>
</connectionStrings>

Du kan även använda dig av file-attributet för appSettings.Config. Då tillåts du ha appSettings-nycklar i Web.Config också, fast Web.Config läser in den externa appSettings.Config-filen. Kom dock ihåg att appSettings-nycklarna i Web.Config skriver över dem som finns i appSettings.Config.

Jämför datum och tid i C#

Vill man jämföra datum och tid i C# så behöver man jobba lite mer än i VB.NET där man kan använda sig av DateDiff().

I C# kan man använda sig av TimeSpan för att jämföra datum. Här är en metod i C# som använder TimeSpan för att få ut antalet dagar mellan två datum (metoden är public static för att enkelt kunna anropas via en basklass):

/// <summary>
/// Compare two given dates
/// </summary>
/// <returns>Number of days between days</returns>
public static int DateDiff(DateTime dt1, DateTime dt2)
{
int diff;

TimeSpan ts = dt1 - dt2;
diff = ts.Days;

return diff;
}

Metoden anropas på detta sätt, där jag väljer att räkna ner antalet dagar till nyårsafton:

try
{
litDiff.Text = DateDiff(DateTime.Now, new DateTime(2008, 12, 31)).ToString();
}
catch { }

Tänk på att det alltid är bra att använda sig av felhantering, i exemplet ovan med hjälp av try catch när jag skriver ut värdet till en Literal-kontroll.

I metoden DateDiff så valde jag att jämföra dagar mellan de givna datumen. Det går lika bra att jämföra på Hours, Milliseconds, Minutes och Seconds.

Använd CultureInfo och TryParse för hantering av decimaler i ASP.NET

Om man ska formatera tal i .NET behöver man tänka på att olika länder har olika decimalavgränsare. Exempelvis används punkt som decimalavgränsare i England, medan vi i Sverige använder kommatecken.

För att visa rätt avgränsare för rätt språk bör man använda sig av klassen CultureInfo i .NET och kombinera den med TryParse-felhantering.

Importera System.Globalization och använd CultureInfo

För att kunna använda sig av CultureInfo måste man importera namespacet System.Globalization. Sedan använder vi oss av CultureInfo och NumberFormatInfo för att visa rätt decimalavgränsare:

// set correct CultureInfo to output correct number format
CultureInfo cultureInfo = CultureInfo.CurrentCulture;
NumberFormatInfo nfi = cultureInfo.NumberFormat;
nfi.CurrencyDecimalSeparator = ".";

double price = 14.90;

if (Double.TryParse(price.ToString(), NumberStyles.Any, nfi, out price) == false) price = 0.00;

litPrice.Text = price.ToString();

CultureInfo.CurrentCulture returnerar exempelvis sv-SE för Sverige och en-GB för England. I .NET finns automatiskt stöd för olika länders talhantering, vilket gör att vi får ut rätt decimalavgränsare för rätt land.

Felhantering med TryParse

En tumregel när man jobbar med tal i .NET är att använda TryParse för att kolla att det verkligen är rätt datatyp som returneras. Du kan använda TryParse för ett flertal datatyper, exempelvis Double och Int32.

I exemplet ovan ger jag slutligen Literalen litPrice ett värde, som ges av price.ToString().

HeaderTemplate och FooterTemplate renderas trots att en Repeater är tom

Ett problem jag stötte på i ett projekt här om veckan var att HTML-kod jag skrivit i HeaderTemplate och FooterTemplate för en Repeater-kontroll renderades, fast Repeatern inte innehöll någon data.

Jag gjorde lite efterforskningar och kom fram till följande:

  • Om det finns en datakälla för Repeatern, så renderas innehållet i HeaderTemplate och FooterTemplate, fast datakällan inte har någon data.
  • Om det inte finns en datakälla, det vill säga datakällan returnerar ett nullvärde, så renderas inte Repeatern över huvud taget.

Lösning på problemet: kolla att datakällan existerar och att den har fler än noll poster i sig:

if (myList != null && myList.Count>0)
{
	rptListing.DataSource = myList;
	rptListing.DataBind();
}

Uppdatering: Använder du exempelvis en SqlDataReader så får du kolla om den booleska egenskapen HasRows är sann i stället för att använda Count.

Till sidhuvudet

Clicky Web Analytics