Da jeg var ung, kunne man i Visual C++ 6.0 selv definere, hvordan typer skulle vises i watch-vinduet ved at rette i en hemmelig tekstfil i notepad.  Det er også muligt i VS.NET, men det kræver kode i stedet for konfiguration.  Man kan nemlig gøre det vha. DebuggerDisplayAttribute.  I modsætning til VC++ løsningen kan man derfor kun ændre visningen for sine egne typer.

Jeg vil her vise, hvordan DebuggerDisplayAttribute bruges.  Vi tager udgangspunkt i følgende type:

class MyClass
{
    public int Number { get; set; }
    public string Text { get; set; }
}

Følgende skærmbillede af watch-vinduet viser standardvisningen for vores type:

default

Hvis man ønsker at vise noget andet end den ikke specielt sigende tekst "{ConsoleApplication3.MyClass}", er den nemme løsning at override ToString:

public override string ToString()
{
    return String.Format("Number={0}, Text=\"{1}\"", Number, Text);
}

Overrides af ToString vil altid blive kaldt af debuggeren i watch-vinduet.  Med den metode kan man få vist, hvad man vil - f.eks.:

tostring

Hvis man ikke ønsker at override ToString bare for at vise noget pænt ved debugging, er DebuggerDisplayAttribute vejen at gå.  Koden kommer til at se således ud:

[DebuggerDisplay("Number={Number}, Text={Text}")]
class MyClass
{
    public int Number { get; set; }
    public string Text { get; set; }
}

 

Resultatet er det samme som ved brug af ToString.  DebuggerDisplay vil altid blive brugt i stedet for ToString af debuggeren.  Bemærk at debuggeren selv indsætter anførselstegn omkring strenge ved brug af DebuggerDisplay.

Som standard viser watch-vinduet alle properties og fields (public såvel som private), når man klikker på '+' ved siden af variabelnavnet.  Vha. en anden attribute, DebuggerBrowsable, kan man helt fjerne properties fra visningen:

[DebuggerDisplay("Number={Number}, Text={Text}")]
class MyClass
{
    public int Number { get; set; }
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public string Text { get; set; }
}

Det giver følgende visning:

DebuggerBrowsable

Man har også mulighed for helt at ændre denne visning vha. en tredje attribute, DebuggerTypeProxy:

[DebuggerDisplay("Number={Number}, Text={Text}")]
[DebuggerTypeProxy(typeof(MyClassDebugView))]
class MyClass
{
    public int Number { get; set; }
    public string Text { get; set; }

    internal class MyClassDebugView
    {
        MyClass c;

        public MyClassDebugView(MyClass c)
        {
            this.c = c;
        }

        public string NumberAndText { get { return c.Number.ToString() + ", " + c.Text; } }
    }
}

 

Debuggeren kalder constructoren for MyClassDebugView med det objekt af typen MyClass, som bliver vist i watch.  Nedenfor er vist, hvordan det kommer til at se ud i watch-vinduet.  Bemærk at debuggeren selv tilføjer et raw-view.

DebuggerTypeProxy

Så er TechEd vel overstået, og jeg er tilbage i DK.

Det hotte emne i år var parallel programming, så de fleste af mine sessioner handlede om det samt om det nye Geneva framework (the framework formerly known as Zermatt).  Til en session om parellel programming af Joe Duffy lykkedes det mig at vinde en signeret version af hans nye bog (på omkring 43225235 sider):

412yJeVFALL__SS500_

Det blev også til et par andre bøger, som jeg dog måtte betale for:

f# 51UY5afgcFL__BO2,204,203,200_PIsitb-sticker-arrow-click,TopRight,35,-76_AA240_SH20_OU02_  

Jeg havde kameraet med til Barcelona.  Desværre var der ikke tid til megen sightseeing, men her er et lille udpluk:

DSC_0039 DSC_0050 DSC_0084 DSC_0094  DSC_0105 DSC_0088

