Kategoriarkiv: .NET

LINQ to XML

Language-Integrated Query (LINQ) er en teknologi der tillader query-egenskaber direkte i C#. I query-form ligner det SQL i syntax og opbygning. Alternativet er method-syntax som vi ikke vil bruge tid på i dette indlæg. Til de interesserede kan forskellen mellem query og method syntax ses her: http://msdn.microsoft.com/en-us/library/bb397947.aspx. MS anbefaler at man bruger query syntax alle de steder man kan, frem for method syntax.

LINQ to XML er en del af LINQ, og er designet til at trække data ud af xml, fra eksempelvis en fil.

Vi skal bruge namespacet ”System.Xml.Linq”. Dette namespace indeholder klasser som XDocument, XElement, XAttribute og XNode. Det er nogle af disse vi bruger i vores query.

LINQ syntaksen minder meget om SQL og kan ses her:
http://msdn.microsoft.com/en-us/library/bb308959.aspx#linqoverview_topic5

Lad os springe ud i det.

Lad os først finde noget XML vi selektivt vil trække data ud af. Følgende er et log udtræk fra SVN:

<?xml version=”1.0″?>
<log>
  <logentry revision=”200″>
    <author>Lars</author>
    <date>2010-06-03T08:24:19.329726Z</date>
    <paths>
      <path action=”M”>/sti/Upload.ascx.cs</path>
      <path action=”D”>/sti/Projekt.csproj.user</path>
    </paths>
    <msg>Changes to upload path handling</msg>
  </logentry>
  <logentry revision=”199″>
    <author>Lars</author>
    <date>2010-05-31T14:30:23.080950Z</date>
    <paths>
      <path action=”M”>/sti/Database.mdf</path>
      <path action=”M”>/sti/Database.ldf</path>
    </paths>
    <msg>Shrinked database again</msg>
  </logentry>
  <logentry revision=”198″>
    <author>Lars</author>
    <date>2010-05-31T13:59:33.689391Z</date>
    <paths>
      <path action=”A”>/sti/Projekt.sln</path>
      <path action=”A”>/sti/Projekt.suo</path>
    </paths>
    <msg>VS2010 Solution files.</msg>
  </logentry>
  <logentry revision=”197″>
    <author>Sral</author>
    <date>2010-05-28T07:10:51.597195Z</date>
    <paths>
      <path action=”M”>/sti/</path>
    </paths>
    <msg>Added files/folders to ignore filter.</msg>
  </logentry>
</log>

Først tæller vi alle log entries grupperet på author. Det kan klares således:

var output = from logentry in SvnLog.Elements("logentry")
    group logentry by (string) logentry.Element("author")
    into logentries
    select new { 
        Author = logentries.Key,
        Entries = logentries.Count()
    };

Vi piller querien fra hinanden og ser på de enkelte dele.

from logentry in SvnLog.Elements("logentry")

Dette statement returnerer en IEnumerable liste af XElementer indeholdende alle under-noder i hvert <logentry> element. Disse bliver så placeret i en variabel kaldet “logentry”.

group logentry by (string) logentry.Element("author") into logentries

