Uppgradera från ASP.NET Mvc 2 till Mvc 3

1. Fixa web.configs

Sök på följande sträng i ditt projekt (finns i några web.configs):

System.Web.Mvc, Version=2.0.0.0

Och replaca den med denna:

System.Web.Mvc, Version=3.0.0.0

Se till att du har följande längst ner i web.config:

<runtime>    
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>     
</assemblyBinding> 
</runtime>
Referera rätt namespace:
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

2. Sätt project GUID

Högerklicka på ditt projekt och välj unload. Sen edit project.

Byt ut (Gamla Mvc 2):

F85E285D-A4E0-4152-9332-AB1D724D3325

Mot (Mvc 3):

E53F8FEA-EAE0-44A6-8774-FFD645390401

3. Lägg till värden i app.config

 <add key="webpages:Version" value="1.0.0.0"/>
 <add key="ClientValidationEnabled" value="true" />
 <add key="UnobtrusiveJavaScriptEnabled" value="true" />
By Jesper Lind

Kom igång med ASP.NET MVC Release Candidate

ASP.NET MVC är ett ramverk som erbjuder ett sätt att utveckla webbplatser som främjar testbarhet, struktur och klar separation av huvudbry. Man får intuitiva url:er och total kontroll över html-kod och css som rendereras. Själva designmönstret Model-View-Controller är ju inget nytt utan har tidigare funnits i webbramverk som t ex Zend, Django, eller Ruby On Rails. Det här handlar alltså om Microsofts implementering för webb av mönstret.

Ramverket utannnonserades av Microsoft i slutet av 2007 och har sedan dess släppts i ett antal förhandstitt-versioner (fem tror jag), en betaversion och nu nyligen en kandidat till den slutgiltiga versionen. Utveckligen har skett i stor samverkan med utvecklarcommunityn och det har varit riktigt roligt att se det växa fram. Det riktiga släppet ska enligt tidigare meddelanden släppas nu under februari månad. Återstår att se ifall de håller den tidsramen, men nu är det hur som helst inte långt kvar.

Det är alltså ett perfekt läge att börja testa med ramverket ifall du inte gjort det tidigare, något jag rekommenderar varmt. Web forms i all ära, när jag har kört MVC ett tag så lockar inte den klassiska modellen längre.

Bra introduktionsartiklar till ASP.NET MVC av Stephen Walter

Stephen Walter skriver för tillfället på boken ASP.NET MVC Framework Unleashed och bjuder på sin blogg på några exempelkapitel från denna. Riktigt bra ifall man vill lära sig runderna i ASP.NET MVC.

I Chapter 1 - An Introduction to ASP.NET MVC ger han en introduktion till vad han anser hur bra mjukvara ska byggas, designmönster, arkitektur och vikten av testdriven utveckling. Han förklarar även hur standardinstallationen av ASP.NET MVC-installationen fungerar.

Vidare med Chapter 2 - Building a Simple ASP.NET MVC Application ber han oss lustigt nog att glömma vad han just sagt om testbarhet och de stora målen med mönstret och visar oss istället hur man bygger en enkel applikation, utan ett enda test. Här får vi dock lära sig hur man skapar projektetet i Visual Studio och bekantar oss med filstruktur coh de allmänna begreppen.

Sen visas i Chapter 3 - Understanding Controllers hur Controllers fungerar, vad för olika typer av resultat de returnerar, hur man kan begränsa dem till att bara acceptera specifika HTTP-attribut och hur man testar dem.

Det i skrivandes stund sista kapitlet som förhandsvisas är Chapter 9 - Understanding Routing och här visas hur man sätter upp sina routes i Global.asax för hur url-stukturen på för sin applikation ska se ut. Han går igenom hur man kan sätta begränsningar på dem och hur man debuggar och testar dem.

En riktigt lärolik samling artiklar alltså och hoppas fler kapitel dyker upp snart. Tänkt bara att detta är temporära kapitel ur boken som kan komma att ändras för att reflektera eventuella sista-minuten-ändringar vi kan komma att få se i den slutgiltiga versionen av ASP.NET MVC.

Köra olika versioner av ramverket samtidigt

Till sist ett litet tips ifall du redan har utvecklat sajter på tidigare versioner och vill testköra den nya RC-versionen utan att oroa dig för att dina tidigare projekt slutar att fungera. Här handlar det om att ta bort MVC-referenserna från GAC efter du installerat den senaste releasen. Man måste in i registret och röja så var försiktig vad du än gör. Jag har följt instruktionerna på min maskin och det gick fint. Mina äldre projekt körs med betaversionens MVC-ddl:er som ligger i respektive projekts Bin-mapp.