Mens jeg overvejer om jeg skal investere i et Nikon D90 eller gå full-frame med Nikon D700, så lånte jeg et Nikon D70 på en tur til Washinton D.C.

Washington 066 Washington 127 Washington 225

Det skal lige nævnes at videooptagelser med spejlrefleks ikke rigtigt har min interesse.

I morgen er det afsted til TechEd i Barcelona.  Første rigtige session (efter det sædvanlige BS i keynote) handler om F#.

Sweet :-D

I år har jeg valgt ikke at slæbe den bærbare med.  Så der kommer ingen kedelige referater på dotninjas.dk.  Til gengæld tager jeg D80'eren med i håb om, at der dukker et Kodak-moment op i ny og næ.

Jeg har kæmpet lidt med at få UPDATE vha. Table<Entity>.Attach til at virke med LINQ to SQL.  Lad os tage udgangspunkt i en databasetabel User.  Vha. LINQ to SQL har vi fået genereret en User klasse, som logisk set har følgende udseende:

public class User
{
    public int Id { get; set; }
    public bool IsMale { get; set; }
    public int Age { get; set; }
    public string Name { get; set; }
}

Vi vil nu gerne opdatere en række i databasen, som har Id = 1.  Det data, der skal gemmes, har en bruger indtastet i en form på en ASP.NET side.  Så vi forsøger os med følgende:

using (DataClassesDataContext db = new DataClassesDataContext())
{
    User lt = new User()
    {
        Id = 1
    };

    db.Users.Attach(lt);

    lt.IsMale = true;
    lt.Age = 42;
    lt.Name = "!";
    db.SubmitChanges();
}

Vores update går fint igennem og alt er godt.

En dag kommer der en rigtig provokerende bruger forbi vores website.  Da han skal redigere sine eksisterende brugeroplysninger, sætter han alder til 0 og udfylder ikke sit navn.  Vi får følgende værdier:

using (DataClassesDataContext db = new DataClassesDataContext())
{
    User lt = new User()
    {
        Id = 1
    };

    db.Users.Attach(lt);

    lt.IsMale = true;
    lt.Age = 0;
    lt.Name = null;
    db.SubmitChanges();
}

Til vores skræk bliver alder og navn ikke opdateret i databasen, og den dag en kvinde finder på ikke at udfylde alder og navn går det helt galt: Intet bliver opdateret.  Årsagen er, at en property på klassen User ikke bliver ændret og at diverse property changed events ikke bliver  kaldt, hvis den nye værdi ikke er anderledes end den eksisterende.  Når man som ovenfor opretter en instans af User, bliver alle properties sat til deres default værdier: False for bools, 0 for integers, null for strings etc.  Derfor mener LINQ to SQL ikke, at det er nødvendigt at sende en UPDATE til databasen, hvis man sætter de forskellige properties til deres default værdier.  Bemærk i øvrigt, at dette har intet at gøre med "Update Check" i .dbml filer.

På nettet er der diverse "løsninger" til problemet: Indlæs først den pågældende entity fra databasen via samme data context og undgå dermed Attach, gem alle entities i Session eller gem alle entities i ViewState.  Jeg er ikke begejstret for nogen af løsningerne.  ViewState løsningen er slet ikke mulig i ASP.NET MVC.

En anden mulig løsning er at sætte alle properties til noget snedigt før man laver sin Attach.  Det er ikke kønt, så jeg er modtagelig overfor andre forslag:

public static void Update(int id, bool isMale, int age, string name)
{
    using (DataClassesDataContext db = new DataClassesDataContext())
    {
        // Grimt - men det virker
        User lt = new User()
        {
            Id = id,
            IsMale = !isMale,
            Age = age == 0 ? 1 : age,
            Name = name == null ? "" : name
        };

        db.Users.Attach(lt);

        lt.IsMale = isMale;
        lt.Age = age;
        lt.Name = name;
        db.SubmitChanges();
    }
}