Vi grupperer her på datasættet fra før med en under-node i <logentry> ved navn <author>. Note: Det er vigtigt at huske castet til string da de fleste klasser i System.Xml.Linq har custom type converters (http://msdn.microsoft.com/en-us/library/ayybcxe5.aspx).

Et cast til string giver elementets værdi, hvor logentry.Element(“author”).ToString() ville give en string repræsentation af objektet.

Til sidst placerer vi vores gruppering i en ny variable ved navn ”logentries”.

select new { Author = logentries.Key, Entries = logentries.Count() };

Her over laver vi en anonym type indeholdende vores fundne data (Anonymous Types: http://msdn.microsoft.com/en-us/library/bb397696.aspx)
Logentries.Key er altid hvad man grupperer på. I dette eksempel laver vi en simpel count på de fundne værdier.

Vi kører det igennem en foreach løkke:

foreach (var d in output) {
     Console.WriteLine("{0}: {1} entries.", d.Author, d.Entries);
}

Resultat:

Lars: 3 entries.
Sral: 1 entries.

Lad os prøve en lidt mere avanceret query med nestede selects.

Vi prøver følgende: Vores query skal tælle attributen action (i <path>) grupperet på værdien. For at være endnu mere vanskelig grupperer vi også på author igen.
Resultatet vi søger skulle gerne se sådan ud:

Lars: A:2, M:3, D:1
Sral: A:0, M:1, D:0

Querien kommer til at se således ud:

var data =
  from logentry
  in SvnLog.Elements("logentry")
  group logentry by (string)commit.Element("author")
    into logentries
    select new
    {
      Author = logentries.Key,
      Added = (
          from path in logentries.Elements("paths").Elements("path")
          where (string)path.Attribute("action") == "A"
          select path
      ).Count(),
      Modified = (
        from path in logentries.Elements("paths").Elements("path")
        where (string)path.Attribute("action") == "M"
        select path
      ).Count(),
      Deleted = (
        from path in logentries.Elements("paths").Elements("path")
        where (string)path.Attribute("action") == "D"
        select path
      ).Count()
    };

Det første i querien ligner statementet fra før: vi grupperer blot på author. Inde i vores anonyme type har vi nu 3 næsten ens selects:

from path in logentries.Elements("paths").Elements("path")

Vi finder alle ”path” noder som vores gruppering i det ydre scope indeholder.

where (string)path.Attribute("action") == "A"

Af de fundne path noder, finder vi alle dem der har attributten ”A”.

select path

Dem der matcher vores where clause selecter vi, plus pakker det hele ind til sidst, så vi kan lave en Count() på det.

foreach (var user in data) {
   Console.WriteLine("{0}: A:{1}, M:{2}, D:{3}",
      user.Author, user.Added, user.Modified, user.Deleted);
}

Og nu får vi det output vi søgte:

Lars: A:2, M:3, D:1
Sral: A:0, M:1, D:0

Certificerede Sitecore CMS .NET udviklere

Så har en flok fra Hinnerup Net været afsted på certificeringskurser: Sitecore Developer Foundation og Sitecore .NET Developer.


Certified .NET Developers

Et stort tillykke til de ny-certificerede medarbejdere:

 Michael S. Fosgerau
 Jakob Justsen
 Rune Alblas
 Tobias Hinnerup
 Lars Støttrup Nielsen
 Casper Hornstrup

Vi føler os nu rigtig godt klædt på til at indgå i opgaveløsning med relation til Sitecore CMS, med en fornyet og gennemgående teknisk indsigt, inden for eksempelvis:

  • Rådgivning om forskellige aspekter omkring Sitecore CMS løsninger
  • Udvikleroplæring og workshops
  • Integration af websites, webservices og eksterne systemer med Sitecore CMS
  • Udvikling af komponenter, kontroller, layouts med videre
  • Udvidelser til Sitecore CMS systemet samt administrations-wizards
  • Design, implementering og vedligeholdelse af eksisterende såvel som nye Sitecore CMS Sites

Strukturel processering af billeder

Når billeder skal processeres programmatisk anvendes en matematisk metode der kaldes morfologiske transformationer (morphological transformations), mønster genkendelse (pattern recognition) og/eller egenskabs ekstraktion (feature extraction).

Her i juletiden har mange været påvirket af huller i asfalten langs eksempeltvist Vejle fjordbroen. Hvis vi begiver os ud i et tankeeksperiment er det med relativt simple billedbehandlingsalgoritmer “nemt” at detektere en betydelig andel af opbyggende huller og dermed kunne man forebyggende iværksætte udbedrende vejarbejde før et problem opstår.

Et eksempel kunne være anvendelse af thresholding, erosion og dilation på en stribe billeder taget af asfaltoverfladen på udvalgte vejstrækninger. For resten af denne artikel antages det, at sådanne billeder er tilgængelige.

Herunder vises en række billeder af asfaltoverflader hvor der er opstået minimale huller og sprækker, samt hvor alvorlige slaghuller forekommer og er delvist udbedrede. Ligeledes vises en normal asfalt overflade. Under hvert billede vises en morfologisk behandlet udgave, der har været underlagt den samme algoritme for samtlige billeder. Givet disse resultatbilleder er det relativt simpelt at afgøre hvorvidt et behandlet resultatbillede udgør en kandidat til manuel inspektion eller ej.

Klik på billedet for at se en stor udgave.

“Proof-of-concept” algoritmen benyttet på resultatbillederne herover er skrevet i .NET C#, og ser overordnet således ud:

Bitmap input = new Bitmap(filename);
Bitmap output =
  Dilate(
    Dilate(
      Threshold(
        Dilate(
          Dilate(
            Dilate(
              Dilate(
                Grayscale(input)
              )
            )
          )
        ), 70
      )
    )
  );

output.Save(
  Path.GetFileNameWithoutExtension(filename) +
  ".processed" + Path.GetExtension(filename));

Tankeeksperimentet skal naturligvis føres videre ud før det bliver praktisk anvendeligt.

Det er klart at der vil være tale om mange tusinde billeder og adskillige terrabytes data, hvorfor system arkitekturen bag processeringssystemet bør være baseret på en skalerbar multiserver/multikerne teknologi. Et eksempel på en gratis open-source arkitektur kunne være AMD’s Framewave eller nVidia’s CUDA.

Ligeledes vil der være særlige elementer i billederne der skal tages særlig hånd om, f.eks. vejstriber og overgange mellem forskellige belægningstyper med videre.