Att jag inte ens har provat att skapa ett RC-projekt är en annan femma. Försöker hålla mig eftersom den riktiga versionen kanske släpps typ imorgon...

By Jesper Lind

Spåra formulärspostningar med Google Analytics

Har tänkt igenom hur denna blogg kan bli bättre och roligare att läsa och har en del ideér. För att kunna mäta om förändingar är bra eller dåliga så ville jag först sätta upp några mål i Google Analytics, och det har jag sysslat med de senaste dagarna. Tänkte dela med mig av vad jag lärt mig.

Först tänkte jag igenom vad målen med bloggen är och kom fram till att det viktigaste och roligaste är när man får input från sina läsare. Det är på så sätt som bloggen kan utvecklas och växa och därför bestämde jag mig för att sätta upp följande två mål.

  1. Kontaktformulär skickat
  2. Kommentar postad

När man sätter upp mål-urlerna i Google Analytics så har man tre olika alternativ, Exakt matchning, Rubrikmatchning och Matchning av vanliga uttryck. Utan att gå in för mycket på dem så kan jag kontatera att Exakt matchning fungerade bäst för det ja ville göra. Hos Google finns det en bra hjälpsida som förklarar hur de olika alternativen fungerar.

De två målen har jag implementerat lite olika så tänkte förklara här hur jag gjort.

Så satte jag upp mål för Kontaktformuläret

Kontaktformuläret postar tillbaks till sig själv och jag har alltså ännu inte gjort om det till PRG-designmönstret som jag skrev om här om dagen. Eftersom adressen inte ändras var jag tvungen att skicka med den påhittade sida "/contact-form-submitted" som jag angett i mitt första mål.

I ContactControllern (ASP.NET MVC) så skickar jag med parameter i ViewData när användaren har postat meddelandet:

this.ViewData["TrackPageView"] = "\"/contact-form-submitted\"";

Denna parameter sätts sen in längst ner på sidan i skriptet för Google Analytics:

<script type="text/javascript">
//<![CDATA[
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write("\<script src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'>\<\/script>" );
//]]>
</script>
<script type="text/javascript">
//<![CDATA[
var pageTracker = _gat._getTracker("<%=Request.Url.ToString().ToLower().IndexOf("codeodyssey.se") != -1 ? "UA-406884-2" : "UA-406884-1" %>");
pageTracker._initData();
pageTracker._trackPageview(<%=ViewData["TrackPageView"] %>);
//]]>
</script>

Mål-koden för kommentarerna

Kommentarsfunktionen har jag nyligen gjort om så den följer PGR-mönstret och här hade jag lite svårare att lägga in spårningskoden på rätt ställe. Provade en del olika grejer där jag bland annat försökte lägga in det på den adress som man postar kommentaren till (bloggurl plus "/add-comment" på slutet). Vet inte exakt varför det inte gick med regulärt uttryck men antar att det beror på att den sidan returnerar en 302-respons (see other adress). Kan vara så att man inte kan tracka såna sidor med Google Analytics.

Sen kom jag på en enklare lösning. La på ett onclick-event på submit-knappen istället där jag anropar pagetrackern och det fungerar nu fint.

<input onclick="pageTracker._trackPageview('/comment-submitted');" type="submit" value="<%= GetLocalResourceObject(" />" class="submit-btn"/>

Först trodde jag att huvudskriptet där tracken inkluderas var tvungen att ligga ovanför eventet, med det visade det sig att den inte behövde, den har ju redan registrerats när sidan laddas. Google Analytics Tracking API var användbart för felsökning och genom att anropa _getAccount() kunde jag bekräfta att eventet hade kontakt med trackern.

Dags att mäta, förändra och förfina

Så nu är det bara att luta sig tillbaks och börja analyserar hur kommentarer och kontaktmeddelande postas på Code Odyssey. Kom gärna med förslag och ideér hur sidan kan göras bättre för att uppmuntra fler kommentarer och rikare samtal.

Vill du själv göra samma sak och använder WordPress kan jag rekommendera How To Track Wordpress Signups and Comments With Google Analytics hos TD Creative.

By Jesper Lind

ASP.NET MVC - Kommentarsfunktion med PRG Pattern

Har programmerat om kommentars-funktionen på bloggen och den följer nu designmönstret Post/Redirect/Get (PRG Pattern). Det innebär att när en kommentar postas så görs det till en specifik url som bara accepterar Http-verbet "Post". Sen skickas besökaren tillbaks till inläggets vanliga url med en ankarlänk till den nyligen postade kommentaren tillagd på slutet.

