Nu på morgonen så släpptes Visual Studio 2010 RC samt .NET 4.0 RC på MSDN. De som inte har en MSDN Subscription får vänta med nedladdningen tills imorgon.

Mer information samt länk till nedladdning finns här:
http://msdn.microsoft.com/en-gb/vstudio/dd582936.aspx

Svenska VS2010-sida hittar du här:
http://www.msdn.se/vs2010

Jag sitter själv och laddar ned filerna nu! :-)

Ett vanligt problem när man utvecklar är att man gång på gång måste skriva funktionalitet för att mappa en klass till en annan. När man arbetar med ASP.NET MVC så händer det ofta att man har en modell som är anpassad för en viss vy, men datan man tar emot kan innehålla samma rådata, men med ett helt annat upplägg. Det vi får göra då är att mappa om det mottagna objektet till det som vi sedan skall använda till vyn.

För att göra mappningen enklare så finns det ett open source-bibliotek som heter AutoMapper, vilket ni kan hitta här:

http://www.codeplex.com/AutoMapper

Fördelen med AutoMapper är att det blir mycket enklare att göra dessa mappningar, då vi får använda fluent configuration (http://martinfowler.com/bliki/FluentInterface.html) för att sätta upp mappningarna, och sedan välja vilka två objekt vi vill skicka data mellan.

Sätt upp projektet

Vi kommer att använda oss utav ett ASP.NET MVC 2-projekt. När vi skapar projektet så väljer vi att skapa ett test-projekt (jag använder Visual Studio Unit Test i exemplet, men ni kan använda MbUnit, NUnit, eller vad som nu faller er i smaken). Till detta skapar vi även ett klassbibliotek för domänmodellen.

ASP.NET MVC-projektet, samt testprojektet måste ha referenser till AutoMapper-dll:en, vilken ni kan ladda ned på CodePlex.

I ASP.NET MVC-projektet så skapar vi en ny modell, kallad CustomerDto. Utseendet på denna blir:

namespace AutoMapperMvc.Models
{
    public class CustomerDto
    {
        public string CustomerId { get; set; }
        public string FullName { get; set; }
        public string[] PhoneNumbers { get; set; }
    }
}

Vi behöver även en domänmodell som vi kommer att koppla mot CustomerDto. Den består av två klasser och en enum:

1 - Class Diagram

Customer-klassen är kunden i sig. Varje kund kan ha flera telefonnummer, och varje telefonnummer kan vara satt som antingen Work eller Home.

Customer:

using System.Collections.Generic;
 
namespace AutoMapperMvc.Domain
{
    public class Customer
    {
        public int CustomerId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public List<PhoneNumber> PhoneNumbers { get; set; }
    }
}

PhoneNumber:

namespace AutoMapperMvc.Domain
{
    public class PhoneNumber
    {
        public PhoneNumberTypeEnum Type { get; set; }
        public string Number { get; set; }
    }
}

PhoneNumberEnum:

namespace AutoMapperMvc.Domain
{
    public enum PhoneNumberTypeEnum
    {
        Work,
        Home,
    }
}

Sätt upp AutoMapper

För att kunna mappa upp objekten så behöver vi sätta upp AutoMapper. Det finns två olika sätt att göra det på: antingen direkt i controllern efter behov, eller i en statisk metod som körs när applikationen startas. Vi kommer att ha en statisk metod då det gör att hela applikationen kan använda sig utav mappningen.

I roten för vårt ASP.NET MVC-projekt skapar vi en fil som heter AutoMapperBootstrapper.cs. Den kan självklart ligga var som helst, och kommer inte att följa med vid produktionssättning då den ändå hamnar i vår assembly.

I den här filen så skapar vi en statisk metod som skapar en grundläggande mappning mellan Customer-objektet i Domain-projektet och CustomerDto-objektet i ASP.NET MVC-projektet.

using AutoMapper;
using AutoMapperMvc.Domain;
using AutoMapperMvc.Models;
 
namespace AutoMapperMvc
{
    public class AutoMapperBootstrapper
    {
        public static void Initialize()
        {
            Mapper.CreateMap<Customer, CustomerDto>();
        }
    }
}

För att metoden skall köras så lägger vi även till den här raden i Application_Start() i global.asax:

AutoMapperBootstrapper.Initialize();

Det som händer nu är att AutoMapper försöker tolka våra egenskaper så gott det kan.

Sätt upp controllern

För att använda oss utav mappningen så skapar vi en ny controller vid namn ”CustomerController”. Här har vi en ActionResult-metod vid namn ”Index”.

Det första vi gör är att se till att vi har någon data, så vi skapar upp en enkel metod direkt i controllern som ger oss den datan vi behöver för att kunna testa mappningen. Datan bör dock självklart komma från ett annat håll, som ett repository, men för att enkelt kunna visa det relevanta här så har jag valt att bara ha en metod för det direkt här.

private Customer[] GetCustomers()
{
    return new List<Customer>() {
        new Customer() {
            CustomerId = 1,
            FirstName = "Mikael",
            LastName = "Söderström",
            PhoneNumbers = new List<PhoneNumber>() {
                new PhoneNumber() {
                    Number = "555-12 34 56",
                    Type = PhoneNumberTypeEnum.Home
                },
                new PhoneNumber() {
                    Number = "555-65 43 21",
                    Type = PhoneNumberTypeEnum.Work
                }
            }
        },
        new Customer() {
            CustomerId = 2,
            FirstName = "Bill",
            LastName = "Gates",
            PhoneNumbers = new List<PhoneNumber>() {
                new PhoneNumber() {
                    Number = "555-12 34 56",
                    Type = PhoneNumberTypeEnum.Home
                },
                new PhoneNumber() {
                    Number = "555-65 43 21",
                    Type = PhoneNumberTypeEnum.Work
                }
            }
        }
    }.ToArray();
}

Det som returneras är två Customer-objekt. I vår vy vill vi dock visa upp CustomerDto, så vi väljer att mappa om objektet till det.

public ActionResult Index()
{
    Customer[] customers = GetCustomers();
 
    CustomerDto[] mappedCustomers = Mapper.Map<Customer[], CustomerDto[]>(customers);
 
    return View(mappedCustomers);
}

Mapper.Map är en generisk metod som har två typer, källtypen och måltypen. Vi vill här omvandla ett Customer-objekt till ett CustomerDto-objekt, precis som vi angav i Bootstrappern där vi skapade mappningen.

Sätt upp vyn

För att testa hur väl mappningen har gått så skapar vi en vy som är hårt typad till CustomerDto.

Vi skapar en vy där vi kan lista CustomerId, Fullname och Phone numbers.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<AutoMapperMvc.Models.CustomerDto>>" %>
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
    <h2>Customers</h2>
 
    <table>
        <tr>
            <th>
                CustomerId
            </th>
            <th>
                Fullname
            </th>
            <th>
                Phone numbers
            </th>
        </tr>
    <% foreach (var item in Model) { %>
        <tr>
            <td>
                <%= Html.Encode(item.CustomerId) %>
            </td>
            <td>
                <%= Html.Encode(item.FullName) %>
            </td>
            <td>
                <ul>
                <% foreach (string number in item.PhoneNumbers) { %>
                    <li><%=number%></li>
                <% } %>
                </ul>
            </td>
        </tr>
    <% } %>
    </table>
</asp:Content>

Om vi kikar på resultatet så kan vi se att vi får det här:

2 - Mapper Result

CustomerId har mappats korrekt, Fullname är tomt och Phone numbers innehåller vilken typ det är.

Anledningen till att det har blivit så här är för att CustomerId används i både Customer och i CustomerDto. Det gör att AutoMapper enkelt kan översätta värdet. FullName kan det dock ej hitta då vi i Customer har FirstName och LastName. Det finns inget som säger att de tillsammans skall bli FullName. Till sist så har vi Phone numbers, vilket är en collection med PhoneNumber, vilket inte AutoMapper känner till.

Anpassa mappningen efter modellerna

Det finns olika sätt att lösa det här på. En väldigt intressant feature i AutoMapper är att det automatiskt känner av ifall det finns en metod som har ”Get” som prefix. Om det finns ett sådant så försöker den använda den för att sätta värdet.

Vi vill till exempel ha FirstName och LastName ihopsatt till FullName. För att lösa detta så kan vi ha en metod i Customer-objektet som vi kallar för ”GetFullName”. Där returnerar vi FirstName och LastName ihopsatt:

Nya Customer:

using System.Collections.Generic;
 
namespace AutoMapperMvc.Domain
{
    public class Customer
    {
        public int CustomerId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public List<PhoneNumber> PhoneNumbers { get; set; }
 
        public string GetFullName()
        {
            return FirstName + " " + LastName;
        }
    }
}

Om vi kikar på sidan igen så ser vi att vi nu har det här:

3 - FullName

Det som hände nu var att AutoMapper såg att vi hade en metod kallad GetFullName, och satte då värdet från den metoden till egenskapen FullName.

Skapa en resolver

Alla telefonnummer ser dock fortfarande ganska lustiga ut. I Customer så har vi en collection med typen PhoneNumber, medan vi i CustomerDto har en collection med strängar. För att kunna ange för AutoMapper vilken property som skall användas så får vi skapa en resolver.

Vi går tillbaka till AutoMapperBootstrapper som vi skapade tidigare.

Vi skapar nu en ny klass i den här filen kallad PhoneNumberResolver. Den skall ärva ValueResolver<Customer, string[]>. Precis som tidigare så är det källtypen och måltypen som anges.

Nästa steg är att köra override på metoden ResolveCore, vilket är av typen TDestination (i det här fallet string[]).

public class PhoneNumbersResolver : ValueResolver<Customer, string[]>
{
    protected override string[] ResolveCore(Customer source)
    {
        List<string> numbers = new List<string>();
 
        foreach (PhoneNumber number in source.PhoneNumbers)
        {
            numbers.Add(number.Number);
        }
 
        return numbers.ToArray();
    }
}

ResolveCore tar emot en parameter som innehåller hela det aktuella objektet. Det vi är intresserade av är telefonnummren, så vi sparar undan dessa i en collection och skickar tillbaka. Vilken typ av nummer det är bryr vi oss inte om då det ändå inte syns i vyn.

För att registrera resolvern så får vi använda oss utav fluent configuration.

Vi modifierar mappningen som vi tidigare skapade i bootstrappern så att den ser ut så här:

Mapper.CreateMap<Customer, CustomerDto>()
    .ForMember(dto => dto.PhoneNumbers, opt => opt.ResolveUsing<PhoneNumbersResolver>());

Det som händer nu är att vi för alla tillfällen där Customer mappas om till CustomerDto så skall PhoneNumberResolver användas för PhoneNumbers i CustomerDto.

För att se hur det påverkade vyn så kompilerar vi och går in på sidan igen:

4 - Phone numbers

När vi nu mappar om objekten så används egenskapen med telefonnumret istället för PhoneNumbers-objektet i sig.

Skapa en formatter

Om vi istället för att bara ange vilket fält som skall användas, vill ange hur fältet skall formateras så kan vi använda en formatter.

Precis som för resolvern så skall vi skapa en klass i bootstrapper-filen.

Istället för att bara visa siffran med CustomerId, så vill vi skriva ut ”ID: ” och sedan siffran. Det vi tar emot kommer att vara en integer, och det vi returnerar kommer att vara en sträng. Vi kommer därför att ärva ValueFormatter<int> i klassen, och köra override på FormatValueCore.

public class CustomerIdFormatter : ValueFormatter<int>
{
    protected override string FormatValueCore(int value)
    {
        return "ID: " + value.ToString();
    }
}

Det som sker här är att vi får värdet direkt som en integer, och returnerar sedan texten så som vi vill ha den. Den kommer då att få det utseendet direkt vid mappningen.

För att köra formattern på ID:t så får vi precis som tidigare använda oss utav fluent configuration, så vi lägger till en formatter:

Mapper.CreateMap<Customer, CustomerDto>()
    .ForMember(dto => dto.PhoneNumbers, opt => opt.ResolveUsing<PhoneNumbersResolver>())
    .ForMember(dto => dto.CustomerId, opt => opt.AddFormatter<CustomerIdFormatter>());

Vi anger här att CustomerId skall köras med formattern vi just skapade.

Resultatet blir:

5 - Id

Testa mappningarna

För att vara säker på att mappningarna fungerar så kan vi skapa enhetstester för det. Då det inte går att köra tester i Visual Web Developer och de enklare versionerna av Visual Studio så går jag bara igenom snabbt hur testerna kan se ut.

Börja med att skapa en ny klass i testprojektet, AutoMapperBootstrapperTest.cs.

Vi väljer här att ha två tester, ett som kollar om konfigurationen är giltig, och en som kollar om värdena har satts rätt.

AutoMapper innehåller en metod som heter Mapper.AssertConfigurationIsValid(), och som har som funktion att kolla igenom konfigurationen och se så att allt står rätt till. Vi kommer att använda den i konfigurationstestet.

Testet i sig ser ut på följande sätt:

using System.Collections.Generic;
using AutoMapper;
using AutoMapperMvc.Domain;
using AutoMapperMvc.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
 
namespace AutoMapperMvc.Tests
{
    [TestClass]
    public class AutoMapperBootstrapperTest
    {
        [TestMethod]
        public void TestAutoMapperBootstrapper()
        {
            AutoMapperBootstrapper.Initialize();
            Mapper.AssertConfigurationIsValid();
        }
 
        [TestMethod]
        public void TestAutoMapperMapping()
        {
            AutoMapperBootstrapper.Initialize();
 
            var customer = new Customer()
            {
                CustomerId = 1,
                FirstName = "Mikael",
                LastName = "Söderström",
                PhoneNumbers = new List<PhoneNumber>() {
                   new PhoneNumber() {
                    Number = "12345",
                    Type = PhoneNumberTypeEnum.Home
                   }
                }
            };
 
            var result = Mapper.Map<Customer, CustomerDto>(customer);
 
            Assert.AreEqual(result.FullName, "Mikael Söderström");
        }
    }
}

Om vi testkör det så bör båda testen vara gröna. Om vi däremot byter namn på metoden GetFullName i Customer till till exempel GetName, så kommer vi att testet inte att gå igenom, då det inte finns någon mappning mot FullName i CustomerDto.

Det här gör att vi enkelt kan se om någon mappning går fel under tiden som vi skapar upp nya objekt och mappar dem.

Det finns även väldigt många andra funktioner i AutoMapper, så jag rekommenderar att ni laddar ned det och testar. Det gör det väldigt enkelt att mappa objekt och testa mappningarna, vilket kan spara mycket tid.

Trots att vi fortfarande inte har skrivit en enda rad kod än så länge så har vi en webbsajt med medlemshantering. Den ser dock lite trist ut, och har inte så mycket funktionalitet, så det ska vi ta och ändra på nu.

För att piffa upp designen snabbt och enkelt så går vi in på ASP.NET MVC Design Template Gallery:
http://www.asp.net/mvc/gallery

Här kan vi ladda ned färdiga mallar som kan användas direkt med ASP.NET MVC. Välj den designen du vill ha och ladda ned. Sedan ersätter du de befintliga filerna med de som finns i den nedladdade filen. Jag själv väljer att ladda ned "Happy Forest", ett helskönt tema.

Resultatet är nu det här, vilket ser mycket bättre ut:

1-happyforest

För att få sidan lite mer personlig så ska vi ta och byta ut lite texter. Vi börjar med "My Sample MVC Application" som vi kan hitta i toppen här (det kan vara någon annan text beroende på vilket tema du har valt). Den här texten kan vi ändra i den så kallade "master-sidan". Vi hittar den under \View\Shared\Site.Master i Solution explorer. Anledningen till att den ligger under Shared är på grund av att den används av flera olika sidor. Klickar vi t.ex. på "About" så kan vi se att samma mall används där. Det enda som skiljer är det som finns i den vita rutan med innehåll. Det gör att det blir mycket enklare att anpassa utseendet på sajten.

Om vi öppnar upp den här filen så kan vi se att vi har rubriken i ren text här. Genom att ändra den till något annat så slår ändringen igenom på alla olika sidor. I den här filen så har vi även menyn, vilket gör att vi enkelt kan lägga till och ta bort sidor i den.

För att ändra i de enskilda vyerna så kan vi kika i Views-mappen. Just startsidan finns under \View\Home\Index.aspx. Genom att ändra i den så kan vi få till en mer personlig startsida.

Efter att ha gjort lite ändringar så ser min sida ut så här:

2-modifiedsite

Nu vill jag även tillåta mina besökare att registrera sig på sidan, och därmed komma åt material som inte anonyma användare kommer åt.

Vi börjar med att klicka på "Log on"-länken uppe till höger. Den innehåller ett färdigt formulär som vi kan använda för att logga in på sidan. Det finns även en länk till en sida där vi kan skapa användare.

Om vi väljer att fylla i formuläret för att skapa användare så kan vi se på direkten att vi är inloggade med det användarnamnet vi just angav. Det som hände var att vi fick en databas skapad åt oss genom ASP.NETs inbyggda medlemshantering (kom i .NET 2.0).

För att lägga till, ta bort och ändra medlemmar snabbt och enkelt så kan vi använda ett inbyggt verktyg för det.

I Visual Web Developer så väljer vi att i menyn klicka på Project -> ASP.NET Configuration. Det som händer nu är att en administrationssida öppnas för databasen vi använder.

3-configtool

Det ser inte ut att vara något speciellt, men det gör det möjligt för oss att enkelt hantera både användare och roller.

Vi kan när som helst öppna det här verktyget och arbeta med det.

Nu låter vi våra besökare registrera sig, men det finns fortfarande inga specialla privilegier för dem. Det vi vill ha är en sida för enbart medlemmar. Sidan skall nås genom att gå till /MembersOnly/ och skall innehålla lite information för dem.

Det första vi måste göra nu är att skapa en Controller för den nya sidan. Genom att högerklicka på Controllers, och välja Add -> Controller så kan vi skapa upp en ny controller.

4-addcontroller

I rutan som kommer upp så väljer vi att ge controllern namnet "MembersOnlyController". Kom ihåg att namnen på controllers alltid måste sluta med "Controller".

När vi har klickat på Add så får vi upp en fil med det här innehållet:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
 
namespace FirstProject.Controllers
{
    public class MembersOnlyController : Controller
    {
        //
        // GET: /MembersOnly/
 
        public ActionResult Index()
        {
            return View();
        }
 
    }
}

Det den består utav är en metod som returnerar ett ActionResult. Det kommer i det här fallet att vara en vanlig vy vid namn Index.aspx. I kommentaren ovanför metoden så ser vi hur vi kommer åt metoden.

Om vi testar att surfa till den nya sidan nu så får vi:

5-error

Problemet är att vi fortfarande inte har kopplat någon vy till sidan. För att göra det så högerklickar vi i metoden och väljer "Add view...".

Det som kommer fram nu är en ruta där vi kan välja att ändra lite inställningar för vyn som skapas. Vi väljer att skippa det nu och klickar istället direkt på Add.

6-addview

Om vi ändrar lite i texten på sidan som skapas och testar att surfa till samma sida så får vi nu det här resultatet:

7-membersonlyview

Problemet är bara det att vem som helst kommer in på sidan, så hur kan vi förändra det?

För att kräva att användaren är autentiserad så väljer vi att sätta ett attribut på controllern.

[Authorize]
public class MembersOnlyController : Controller

Genom att lägga till Authorize för MembersOnlyController så ser vi till att vi enbart tillåter inloggade medlemmar att besöka vyerna som visas här. Om man inte är inloggad så skickas man automatiskt till inloggningssidan. Loggar man in där så skickas man tillbaka till medlemssidan och kan därefter se innehållet.

Genom att använda minimalt med kod så har vi nu en webbsajt med en rolig design, medlemshantering, innehåll för enbart inloggade och olika sidor med information. Vem sade att ASP.NET var svårt?

När vi nu har installerat verktygen så är det dags att skapa första projektet. Det vi kommer att skapa är ett ASP.NET MVC-projekt. Det finns två olika typer av ASP.NET-projekt, Web Forms och MVC. Web Forms är den första varianten som kom med .NET 1.0, och MVC är den nyare som kom efter .NET 3.5 SP 1. Skillnaden i dessa ligger i hur man jobbar med presentationen, men då de båda bygger på .NET så är det inte svårt att lära sig det andra när man har lärt sig det ena.

Det första vi ska göra är att skapa ett nytt projekt i Visual Web Developer. Vi börjar med att starta programmet, vilket ger en startskärm med lite olika alternativ. Det vi väljer här är att skapa ett nytt projekt.

1-newproject

Under Visual C# -> Web så finner vi ASP.NET MVC 2 Web Application, vilket är vad vi ska skapa upp.

2-createproject

Vi kallar projektet för "FirstProject", och låter "Solution name" vara detsamma. Klicka nu på OK.

En solution är en samling med projekt. Varje projekt är en enskild modul, och kan bestå av t.ex. en Silverlight-applikation, en webbsajt, en console-applikation eller kanske ett Xbox-spel (allt går att göra med .NET!).

När vi har skapat upp projektet så kan vi se att startsidan har stängts ned och vi till höger har en ruta med titeln "Solution explorer". Alla filer projekt och dess tillhörande filer kan vi hitta i denna ruta. Genom att dubbelklicka på någon fil så får vi upp den i editorn. Alla rutor, inklusive denna, kan flyttas runt på skärmen (även till andra skärmar), vilket gör att man får möjlighet att anpassa det efter de behov man har.

Om vi ser i Solution explorer så kan vi se att det inte bara är vanliga mappar och filer vi har där, utan även andra alternativ:

3-solutionexplorer

Om vi öppnar upp Properties så kan vi se att vi får en mängd olika alternativ. Det som finns här är unikt för det specifika projektet. Det som ändras här kommer att finnas kvar i inställningarna för projektet, även om vi stänger ned projektet och öppnar upp det igen.

Det vi kommer att ändra nu på en gång är webbserver-inställningarna. Som standard så används en inbyggd webbserver när man startar ett projekt i Visual Web Developer, men då vi vill kunna använda funktionaliteten i IIS i fortsättningen så väljer vi att under Web i Properties sätta webbservern till IIS (det kan kräva administratörsrättigheter, så då måste vi starta om Visual Web Developer genom att högerklicka på ikonen och välja att starta som administratör).

4-setupiis

För att skapa upp en virtuell katalog så klickar vi på knappen "Create Virtual Directory". Alla webbsajter man skapar bör ligga i en virtuell katalog, eller en egen web site i IIS.

Vi väljer nu att spara projektet.

Går vi in i IIS så kan vi se att en ny virtuell katalog har skapats med en mängd olika inställningar. Testa gärna att klicka runt bland dess inställningar för att utforska funktionerna lite!

5-iis

Går vi tillbaka till Visual Web Developer så kan vi se att vi har en fil kallad AssemblyInfo.cs under Properties, och det är precis vad namnet säger. När vi kompilerar projektet så skapas en assembly (dll-fil), och AssemblyInfo.cs anger olika egenskaper för assemblyn.

Om vi öppnar upp AssemblyInfo.cs så får vi upp en del olika egenskaper som förklarar vad projektet gör. Ta det som vana att fylla i dessa, då det gör det enklare att i efterhand se vad olika assemblies har för syften. Egenskaperna har inget med funktionaliteten i sig att göra, utan är helt och hållet för utvecklarna själva.

Går vi vidare i Solution explorer så kan vi även hitta References. Det är referenser till olika assemblies, med specifika användningsområden. Som exempel så behöver vi en referens till System.Data för att få tag i funktionerna som har med databashantering att göra, och System.Web för att få tillgång till de ASP.NET-specifika delarna.

Om vi inte kommer åt någon funktion så beror det ofta på att man inte har en referens till rätt assembly.

Om vi väljer att använda tredjepartskomponenter så måste vi även lägga till dem som referenser för att få tillgång till funktionaliteten.

Då det är ett ASP.NET MVC-projekt så får vi vissa mappar och filer från början som innehåller funktionalitet som underlättar för oss.

Mapparna Models, Views och Controllers är de som är MVC-specifika. MVC i sig förklarar hur presentationen skall hanteras. Om vi till exempel vill lista olika kunder så börjar vi med att ha en modell (klass) kallad "Kund" och egenskaper för kunden (till exempel Namn, Adress, Telefonnummer med mera). Vi behöver även en controller. Här plockar vi tag i alla kunder och returnerar till vyn, vilket är filen som skapar all HTML.

Utöver detta så finns filerna global.asax och web.config, vilka är standardfiler i alla webbprojekt.

I global.asax så kan vi till exempel registrera så kallade "routes" och annat. En route förklarar hur url:erna på sidan byggs ut. Vi kan till exempel ha http://www.sidan.se/customers/123 för att visa kunden med id satt till 123.

Web.config innehåller alla inställningar vi vill göra för sidan. Här kan vi samla connection strings, variabler som skall vara globala för sajten, registrera HttpModules och HttpHandlers och mycket annat. Det här är en av de absolut viktigaste filerna i ett projekt.

Så vad får vi i det här projektet som standard?

Redan nu så har vi faktiskt en fullt fungerande sajt. Genom att trycka på F5 så kompileras vårt projekt och startas upp i webbläsaren. Vi får en fråga om vi vill aktivera debugging, och där väljer vi att modifiera web.config för att tillåta det. Debugging är en av de absolut största fördelarna med Visual Web Developer och de andra Express-programmen (och självklart Visual Studio). Den här frågan får vi bara en gång under projektets arbete då inställningen sparas i web.config.

6-debug

När webbprojektet har kompilerats så startas Internet Explorer med sajten automatiskt. Vi kan här se att det vi fick från grunden var en enkel sida byggd på ASP.NET MVC, och som faktiskt har stöd för olika sidor (vyer), samt möjlighet att skapa användare och logga in med, trots att vi inte har skrivit en enda rad kod än så länge!

7-sitestarted

När man utvecklar så har verktygen en väldigt stor roll i hur effektivt man arbetar. När man skriver ASP.NET-sidor så kan man använda vilken texteditor som helst, inklusive det gamla hederliga Notepad, då det är ren text. För att man skall bli produktiv i sitt utvecklande så kan det dock vara bra att ha kompetenta verktyg som underlättar arbetet.

Har man tidigare arbetat med ASP så har det egentligen aldrig funnits några bra verktyg, dels på grund av att det kan vara svårt att erbjuda hjälpmedel för tolkning av koden då den är dynamisk, men även då det är en begränsad teknologi som ofta kräver att man använder sig utav komponenter skrivna i C++, Visual Basic och liknande.

När man arbetar med ASP.NET så ser situationen helt annorlunda ut. Här har vi (om vi inte själva har valt något annat) ett statiskt språk med ett enormt bibliotek - .NET Framework - till hjälp. De komponenter som används är även oftast .NET-baserade, vilket gör att verktyget vi sitter med bara behöver fokusera på en plattform. Det är det här som har gjort Visual Studio-familjen så pass uppskattad.

Förutom just texteditorn så finns det även andra verktyg som vi kan använda oss utav, och jag kommer att gå igenom ett par verktyg som jag rekommenderar att alla laddar ned. De verktyg jag listar här är helt gratis. Förutom dessa så finns det även andra som Resharper, FxCop m.m., men för en nybörjare så är det inget viktigt att grotta sig in i.

Till att brja med så kommer jag att visa några enkla program som jag brukar använda utöver utveckligen i sig. Sen kommer några verktyg som man absolut bör ha som nybörjare, då de erbjuder väldigt bra funktioner för arbetet.

Jag kommer inte att gå igenom särskilt djupgående hur de olika programmen fungerar, utan istället så kommer jag att introducera dem allt eftersom och visa hur de fungerar.

Paint.net
När man arbetar med webbsidor så behöver man ofta redigera bilder som används där. För professionellt bruk så används Photoshop väldigt mycket, men ett verktyg som jag själv föredrar då det är enkelt, gratis och ändå funktionsrikt är Paint.net. Det är ett bildredigeringsprogram som är skrivet i .NET och har stöd för att öppna och spara i de vanligaste formaten. Det finns även väldigt många tillägg att ladda ned. Om man är sugen på att skriva egna tillägg så kan detta göras i valfritt .NET-språk.

6-paintnet

http://www.getpaint.net

.NET Reflector
Efter att man har skrivit sin .NET-kod så kompilerar man den. När koden kompileras så blir det en dll-fil som inte kan öppnas upp direkt i någon editor. Då det är .NET så är det dock möjligt att med hjälp av Reflection läsa in en .NET-dll-fil och läsa av funktionaliteten i den.

Man kan även om man vill använda ildasm som ingår i SDK:n. Det är en disassembler för .NET och gör det möjligt att se IL-koden från en dll. Det kan dock vara svårt att tolka den, och på grund av det så har .NET Reflector blivit ett otroligt populärt verktyg och finns i de allra flesta .NET-utvecklares verktygslåda. Det är ett program som gör det möjligt att ta en dll-fil och se källkoden i C#, VB, IL och andra språk. Då basklasserna i .NET Framework är skrivna i .NET så kan man även se "källkoden" (inom citationstecken då det inte är den egentliga källkoden, utan en tolkning av den) för det.

7-reflector

http://www.red-gate.com/products/reflector/

Fiddler
Något som blir större och större är Ajax, vilket tillåter mer dynamiska webbplatser. Däremot så kan det vara svårt att felsöka Ajax-anropen då de skickas asynkront. För att underlätta felsökningen så finns det en del verktyg, som Developer Tools i Internet Explorer 8, Firebug i Firefox och även Fiddler, vilket fungerar med båda webbläsarna.

Fiddler fungerar som proxy och läser av alla anrop som webbläsarna gör, och samlar ihop all informationen i ett smidigt gränssnitt. Det går även att skapa egna anrop från gränssnittet för att enkelt kunna felsöka sina Ajax-anrop.

11-fiddler

http://www.fiddler2.com/fiddler2/

Web platform installer
Web Platform Installer är ett relativt nytt verktyg och är guld värt för alla utvecklare, särskilt de som är nybörjare. Det är ett program som hämtar en lista med alla senaste versionerna av olika verktyg och även webb-applikationer och gör det möjligt att ladda ned och installera dem.

Börja med att ladda ned det från URL:en nedan. Efter det så startar vi upp applikationen. Under Web Platform-fliken så börjar vi med att välja "Web server", vilket bör visa ungefär detsamma som bilden nedan. Jag väljer själv att installera allt här för enkelhetens skull, men känner du att du verkligen inte behöver vissa delar så kan du skippa dem och eventuellt installera senare om det skulle behövas.

1-wpi

När vi nu klickar på Install, så laddas de valda produkterna ned och installeras en efter en. Det kan ta en stund beroende på hur snabb uppkoppling du har, samt hur många alternativ du bockade i.

2-download

Nästa steg är att välja Frameworks and runtimes. Här kan vi bl.a. välja ASP.NET MVC och .NET Framework 4.0, men även PHP om vi så önskar. Här väljer vi det som finns under .NET Framework och installerar.

3-wpiaspnet

För att kunna arbeta med databaser så väljer vi under "Database" att installera SQL Server Express 2008 samt Management Studio Express. Det är en light-version av SQL Server 2008 samt ett väldigt kompetent verktyg för att hantera databasen.

4-wpidatabase

Då vi väljer att installera SQL Server så får vi välja mellan Windows Integrated Security och Mixed Mode Authentication. Det förstnämnda låter oss använda Windows-konton för att logga in i databasen, och det andra alternativet låter oss att dels använda Windows-konton, men även konton som vi skapar direkt i databasen. Väljer vi Mixed Mode så får vi ange ett standardlösenord för en användare vid namn "sa". Loggar vi in med denne så får vi fulla rättigheter på allt.

5-wpidatabasesetup1

När vi har gått igenom de här stegen så har vi det mesta som behövs för att vi skall komma vidare i utvecklingen.

http://www.microsoft.com/web/

Visual Studio Express 2010 Beta 2
Det absolut viktigaste vi kommer att installera är Visual Web Developer 2010. När det här skrivs så är Beta 2 den senaste versionen, och finns ej med i Web Platform Installer, så vi får ladda ned det själv, vilket vi kan göra på länken nedan. Vi kan antingen välja att hämta enbart Visual Web Developer, eller så kan vi välja att ladda ned ett helt paket (en iso-image) med alla Express-programmen i ett paket.

Om du väljer att ladda ned iso-avbildningen så får du använda t.ex. Daemon Tools för att öppna upp den direkt i Windows, eller så kan du bränna ut den på en skiva.

När vi kör det så får vi då välja att installera just Visual Web Developer 2010:

8-vsexpress

När vi installerar det så får vi förutom Visual Web Developer 2010 även tillägg för t.ex. ASP.NET MVC.

9-vsexpressinstall

Första gången man startar Visual Web Developer så skapas filer upp under "Mina Dokument" med inställningar, vilket gör att första starten går lite segare. När detta är gjort så kommer det dock att gå mycket snabbare.

Grattis, du har nu Visual Web Developer och kan därmed sätta igång med kodandet!

http://www.microsoft.com/express/future/default.aspx

Jag tog tidigare upp ett exempel som visar hur man kan använda dynamic och det nya objektet ExpandoObject för att skicka dynamiska variabler från controllern till vyn (http://weblogs.asp.net/mikaelsoderstrom/archive/2010/01/08/asp-net-mvc-2-0-amp-net-4-0-expandoobject.aspx). En nackdel med det kan vara att man t.ex. inte får någon intellisense, vilket man annars får när man använder statiska objekt.

För att lösa det så kan vi (förutom att skapa nya typer anpassade för den specifika vyn) använda Tuple, vilket även det är en nyhet i .NET 4.0. Tuple är ett objekt som kan innehålla flera hårt typade parametrar, vilket gör att det kan användas på väldigt många olika sätt.

Det vi ska göra här är att skapa upp en ny ActionResult-metod vid namn ”TupleView”. Den skall returnera exakt samma data som i det tidigare exemplet, men nu med ett statiskt objekt, vilket gör det hårt typat. Vi kommer ej heller i det här exemplet att skapa upp någon ny typ, utan kommer att förlita oss helt på vad Tuple har att erbjuda.

Det vi ska ha är en lista med customer-objekt. Varje customer-objekt ska ha en Name-property, en Age-property samt en Func<int, string> som skriver ut åldern på ett språk som är unikt för det enskilda objektet.

Då Tuple är ett generiskt objekt med möjlighet att ha en eller flera typer så ska vi skapa upp ett sådant för varje person, av typen ”string, int, Func<int, string>”. Vi lagrar dessa i en lista och returnerar till vyn.

public ActionResult TupleView()
{
    var customers = new List<Tuple<string, int, Func<int, string>>>();
 
    var Mikael = new Tuple<string, int, Func<int, string>>(
        "Mikael",
        24,
        new Func<int, string>(a => a.ToString() + "år gammal")
    );
    customers.Add(Mikael);
 
    var Bill = new Tuple<string, int, Func<int, string>>(
        "Bill",
        54,
        new Func<int, string>(a => a.ToString() + "years old")
    );
    customers.Add(Bill);
 
    return View(customers);
}

Vi har inga namngivna properties här som med ExpandoObject, men det vi får istället är statiskt typade objekt (även om vi kan välja att ha med dynamiska objekt i vår tuple).

När vi skriver ut dem i vyn så kan vi inte använda namnen på egenskaperna då de inte existerar, utan istället så får vi använda egenskaper som ”Item1”, ”Item2” osv. Item1 är det första värdet i Tuple-objektet, alltså namnet i det här fallet.

När vi skriver ut det i vyn så bör det se ut så här för att vi ska få samma resultat som i ExpandoObject-exemplet:

<h2>TupleView</h2>
 
<ul>
<% foreach (var item in Model) { %>
    <li><%= item.Item1 %>, <%= item.Item3(item.Item2) %></li>
<% } %>
</ul>

Det går alltså även här att använda Func<T> utan problem, vilket gör att det är möjligt att skicka med metoder i ett Tuple-objekt.

Jag har tidigare skrivit om ett nytt objekt i .NET 4.0, kallat ExpandoObject (http://weblogs.asp.net/mikaelsoderstrom/archive/2009/10/06/skapa-objekt-med-data-dynamiskt-med-expandoobject.aspx). Det är ett objekt som finns under System.Dynamic och som låter utvecklaren själv sätta egenskaper och metoder direkt på det istället för att skapa upp en separat klass.

Vid vissa tillfällen så är det överflödigt att skapa upp en ny klass då man ändå bara ska använda objektet på ett enda isolerat ställe, och då kan det vara smidigare att skapa upp ett dynamiskt objekt som skickas iväg direkt.

Ett scenario där man kan vilja skapa upp dynamiska objekt är i controllers i ASP.NET MVC. Oftast så är det specifika modeller för de specifika vyerna, vilket gör att det kan vara svårt att använda samma modell till flera vyer (om man inte vill ha extra egenskaper förstås). Genom att använda ExpandoObject här så kan vi skapa upp en ny modell för den aktuella vyn och returnera direkt.

Till att börja med så måste vi ha Visual Studio 2010 då det kräver .NET 4.0. Vi skapar upp ett ASP.NET MVC 2.0-projekt och lägger till en ActionResult-metod i HomeController med namnet ”DynamicView”. Allt är än så länge helt standard.

Då vi vill ha en dynamisk metod så kommer vi att börja med att skapa upp en instans av List<dynamic>. Anledningen till att det är av typen dynamic är för att vi måste ha en dynamisk instans av ExpandoObject för att kunna lägga till de egenskaper och metoder vi vill ha.

Action-metoden kommer att se ut så här:

public ActionResult DynamicView()
{
    List<dynamic> customers = new List<dynamic>();
 
    dynamic Mikael = new ExpandoObject();
    Mikael.Name = "Mikael";
    Mikael.Age = 24;
    Mikael.PrintAge = new Func<int, string>(a => a.ToString() + "år gammal");
    customers.Add(Mikael);
 
    dynamic Bill = new ExpandoObject();
    Bill.Name = "Bill";
    Bill.Age = 54;
    Bill.PrintAge = new Func<int, string>(a => a.ToString() + " years old");
    customers.Add(Bill);
 
    return View(customers);
}

Det som sker här är att två dynamiska objekt av typen ExpandoObject skapas upp. De får sedan två egenskaper, ”Name” och ”Age”. Utöver det så får de även en varsin metod ”PrintAge” som skriver ut åldern. Värt att notera är att det är två olika Func<T1, T2>, den första skriver ut det på svenska, och den andra på engelska. De här metoderna kommer att vara specifika för de enskilda dynamiska objekten.

Till sist så lägga objekten i en List<dynamic> som sedan returneras.

För att se resultat så måste vi skapa upp en vy som kan visa dessa. Vyn som skapas upp ska ha modellen ”System.Collections.Generic.List<dynamic>”. Till skillnad från statiska typer så får vi inte upp våra egenskaper direkt på sidan, utan vi måste istället skriva ut dem manuellt.

För att få fram namn och ålder kan vi skriva precis som om det vore en statisk modell:

<h2>DynamicView</h2>
 
<ul>
<% foreach (dynamic item in Model) { %>
    <li><%= item.Name %>, <%= item.Age %></li>
<% } %>
</ul>

Vi har dock en metod ”PrintAge” som tar emot åldern och skriver ut den. Genom att ändra koden ovan till nedanstående så får vi ut texten på två olika språk:

<h2>DynamicView</h2>
 
<ul>
<% foreach (dynamic item in Model) { %>
    <li><%= item.Name %>, <%= item.PrintAge(item.Age) %></li>
<% } %>
</ul>

Resultat:

result

Genom att använda dynamiska typer så är vi inte bunden till någon specifik typ, utan vi kan använda vilken dynamisk ActionResult-metod som helst så länge som den returnerar ett objekt med dessa egenskaper och metoder.

Jag fick ett mail tidigare idag där det stod att jag har blivit utnämnd till ASP.NET MVP. Det känns fantastiskt kul att ha blivit utnämnd till det. Wow! :-)

mvp_logo

Tack vare att ASP.NET MVC 2 använder sig utav Data Annotations så kan v enkelt använda oss utav dessa för att få till validering på våra sidor. Vi kan på enskilda fält sätta att till exempel värdet måste vara ett tal 10-20, att det inte får vara tomt m.m.

För att komma igång med exemplet som jag kommer att använda mig utav så skapa först en enkel modell:

using System.ComponentModel.DataAnnotations;
using MvcValidate.Code;
 
namespace MvcValidate.Models
{
    public class Customer
    {
        public int Id { get; set; }
 
        public string Name { get; set; }
 
        public int Age { get; set; }
    }
}

Vi behöver sedan en controller för att hantera alla Customer-objekt.

Kom ihåg att nedan inte är best practices, utan är bara en enkel controller som gör livet enklare för oss i exemplet.

using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using MvcValidate.Models;
 
namespace MvcValidate.Controllers
{
    public class CustomerController : Controller
    {
        public ActionResult Index()
        {
            return View(Customers.GetCustomers());
        }
 
        public ActionResult Details(int id)
        {
            Customer customer = Customers.GetCustomers().Where(c => c.Id == id).FirstOrDefault();
 
            return View(customer);
        }
 
        public ActionResult Create()
        {
            return View();
        }
 
        [HttpPost]
        public ActionResult Create(Customer customer)
        {
            try
            {
                Customers.GetCustomers().Add(customer);
 
                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }
 
        public ActionResult Edit(int id)
        {
            Customer customer = Customers.GetCustomers().Where(c => c.Id == id).FirstOrDefault();
 
            return View(customer);
        }
 
        [HttpPost]
        public ActionResult Edit(int id, Customer customer)
        {
            Customer currentCustomer = Customers.GetCustomers().Where(c => c.Id == id).FirstOrDefault();
 
            try
            {
                UpdateModel(currentCustomer);
 
                var yadda = Customers.GetCustomers();
 
                return RedirectToAction("Index");
            }
            catch
            {
                return View(Customers.GetCustomers().Where(c => c.Id == id).FirstOrDefault());
            }
        }
    }
 
    internal static class Customers
    {
        private static List<Customer> customers;
 
        public static List<Customer> GetCustomers()
        {
            if (customers != null)
                return customers;
 
            customers = new List<Customer>()
            {
                new Customer()
                {
                    Id = 1,
                    Name = "Mikael",
                    Age = 24
                },
                new Customer()
                {
                    Id = 2,
                    Name = "Nisse",
                    Age = 54
                }
            };
 
            return customers;
        }
    }
}

Förutom olika Action-metoder för olika scenarion så har jag lagt till en statisk klass som innehåller lite dummydata. Anledningen till att jag har den statisk är för att vi enkelt ska kunna se vilka ändringar som görs.

Nästa steg är att skapa upp vyer för alla Action-metoder. Skapa standardvyer för List, Create osv, och typa dem mot Customer som vi skapade tidigare.

Det vi har nu är en enkel sida baserad på ASP.NET MVC, vilken tillåter oss att lista, skapa och ändra kunder.

Få igång servervalidering

Ett problem i koden ovan är att värdena kan sättas lite vilt, då vi inte har någon kontroll över dem. För att se till att vi alltid har korrekt värden så kan vi använda Data Annotations, vilket låter oss sätta olika regler på olika fält. Dessa slår sedan igenom över hela sidan på de specifika fälten, vilket gör det enkelt att modifiera alla dessa i efterhand.

För att få till validering på vår befintliga modell så uppdaterar vi den så att den ser ut så här:

using System.ComponentModel.DataAnnotations;
using MvcValidate.Code;
 
namespace MvcValidate.Models
{
    public class Customer
    {
        [Required]
        public int Id { get; set; }
 
        [Required]
        public string Name { get; set; }
 
        [Range(18, 65)]
        [Required]
        public int Age { get; set; }
    }
}

I System.ComponentModel.DataAnnotations så har vi de attribut som används för de olika fälten.

Required-attributet visar att det aktuella fältet måste ha ett värde, annars går det inte igeom. Attributet Range som vi har satt på Age-fältet förklarar att värdet på Age måste vara en integer med ett värde mellan 18 och 65.

Det går utmärkt att använda flera attribut på ett enskilt fält, som vi gör för Age.

När vi har satt dessa attribut och kör sidan så har vi automatiskt fått validering av det som skickas in till modellen. Försöker vi t.ex. sätta ett tomt värde på Name och talet ”17” på Age så får vi det här resultatet när vi postar:

1 - Fail

Vi får nu alltså automatisk validering på våra fält, bara genom att sätta olika attribut på dem.

Få igång klientvalidering

En nackdel med valideringen ovan är att den kräver att vi postar sidan. Det absolut bästa, både för användarupplevelsen och för prestandan är att ingen postning görs, utan att vi direkt får information om att värdet är fel. För att få det gjort så kan vi använda oss utav den klientvalidering som finns i ASP.NET MVC 2.

För att aktivera klientvalidering på sidan så lägger vi till det här innan Html.BeginForm() i Edit.aspx:

<% Html.EnableClientValidation(); %>

Starta sidan igen efter ändringen. Plocka nu bort värdet för Name i edit-sidan och tryck på tab. Nu kan vi på en gång se att värdet är felaktigt., utan att behövs posta om sidan.

2 - ClientFail

Så hur fungerar det här?

Om vi kikar i källkoden för den renderade sidan så kan vi se att det finns en Json-sträng inbakad på sidan. Då alla mellanslag och radbrytningar är borttagna så kan det vara svårt att se vad som står, men genom att lägga till dessa så kan vi se att det som har lagts in är det här:

Sys.Mvc.FormValidation.enableClientValidation({
    "Fields": [{
        "FieldName": "Name",
        "ReplaceValidationMessageContents": true,
        "ValidationMessageId": "form0_Name_validationMessage",
        "ValidationRules": [{
            "ErrorMessage": "The Name field is required.",
            "ValidationParameters": {},
            "ValidationType": "required"
        }]
    },
    {
        "FieldName": "Age",
        "ReplaceValidationMessageContents": true,
        "ValidationMessageId": "form0_Age_validationMessage",
        "ValidationRules": [{
            "ErrorMessage": "The field Age must be between 18 and 65.",
            "ValidationParameters": {
                "minimum": 18,
                "maximum": 65
            },
            "ValidationType": "range"
        },
        {
            "ErrorMessage": "The Age field is required.",
            "ValidationParameters": {},
            "ValidationType": "required"
        }]
    }],
    "FormId": "form0"
},
null);

Det är alltså Json som innehåller de fält som skall valideras, samt information om hur valideringen skall gå till.

De intressanta bitarna här är ErrorMessage och ValidationParameters. För Name så finns inga ValidationParameters då den bara skall se till så att vi har värden, men för Age så har vi parametrar som används i bakgrunden för att se till så att de regler som vi själva har satt, stämmer överens med de som har skickats in.

Det som genereras är alltså baserat på det som vi själva har angett i modellen.

Om något blir fel så visas ErrorMessage på sidan.

Då det är ett rent JavaScript-objekt så innebär det att vi själva skulle kunna fånga upp det och använda för egen validering.

Skapa en anpassad validering

Det händer ofta att man har egna specialscenarion där man vill ha en anpassad validering då det inte finns något inbyggt för det. Som ett exempel så kan vi kräva att det finns ett visst antal mellanslag för namnen på vår sida.

Det första vi ska göra nu för att få till valideringen är att skapa själva attributet som skall sättas på fälten.

using System;
using System.ComponentModel.DataAnnotations;
 
namespace MvcValidate.Code
{
    public class NameAttribute : ValidationAttribute
    {
        public int Spaces { get; set; }
 
        public override bool IsValid(object value)
        {
            string name = value as string;
            int spaces = 0;
 
            if (String.IsNullOrEmpty(name))
                return false;
 
            foreach (char item in name)
            {
                if (item.Equals(' '))
                    spaces++;
            }
 
            return spaces.Equals(Spaces);
        }
    }
}

Istället för att ärva direkt från Attribute som vi normalt gör med attribut, så ärver vi från ValidationAttribute. ValidationAttribute innehåller ett par olika metoder, men den enda vi behöver är IsValid() som kollar om det inskickade värdet är giltigt. Vi har även med en egenskap ”Spaces”, vilken innehåller antalet mellanslag som måste finnas.

Det som sker i IsValid är att vi ser så att vi har rätt antal mellanslag i det inskickade värdet.

Vi måste sedan lägga till attributet på vårt fält:

[Name(ErrorMessage="Du måste ha ett mellanslag.", Spaces=1)]
public string Name { get; set; }

Om vi nu kör sidan så får vi upp det här när vi postar ett namn utan mellanslag:

3 - CustomFail

Nu får vi anpassad validering som slår igenom när vi postar sidan. För att kunna använda klientvalidering så måste vi se till att även skriva en sådan.

Till att börja med så behöver vi en Validator för modellen. Den används för att skicka våra regler med dess parametrar till klienten.

using System.Collections.Generic;
using System.Web.Mvc;
 
namespace MvcValidate.Code
{
    public class NameValidator : DataAnnotationsModelValidator<NameAttribute>
    {
        public NameValidator(ModelMetadata metadata, ControllerContext context, NameAttribute attribute)
            : base(metadata, context, attribute)
        {
        }
 
        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        {
            var rules = new List<ModelClientValidationRule>();
 
            var rule = new ModelClientValidationRule()
            {
                ErrorMessage = Attribute.ErrorMessage,
                ValidationType = "name"
            };
 
            rule.ValidationParameters.Add("spaces", Attribute.Spaces);
 
            rules.Add(rule);
 
            return rules;
        }
    }
}

Genom att köra en override på GetClientValidationRules så kan vi själva ange vad som skall skickas till klienten. Det är det här som dyker upp i den Json som vi såg tidigare.

Vi behöver sedan registrera det här i global.asax under Application_Start.

DataAnnotationsModelValidatorProvider
.RegisterAdapter(typeof(NameAttribute), typeof(NameValidator));

Här registrerar vi NameAttribute och NameValidator så att de skall finnas med i klientvalideringen. Utan det här så kommer inte valideringsreglerna med.

Det sista steget för att få till valideringen är att skriva JavaScriptet som skall användas för valideringen.

Börja med att skapa en js-fil i Scripts-mappen. Här ska vi använda oss utav ValidatorRegistry som finns i MicrosoftMvcAjax.js.

var validator = Sys.Mvc.ValidatorRegistry.get_creators();
 
validator["name"] = function(rule) {
    console.log('Validator');
    var spaces = rule.ValidationParameters["spaces"];
 
    console.log('Spaces: ' + spaces);
 
    var validator = {
        validate: function(validation, elements, value) {
            var currentSpaces = (value.split(' ').length - 1);
 
            console.log('Value: ' + value);
            console.log('currentSpaces: ' + currentSpaces);
            console.log('spaces: ' + spaces);
 
            if (currentSpaces < spaces) {
                return [rule.ErrorMessage];
            }
 
            return null;
        }
    };
 
    return validator;
};

Här har vi en JavaScript-version av valideringen. Jag har även med loggning för att vi ska kunna se i consolen så att alla värden ser rätt ut.

JavaScriptet ovan körs varje gång vi skriver ett tecken, vilket gör att vi snabbt och enkelt kan se om värdet är giltigt. Man bör dock inte använda något Ajax-anrop, då det kan leda till väldigt mycket overhead.

Om vi infogar js-filen på sidan så kan vi se att vi nu har realtidsvalidering av det vi skriver i textrutan.

Vi kan även se i consolen (kräver Firebug för Firefox eller Internet Explorer 8) om allt stämmer:

4 - Log

Utan något större arbete så kunde vi skapa en anpassad validering som fungerar på både servern och klienten, tillsammans med den inbyggda valideringsfunktionaliteten i ASP.NET MVC 2.

Första alphan av jQuery 1.4 (tidigare 1.3.3) har släppts. Den innehåller väldigt många prestandafixar och en del små funktioner, dock inga större förändringar utåt.

För er som har möjligheten att köra med alpha-versioner så kan jag rekommendera er att ladda ned den och testa på era sidor. Hittar ni några buggar så meddela dem till jQuery-teamet så att de kan fortsätta komma med bra releaser. :-)

Ni hittar mer information samt nedladdningarna här:

http://blog.jquery.com/2009/12/04/jquery-14-alpha-1-released/

More Posts Next page »