Innan denna förändring så postades kommentaren direkt till samma vy/url som även används till att visa inlägget.

Fördelar med PRG-mönstret

Fördelen med att följa PRG-mönstret är att kommentaren inte dubbelpostas ifall användaren klickar på tillbaka-knappen i sin webbläsare. Nuförtiden så varnar ju dock de flesta moderna webbläsare att en ompostning kommer ske, men det är ofta man inte tänker på att det inte går att backa och det är ett rätt störande meddelande som man slipper med PRG-mönstret.

Läs mer hos Stephen Walter som också beskriver samma mönster med ASP.NET MVC i artikeln "Call RedirectToAction after Submitting a Form".

Hur man gör för att redirecta till en länk med ankare på slutet hittade jag på en japansk blogg.

Hur det är implementerat här på bloggen

Tänkte visa lite exempelkod som är ungefär den kod som körs på bloggen just nu.

Först de routes som har med att visa inläggen och posta kommentarer:

routes.MapRoute(
    "BlogPostAddComment",
    archiveRoute + "/{year}/{month}/{day}/{slug}/add-comment", //arkiv/2008/05/29/radonförgiftning#ankar-länk
    new { controller = "Blog", action = "AddComment" }
    );

routes.MapRoute(
    "BlogPostWithAnchor",
    archiveRoute + "/{year}/{month}/{day}/{slug}#{anchor}", //arkiv/2008/05/29/radonförgiftning#ankar-länk
    new { controller = "Blog", action = "ArchiveBySlug" }
    );

routes.MapRoute(
    "BlogPost",
    archiveRoute + "/{year}/{month}/{day}/{slug}", //arkiv/2008/05/29/radonförgiftning
    new { controller = "Blog", action = "ArchiveBySlug" }
    );
	}

Sen det Action i Controllern som sätter in kommentarern i databasen och redirectar användaren till rätt vy igen. Det sker lite mer blanda annat har vi ett anti-spam-skydd och kommentarern mailas även till mig (nytt för idag så nu kommer jag vara mer alert på nya kommentarer).

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddComment(int year, int month, int day, string slug)
{

    var url = Request.Form["url"];
    int postid = Convert.ToInt32(Request.Form["postid"]);
    var name = Request.Form["name"];
    var body = Request.Form["body"];
    var email = Request.Form["mail"];

    int addedCommentId = blogService.AddComment(postid, name, body, email, url);

    return RedirectToRoute("BlogPostWithAnchor", new { action = "ArchiveBySlug", year = year, month = month, day = day, slug = slug, anchor = "comment-"+addedCommentId });

}
By Jesper Lind

Glöm inte att sätta oanvända klasser till Obsolete

Något som jag måste bli bättre på är att markera oanvända kodstycken som Obsolete. Det innebär alltså att klassen helst inte ska användas längre och har tagit ett steg vidare mot avveckling. Man kan skriva ett meddelande också som kommer upp i kompilern som påminnelse för att man ska ta bort dem.

Exempel från MSDN.

 

[Obsolete("This class is obsolete; use class B instead")]
class A{
public void F() {}
}
By Jesper Lind

Filtrera DataSet

Ja även fast det är spännande att jobba med nya databas-tekniker som LinqToSQL och Subsonic, så kan det vara bra att kunna lite hederliga DataSet-operationer.

Just nu så hade jag ett problem med Subsonics Paging-funktioner, där en Where-sats inte kom med. Jag fick dubbla poster hur jag än gjorde. Då kom jag på att jag kunde filtrera DataSet:et i efterhand och skrev följande lilla funktion.

private static DataSet FilterDataSet(DataSet ds,string filter)
{
    DataSet clone = ds.Clone();

    DataRow[] foundRows = ds.Tables[0].Select(filter);

    for (int i = 0; i < foundRows.Length; i++)
    {
        DataRow row = foundRows[i];
        clone.Tables[0].ImportRow(row);
    }

    return clone;
}

Anropar den sedan på följande vis. I detta fallet ville jag ha ut texter på en visst språk, och inte alla språk i databas-tabellen som Subsonic envisade sig med att returnera.

return FilterDataSet(q.ExecuteDataSet(),"Culture='sv-SE'");

Är du sugen på liknade exempel, så kan jag rekommendera tidigare inlägg där vi skrivit om hur man sorterar ett DataTable eller hur man skapar nya kolumner i DataSet och sparar det i Cacheminne.

By Jesper Lind

Parametervärden i resursfiler

Tänkte visa ett exempel på hur man kan byta ut parametrar i resursfiler. I mitt exempel hämtar jag ut en text för copyright-meddelande och byter ut en parameter {0} med string.format mot aktuellt år.

I resursfilern har jag en nyckel som heter Copyright och lägger in följande sträng.

Copyright © 2004-{0}, Code Odyssey, all rights reserved.

Sen på aspx-sidan lägger jag in strängen på följade vis.

<%=String.Format((String)GetLocalResourceObject("Copyright"), DateTime.Today.Year )%>

Och så slipper man gå in och ändra vid varje nyår.

Sen angående Copyright så funderar jag på att ändral licens på denna sajten. Kanske Copyleft eller nån Creative Common-variant.

By Jesper Lind

Skapa en ExpressionBuilder för Application-variabler

Hade ett litet problem när jag ville få in en Application-variabel i SelectParameters till en SqlDataSource. Sessions-variabler går ju men ej Application. Se en tutorial för vilka typer av parametrar man kan skicka med som standard.

Googlade runt lite och hittade infomation om något som heter ExpressionBuilder i ramverket som jag aldrig använt tidigare. Med detta kan man skapa egna prefix och koppla det till klasser som tar ut den data man vill åt, och man kan binda värden i kontroller. Så min idé var att skapa en ExpressionBuilder som hämtade ut Application-variabler och det gick väldigt bra.

Här kommer jag gå igenom de steg jag gjorde, det hela handlade om att jag ville åt en egen parameter för vilken @Culture SqlDataSource skulle få som inparameter.

I Global.asx skapar jag parametern baserat på domännamnet. Är det en .se-domän vill jag ha "sv-SE" och för alla andra "en-GB".

protected void Application_BeginRequest(Object sender, EventArgs e)
{
        //Check if domain is swedish
       Application["CurrentCulture"] = HttpContext.Current.Request.Url.ToString().IndexOf("codeodyssey.se") != -1 ? "sv-SE" : "en-GB";
}

Min SqlDataSource ser ut ungefär så här:

<asp:SqlDataSource
    ID="sdsBlogPosts"
    runat="server"
    ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
    SelectCommand="SELECT Title, Message FROM BlogPost WHERE Culture=@Culture"
    DataSourceMode="DataSet">
    <SelectParameters>
        <asp:Parameter Name="Culture" DefaultValue="<%$ AppVar:CurrentCulture%>" />
    </SelectParameters>
</asp:SqlDataSource>

För att få ExpressionBuilder att fungera så deklarerar man den i Web.config.

<compilation debug="false">
  <expressionBuilders>
    <add expressionPrefix="AppVar" type="AppVarExpressionBuilder, __code"/>
  </expressionBuilders>
</compilation>

Och sen själva klassen lägger man i App_Code.

using System.CodeDom;
using System.Web.Compilation;
using System.Web.UI;

/// <summary>
/// Summary description for AppVarExpressionBuilder
/// </summary>

public class AppVarExpressionBuilder : ExpressionBuilder
{
    public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
    {
        CodeTypeReferenceExpression targetClass = new CodeTypeReferenceExpression(typeof(AppVarExpressionBuilder));
        const string targetMethod = "GetIdentity";
        CodeExpression methodParameter = new CodePrimitiveExpression(entry.Expression.Trim());
        return new CodeMethodInvokeExpression(targetClass, targetMethod, methodParameter);
    }

    public static object GetIdentity(string param)
    {
        string returnString = string.Empty;
        if (System.Web.HttpContext.Current.Application[param]!=null)
        {
            returnString = System.Web.HttpContext.Current.Application[param].ToString();
        }
        return returnString;
    }
}

Hade stor hjälp av ett artikel av Peter Kellner för att lista ut hur ExpressionBuilders fungerar så tack för den.

By Jesper Lind

ISAPI Rewrite 3 på Bineros servrar

Vi har börjat testa lite smått på några webbsajter att använda ISAPI-modulen som finns installerad hos Binero och det fungerar ganska bra. Vi har till och med fått igång ett första MVC-projekt på .NET 3.5. Riktigt kul med ett webbhotell som är framåt och förstår vilken teknik vi utvecklare vill ha.

Det tog lite tid att lista ut hur man fick igång det eftersom Bineros förklaring av Rewrite 3 inte är så utförlig. De har manualen på deras sajt så där kan man lära sig lite (funkar dock inte med Firefox 3). Helicon Tech som ligger bakom modulen rekommenderas också.

Mest hjälp för att få igång det hade jag av av några trådar på Aspsidan och webForum.

Något att tänka på är att de ändringar man gör i Script Mappings i Bineros kontrollpanel tar ett tag innan de slår igenom. Kommer ihåg den första kvällen då jag kämpade med det in på småtimmarna men hade ingen lycka. Provade mängder av olika konfigurationer och gav sist upp och bara lämnade det. Nästa morgon när jag laddade upp sidan så bara det fungerade!

Så nu ska jag förklara det jag kommit på hittills.

Det första man måste göra är att lägga till en *-mappning enligt följande.

Filetype: *
Executable: Aspnet2
is wildcard: false
is script engine: false
verify that file exists: false

När du ändå är där kan du även lägga till .asax eftersom Binero verkar ha glömt det i standardinställningen. Se bilden för exempel på de två inställningarna längst ner.

 

Sen är det dags att knåpa ihop en httpd.ini (uppdatering: filen ska ha namnet .htaccess och inget annat) som styr hur ISAPI Rewrite 3 ska uppföra sig. Här under ser ni våran som fungerar fint om man vill ta bort .aspx ändelserna. Jag har dock inte lyckats att exkludera vissa mappar, så om någon kan se vad det är för fel på sista raden som ska blocka mappen "MappSomInteSkaOmskrivas" så uppskattar jag tips. Uppdatering: Denna rad måste ju givetvis ligga överst för att de andra reglerna ska ignoreras. Har nu uppdaterat skriptet som det ska se ut och då funkar det mycket bättre.

# Helicon ISAPI_Rewrite configuration file
# Version 3.1.0.34

RewriteEngine on

#Exclude folder
RewriteRule /(?!(?:Images|Css|MappSomInteSkaOmskrivas)/.*).* - [L]

#Redirect extension requests to avoid duplicate content
RewriteRule ^([^?]+)\.aspx$ $1 [NC,R=301]

#Internally add extensions to request
RewriteCond %{REQUEST_FILENAME}.aspx -f
RewriteRule (.*) $1.aspx
By Jesper Lind

.NET-utveckling på en Mac fungerar fint

Oj vilket blogg-uppehåll det varit här, hoppas ni inte saknat oss för mycket. Nu kommer vi köra igång igen och hoppas på att kunna skriva regelbundet här om webbutveckling som vi brukar.

Denna gång bloggar jag från en MacBook Pro, så se fram emot många OS X-relaterade artiklar i framtiden.

Men ni Windows-geeks behöver inte misströsta. Kör Vista i en boot camp installation via VMware Fusion, så det kommer finnas mycket matnyttigt för den plattformen också.

Man kan säga att mitt dataliv har blivit rätt mycket trevligare sen jag kunde pensionera den Dell-burk jag använde innan (och nej Dell har inte hört av sig för att kompensera för den dåliga upplevelsen). Det var inte helt smidigt att ha en dator som stängde av sig själv titt som tätt när den blev överhettat. En del bekanta brukar le så smått när jag berätta om hur jag fick sitta på balkongen med kylklampar under datorn, vid krävande operationer.

Jag har tidigare lovat en utvärdering av hur det fungerar att använda Visual Studio på en Mac via Vista som slavsystem, och kan säga att det går helt okej. I min maskin har jag 2 GB ram och det duger bra åt att köra de båda operativen sida vid sida. Funkar till och med bra att använda det processorintensiva pluginet Resharper som jag blivit så förtjust i.

VMware Fusion som gör det möjligt att köra OS X och Windows (eller andra operativ) samtidigt har många bra inställningar där man bland annat kan ställa in hur mycket ram man vill dedikera till slavsystemet och det går också att simulera två processorer med hjälp av mjukvaran.

Något som är lite svårt i början är att vänja sig vid alla teckenkombinationer. Nu har jag hittat hur jag skriver in det mesta, men saknar fortfarande vänster måsvinge. Så här har jag lite forskning kvar innan jag kodar lika effektivt som på en PC.

Så jag kan gärna rekommendera även gamla Windows-rävar att i alla fall fundera på att switcha till Mac. OS X är ett väldigt skönt operativ som gör de mesta uppgifter till en bättre upplevelse. Mac-masknerna är också väldigt snygga och har väl integrerad hårdvara. Något som jag uppskattar som inte är så intresserad av att mecka med olika drivrutiner fram och tillbaks. På maccen funkar det mesta med en gång, och allt OS X är väldigt lättanvänt.

By Jesper Lind