Diverse prosjekter

Her kan du lese om diverse utviklingsprosjekter jeg driver med – hobbyprosjekter stort sett, inkludert open source prosjekter jeg har startet eller bidrar på.

Snookiepoof på google code

Snookiepoof er et lite C# prosjekt jeg har jobbet med de siste ukene, hvor jeg eksperimenterer med ting som OO-design patterns og clean code. Jeg planlegger å skrive endel blogposter fremover basert på erfaringer med dette prosjektet.

Snookiepoof er en motor for rollespill-aktige spill, hvor man beveger seg rundt i en verden og oppdager ting man kan gjøre. Foreløpig inneholder løsningen et konsoll-basert interface, men designet skal også kunne støtte andre typer brukergrensesnitt.

Jeg bruker TDD/BDD under utvikling, og legger fokus på gode abstraksjoner og å følge SOLID-prinsippene. Command-pattern er mye brukt, og spillmotoren inneholder bl.a. min implementasjon av en generisk state machine.

Av eksterne avhengigheter kan jeg nevne at jeg har brukt TinyBDD til å skrive de fleste testene, og bruker Ninject for dependency injection. Jeg planlegger også å bruke db4o for persistering.

Jeg har lagt ut prosjektet på Google Code, så hvis du er interessert i å ta en titt så har du muligheten til det.

Knagger:

Overvåk ledig diskplass med WMI

Med gjevne mellomrom går en eller annen disk på en eller annen server full, enten det er på grunn av ivrige logger, akkumulerende backups, voksende databaser eller noe helt annet. Uansett hva det er skaper det alltid problemer og tapt arbeidstid. Og med like gjevne mellomrom kan man høre meg klage, og si at nå vi få på plass en løsning som kan overvåke diskene våre.

I dag bestemte jeg meg derimot for å ikke klage lengre, og 20 minutter senere hadde jeg laget en liten app som viste meg hvor mye ledig diskplass det var på serverne våre. Jeg ante ikke at det skulle være enkelt.

Jeg begynte med å lage en liten dataklasse for å holde på informasjonen om en disk. Med hjelp av CodeRush tok det ikke mange sekunder eller tastetrykk. Her er DiskInfo-klassen:

    8 public class DiskInfo

    9 {

   10     public string DriveLetter { get; set; }

   11     public string VolumeName { get; set; }

   12     public double Size { get; set; }

   13     public double Free { get; set; }

   14     public double PercentFree { get { return Free / Size; } }

   15 

   16     public override string ToString()

   17     {

   18         return string.Format(“{0}{1}{2}{3}{4}”,

   19             DriveLetter.PadRight(5),

   20             VolumeName.PadRight(30),

   21             Size.ToString(“###,###,###,###”).PadLeft(15),

   22             Free.ToString(“###,###,###,###”).PadLeft(15),

   23             PercentFree.ToString(“#0%”).PadLeft(10));

   24     }

   25 }

Og så trengte jeg noe som kunne hente ut informasjonen. Jeg har hørt mye om Windows Management Instrumentation (WMI), men aldri brukt det. Men en liten titt på System.Management namespacet avslørte at dette var akkurat det jeg behøvde:

Provides access to a rich set of management information and management events about the system, devices, and applications instrumented to the WMI infrastructure. Applications and services can query for interesting management information (such as how much free space is left on the disk, what is the current CPU utilization, which database a certain application is connected to, and much more)…

Jeg laget en kjapp liten command-klasse jeg valgte å kalle DiskInfoRetriever, som gitt navnet på en server henter ut informasjon om alle diskene på den maskinen:

    9 using System.Management;

   10 

   11 public class DiskInfoRetriever

   12 {

   13     private string _Server;

   14 

   15     public List<DiskInfo> Items { get; set; }

   16 

   17     public DiskInfoRetriever(string server)

   18     {

   19         _Server = FixServerName(server);

   20         Items = new List<DiskInfo>();

   21     }

   22 

   23     private string FixServerName(string server)

   24     {

   25         return server.StartsWith(@”\\”)

   26             ? server

   27             : @”\\” + server + @”\root\cimv2″;

   28     }

   29 

   30     public void Execute()

   31     {

   32         var scope = new ManagementScope(_Server);

   33         var query = new ObjectQuery(

   34               “select FreeSpace, VolumeName, Size, Name “

   35             + “from Win32_LogicalDisk where DriveType=3″);

   36 

   37         var searcher = new ManagementObjectSearcher(scope, query);

   38         var results = searcher.Get();

   39 

   40         Items.Clear();

   41 

   42         const int MEGABYTE = 1048576;

   43         foreach (ManagementObject result in results)

   44         {

   45             Items.Add(new DiskInfo

   46             {

   47                 DriveLetter = result["Name"].ToString(),

   48                 VolumeName = result["VolumeName"].ToString(),

   49                 Size = (double.Parse(result["Size"].ToString()) / MEGABYTE),

   50                 Free = (double.Parse(result["FreeSpace"].ToString()) / MEGABYTE),

   51             });

   52         }

   53     }

   54 }

Legg merke til den SQL-lignende spørringen på linje 34/35.

Nå gjenstod det bare å definere en liste med servere, initialisere en bunch med command-objekter, eksekvere dem, og loope gjennom resultatet for å skrive objektene til konsollet. Det gjorde jeg i Main:

   10 static void Main(string[] args)

   11 {

   12     string[] servers = { “s1dev02″, “s1dev03″, “s1dev04″, “s1dev05″, “s1hvsql01″ };

   13 

   14     try

   15     {

   16         foreach (var server in servers)

   17         {

   18             Console.WriteLine(server + “:”);

   19 

   20             var diskInfoRetriever = new DiskInfoRetriever(server);

   21             diskInfoRetriever.Execute();

   22             diskInfoRetriever.Items.ForEach((disk) => Console.WriteLine(disk));

   23 

   24             Console.WriteLine();

   25         }

   26     }

   27     catch (Exception ex)

   28     {

   29         Console.ForegroundColor = ConsoleColor.Red;

   30         Console.WriteLine(ex.ToString());

   31     }

   32 

   33     Console.WriteLine();

   34     Console.WriteLine(“press ENTER to exit..”);

   35     Console.ReadLine();

   36 }

20 minutter tidligere hadde jeg ingen anelse om at jeg kom til å lage dette her, eller at det var så enkelt. For fremtiden skal jeg tenke meg om en ekstra gang før jeg klager over en manglende, teknisk løsning. Det meste lar seg løse, og mye er enklere enn man tror.

Neste steg blir å lage en ny modul til ContikiCenter som kan informere oss om når disker nærmer seg smertegrensen. Her er forresten resultatet av dagens kjøring:

Disk Watcher console app

Obs, det ser ut som om vi må foreta en liten opprydning på s1dev04. Jaja, det får vente til i morgen.. :)

The Forecast Exchange: Flere grafer og en promo video

Vi er for tiden godt inne i andre runde med beta-testing av The Forecast Exchange, open-source prosjektet hvor man bruker kjøp og salg av aksjer til å spå om fremtiden. Det jeg har bidratt med den siste tiden er å visualisere data gjennom grafer.

Det førset vi la til var en liten trendgraf, som vi fikk plass til i listen over aktive spådommer. Dette er videreføringen av en håndlaget sparkline-kontroll jeg laget for Fussball Manager, et annet lite hobbyprosjekt. Og resultatet ser sånn ut:

The Forecast Exchange: Active predictions
Klikk bildet for full størrelse

Jeg er interessert i å lære meg jQuery, det mest populære JavaScript/Ajax-biblioteket for tiden, og i går satt jeg og så gjennom alle plugin’ene som er laget for jQuery. Da kom jeg over en plugin som enkelt lar deg opprette Google Charts med jQuery. Jeg har blogget om Google Charts tidligere her.

Ved å bruke denne plugin’en la jeg til to ekstra grafer på The Forecast Exchange: En litt større trendgraf som viser utviklingen av verdien på aksjene, og et lite “spidometer” som viser verdien på aksjene etter siste salg:

The Forecast Exchange: Prediction charts

Og til slutt måtte jeg selvfølglig lage en liten promotion-video for prosjektet vårt. Vel bekomme:

Knagger: , , , ,

Enkel integrasjon mot TeamCity

Du tror det kanskje ikke, men versjon 3 av ContikiCenter er sluppet – bare 14 dager etter versjon 1. Dette er en WPF-applikasjon jeg utvikler som står sentralt plassert i resepsjonen der jeg jobber, og viser noen sentrale data om statusen i CMA Contiki’s R&D avdeling.

ContikiCenter TeamCity integration 1
Contiki Center versjon 3.0

Fra før har jeg laget en modul for personellstatus, og en som viser status på vårt nattlige CruiseControl-bygg som produserer installasjonspakker m.m. (litt om den modulen her). Til versjon 3 har jeg utviklet en integrasjon til TeamCity, og her skal jeg fortelle litt om den og dele noen WPF-erfaringer etc.

TeamCity er en server for kontinuerlig integrasjon (CI) fra JetBrains, selskapet bak ReSharper. Hver gang vi sjekker inn endringer i kildekodesystemet vårt, fyrer TeamCity igang automatisk kompilering og kjøring av eventuelle enhets- og integrasjonstester. Kontinuerlig integrasjon er et uvurderlig hjelpemiddel når man jobber i felleskap på samme produkt.

Advarsel: WPF-koden i denne artikkelen utgjør på ingen måte best practice. Jeg har enda ikke lært meg MVVM, og er ikke så flink til å separere logikk og visningskode. Modulene mine er derimot så enkle at dette ikke har vært nødvenig, og jeg håper man kan finne et og annet tips av verdi likevel.

Modulen jeg har laget for ContikiCenter lister statusen på de viktigste CI-prosjektene vi har kjørende. TeamCity tilbyr Atom-feeds med diverse informasjon (Atom er nesten det samme som RSS), og det er dette jeg har brukt for å lage integrasjonen. Med LINQ to XML er det enkelt å hente feeden, parse den og opprette instanser av klassen Build som jeg har laget for å holde informasjon om et TeamCity-bygg. Det gjør jeg på denne måten:

var xml = XDocument.Load(_feedUrl);

XNamespace xmlns = “http://www.w3.org/2005/Atom”;

 

var entries = from xElem in xml.Descendants(xmlns + “entry”)

              select new Build

              {

                  Name = BuildParser.ExtractName(xElem.Element(xmlns + “title”).Value),

                  BuildTime = BuildParser.ExtractTime(xElem.Element(xmlns + “updated”).Value),

                  StatusDescription = xElem.Element(xmlns + “author”).Element(xmlns + “name”).Value,

              };

BuildParser er en enkel hjelpeklasse for å parse ut deler av informasjonen jeg trenger. Selve modulen som skal vise byggene har følgende code behind:

   24 public partial class TeamCityDefaultView : UserControl

   25 {

   26     private string _feedUrl;

   27     private DispatcherTimer _timer;

   28     private ObservableCollection<Build> _builds = new ObservableCollection<Build>();

   29 

   30     public ObservableCollection<Build> Builds

   31     {

   32         get

   33         {

   34             return _builds;

   35         }

   36     }

   37 

   38     public TeamCityDefaultView()

   39     {

   40         _feedUrl = ConfigurationManager.AppSettings["TeamCityFeed"];

   41         FetchBuilds();

   42 

   43         InitializeComponent();

   44 

   45         _timer = new DispatcherTimer();

   46         _timer.Interval = new TimeSpan(0, 1, 0);

   47         _timer.Tick += new EventHandler(_timer_Tick);

   48         _timer.Start();

   49     }

   50 

   51     void _timer_Tick(object sender, EventArgs e)

   52     {

   53         FetchBuilds();

   54     }

   55 

   56     private void FetchBuilds()

   57     {

   58         var teamCityFeed = new TeamCityFeed(_feedUrl);

   59         teamCityFeed.Load();

   60 

   61         _builds.Clear();

   62         foreach (var item in teamCityFeed)

   63         {

   64             _builds.Add(item);

   65         }

   66     }

   67 }

Propertien Builds er en ObservableCollection som til enhver tid inneholder alle byggene fra Atom-feeden. I XAML-delen av kontrollen kan jeg binde mot denne, og brukergrensesnittet vil automatisk oppdatere seg om inneholdet i kolleksjonen oppdaterer seg.

I konstruktøren setter jeg opp en DispatcherTimer til å tikke én gang i minuttet, og for hvert tikk henter jeg feeden på nytt og oppdaterer Builds-properien. TeamCityFeed er klassen min som henter og parser feeden.

Resultatet er modulen du ser nedenfor, som viser navnet og statusbeskrivelsen for hver bygg.

ContikiCenter TeamCity integration 2

Grensesnittet består av to ulike WPF-kontroller. TeamCityDefaultView er den blå, store boksen. DataContext er satt til å peke på seg selv (ved hjelp av den merkelige syntax’en på linje 5 (se nedenfor)), og slik vil den få tilgang til Builds-propertien. TeamCityDefaultView inneholder videre en ItemsControl, som mer eller mindre tilsvarer en Repeater i ASP.NET (om du er kjent med det). Og det er i den kontrollen vi sier at ItemsSource skal bindes mot Builds.

Grensesnittet for hvert enkelt bygg defineres av en annen WPF-kontroll jeg har kalt BuildControl. For å bruke den må jeg inkludere namespacet den ligger i, og det har jeg gjort i linje 3. Ved å sette DataContext={Binding} vil hver kontroll som opprettes på bakrunn av Builds-kolleksjonen motta én Build-instans.

ContikiCenter TeamCity integration 4
Klikk her for større bilde

XAML’en til BuildControl ser du nedenfor. Den inneholder enkelt og greit to tekstblokker som bindes mot henholdsvis Name og StatusDescription fra Build-instansen som er datakonteksten til kontrollen.

ContikiCenter TeamCity integration 5
Klikk her for større bilde

Nå gjenstår det bare en ting: Om en build feiler ønsker jeg at det skal bli veldig synlig for dem som går forbi statustavlen.., jeg vil at den skal skrike rødt. For å få til det overstyrer jeg OnRender i BuildControl-komponenten, henter ut Build-instansen, og sjekker statusbeskrivelsen. Om beskrivelsen er “failed build” overstyrer jeg utseende på Border-kontrollen, som er ansvarlig for hvordan raden ser ut:

   28 protected override void OnRender(DrawingContext drawingContext)

   29 {

   30     Build build = DataContext as Build;

   31 

   32     if (build != null)

   33     {

   34         if (IsFailedBuild(build))

   35         {

   36             RadialGradientBrush failedGradientBrush = new RadialGradientBrush()

   37             {

   38                 GradientOrigin = new Point(1, 1.5),

   39                 SpreadMethod = GradientSpreadMethod.Pad,

   40             };

   41             failedGradientBrush.GradientStops.Add(

   42                 new GradientStop(

   43                     Color.FromArgb(0xFF, 0xFF, 0×93, 0x4A), 0));

   44             failedGradientBrush.GradientStops.Add(

   45                 new GradientStop(

   46                     Color.FromArgb(0xFF, 0xFF, 0×19, 0x0B), 1.3));

   47             _border.Background = failedGradientBrush;

   48             _border.Opacity = 1;

   49         }

   50     }

   51     base.OnRender(drawingContext);

   52 }

   53 

   54 public bool IsFailedBuild(Build build)

   55 {

   56     return build.StatusDescription.ToLower().Equals(“failed build”);

   57 }

Borderen (og dens innhold) har jeg også per default satt til å være gjennomsiktig. Om bygget feiler setter jeg opacity til 1, slik at den blir ekstra synlig. Og sånn blir resultatet:

ContikiCenter TeamCity integration 3

Jeg måtte fremprovosere en feil for å få tatt dette snapshot’et, siden byggene våre er så stabile :)

Håper du fant noe av interesse her. Har du noen kommentarer til mine ynkelige forsøk på bruk av WPF så setter jeg umåtelig pris på en kommentar eller tre.

Knagger: , ,

En enkel SOA komponent

SOA, tjenesteorientert arkitektur, er et utvannet og mye missbrukt begrep. For meg dreier det seg først og fremst om å lage små, mer eller mindre uavhengige tjenester, som kan kombineres og sammarbeide for å lage større løsninger. Den siste tiden har jeg laget en rekke slike små tjenestekomponenter. Jeg bruker omtrent den samme fremgangsmåten hver gang, og fikk derfor lyst til å dele dette mønsteret her.

SOA Component

BrainSpill.CoreDet viktigste prinsippet mitt når jeg lager slike komponenter er enkelhet. Jeg lager en liten, no fuss komponent, som ikke vet noe om verden bortsett fra akkurat det tjenesten skal tilby. Den eier sine egne data, og er så generisk at den i prinsippet kan brukes til mange ting.

Du vil se eksempler på bruk av db4o, LINQ, Ninject og WCF i denne artikkelen. Eksempelet jeg skal vise er en komponent jeg har kalt BrainSpill – en tjeneste hvor man kan lagre og hente ut meldinger. Jeg har planer om å bruke BrainSpill til å lage en twitter-lignende tjeneste til bruk internt på jobben. Hvis du ser for deg en hjerne som flyter over av tanker og ideer som bare må deles med andre så skjønner du sikkert hvordan navnet på tjenesten oppstod.

Jeg begynner med å lage et interface hvor jeg definerer hva tjenesten skal tilby. Jeg velger å gjøre dette i et eget class library prosjekt jeg kaller for BrainSpill.Core. IMessageRepository skal ta imot nye meldinger, gi tilgang til å slette meldinger, hente ut de siste n meldingene, og fortelle hvor mange meldinger som totalt er lagret i jenesten. (Dette er første versjon, og interfacet vil nok vokse noe etterhvert.)

    7 [ServiceContract]

    8 public interface IMessageRepository

    9 {

   10     [OperationContract]

   11     void AddMessage(AddMessageRequest messageData);

   12 

   13     [OperationContract]

   14     void DeleteMessage(MessageId id);

   15 

   16     [OperationContract]

   17     IEnumerable<Message> GetLatestMessages(int numberOfMessages);

   18 

   19     [OperationContract]

   20     int GetTotalNumberOfMessages();

   21 }

Jeg dekorerer interfacet med ServiceContract og metodene med OperationContract, slik at det kan publiseres vha. Windows Comunication Framework (WCF).

Legg merke til at jeg bruker tre andre klasser her: Message er en komplett melding lagret i tjenesten. AddMessageRequest holder dataene som brukes til å opprette en ny melding; en klient av tjenesten har ikke lov til å opprette fullverdige meldinger selv – det er f.eks. tjenesten som gir meldingen en id, lagringstidspunkt m.m. Jeg har også valgt å lage en egen klasse for meldingens id: MessageId. Denne er i prinsippet kun en wrapper for en Guid. Alle disse klassene dekoreres med DataContract-atributtet, og properties som skal være tilgjengelig på klientsiden dekoreres med DataMember.

Når alt dette er på plass koder jeg en konkret implementasjon av IMessageRepository. For tiden bruker jeg objektdatabasen db4o for lagring – det er absolutt det enkleste. Jeg gir derfor OdbMessageRepository en konstruktør som tar inn en IObjectContainer (en db4o database).

   15 public class OdbMessageRepository : IMessageRepository, IDisposable

   16 {

   17     private IObjectContainer _database;

   18 

   19     public OdbMessageRepository(IObjectContainer database)

   20     {

   21         _database = database;

   22     }

Deretter implementerer jeg metodene definert i IMessageRepository. Takket være db4o er dette ganske enkelt. Jeg benytter en del LINQ, og jeg vet ikke om koden jeg har skrevet her er optimal (sansynligvis ikke), men eksperimentering har vist at det er godt nok for meg i denne omgang.

   24 public void AddMessage(AddMessageRequest messageData)

   25 {

   26     Message messageToAdd = new Message(messageData);

   27     _database.Store(messageToAdd);

   28 }

   29 

   30 public void DeleteMessage(MessageId id)

   31 {

   32     _database.Delete(GetById(id));

   33 }

   34 

   35 public IEnumerable<Message> GetLatestMessages(int numberOfMessages)

   36 {

   37     var query = from Message m in _database

   38                 orderby m.Created descending

   39                 select m;

   40 

   41     return query.Take(numberOfMessages);

   42 }

   43 

   44 public int GetTotalNumberOfMessages()

   45 {

   46     var query = from Message m in _database

   47                 select m;

   48 

   49     return query.Count();

   50 }

   51 

   52 private Message GetById(MessageId id)

   53 {

   54     var query = from Message m in _database

   55                 where m.Id == id

   56                 select m;

   57 

   58     return query.First();

   59 }

Du la kanskje merke til at jeg lot OdbMessageRepository arver fra IDisposable? Objektdatabasen er nemlig en ressurs som må frigjøres på riktig måte. Derfor implementerer jeg Dispose().

   61 #region IDisposable Members

   62 

   63 private bool _isDisposed;

   64 

   65 public void Dispose()

   66 {

   67     Dispose(true);

   68     GC.SuppressFinalize(this);

   69 }

   70 

   71 private void Dispose(bool disposing)

   72 {

   73     if (_isDisposed)

   74     {

   75         return;

   76     }

   77     _isDisposed = true;

   78 

   79     if (disposing)

   80     {

   81         if (_database != null)

   82         {

   83             _database.Dispose();

   84             _database = null;

   85         }

   86     }

   87 }

   88 

   89 #endregion

BrainSpill.ServiceNå er kjernefunksjonaliteten i BrainSpill ferdig implementert. Jeg legger så til et nytt prosjekt for selve windows tjenesten: BrainSpill.Service. Jeg oppretter BrainSpillService som arver fra System.ServiceProcess.ServiceBase.

Selv om det kanskje er overkill i et så lite prosjekt som dette har jeg lagt meg til vanen å alltid bruke dependency injection, og tjenesten oppretter derfor en IoC container i konstruktøren. Jeg bruker som regel Ninject, som tar inn en DependencyModule – mer om den om litt..

    9 public partial class BrainSpillService : ServiceBase

   10 {

   11     public const string SERVICE_NAME = “BrainSpillService”;

   12 

   13     private IKernel _IocContainer;

   14     public ServiceHost _serviceHost = null;

   15 

   16     public BrainSpillService()

   17     {

   18         _IocContainer = new StandardKernel(

   19             new BrainSpillServiceDependencyModule());

   20 

   21         ServiceName = SERVICE_NAME;

   22     }

   23 

   24     protected override void OnStart(string[] args)

   25     {

   26         if (_serviceHost != null)

   27         {

   28             _serviceHost.Close();

   29         }

   30         _serviceHost = new ServiceHost(

   31             _IocContainer.Get<IMessageRepository>());

   32         _serviceHost.Open();

   33     }

   34 

   35     protected override void OnStop()

   36     {

   37         if (_serviceHost != null)

   38         {

   39             _serviceHost.Close();

   40             _serviceHost = null;

   41         }

   42     }

   43 

   44     protected override void Dispose(bool disposing)

   45     {

   46         if (disposing)

   47         {

   48             if (_IocContainer != null)

   49             {

   50                 // note: disposing the kernal also

   51                 // disposes the repository if needed

   52                 _IocContainer.Dispose();

   53                 _IocContainer = null;

   54             }

   55         }

   56 

   57         base.Dispose(disposing);

   58     }

   59 }

Koden er nokså standard for en windows service. Jeg overstyrer OnStart, hvor jeg oppretter en ny WCF ServiceHost. Jeg bruker Ninject til å opprette denne for meg, og spesifiserer kun navnet på interfacet, nemlig IMessageRepository. I OnStop stenger jeg ned den samme hosten.

Jeg overstyrer også tjenestens Dispose-metode, hvor jeg passer på å frigjøre IoC containeren. Den vil da ta seg av å kalle Dispose på alle objekter den har opprettet, som i dette tilfellet er OdbMessageRepository (som igjen kaller Dispose på db4o databasen).

Som sagt brukte jeg en Dependency-modul da jeg opprettet IoC containeren – koden for den ser du nedenfor. Jeg arver fra Ninject’s StandardModule, og overstyrer Load, hvor jeg definerer avhengighetene jeg trenger. I dette tilfellet er jeg kun interessert i å få opprette en IMessageRepository. Ved å bruke Ninject’s fluent interface sier jeg at når jeg ber om en IMessageRepository så vil jeg ha en instans av OdbMessageRepository. Jeg spesifiserer også at Ninject skal bruke Sinleton-oppførsel, hvilket vil si at den alltid bruker én og samme instans av klassen.

Til slutt forteller jeg hvilken parameter Ninject skal sende inn i konstruktøren til OdbMessageRepository. Jeg sier at argumentet som heter “database”, av type IObjectContainer, skal hentes fra metoden OpenDatabase(). Og OpenDatabase bruker db4o’s factory til å åpne basen fra en fil (eller opprette den om den ikke finnes fra før) basert på en filsti definert i configfilen.

   10 public class BrainSpillServiceDependencyModule : StandardModule

   11 {

   12     public override void Load()

   13     {

   14         Bind<IMessageRepository>()

   15             .To<OdbMessageRepository>()

   16             .Using<SingletonBehavior>()

   17             .WithArgument<IObjectContainer>(“database”, OpenDatabase());

   18     }

   19 

   20     private static IObjectContainer OpenDatabase()

   21     {

   22         return Db4oFactory.OpenFile(ConfigurationManager.AppSettings["database"]);

   23     }

   24 }

Det gjenstår noen steg for å få WCF til å fungere orntlig; jeg må definere adressen og bindingen – dette gjør jeg i configfilen.

CropperCapture[31]

Det irriterer meg at jeg må spesifisere den konkrete implementasjonen av interfacet her – OdbMessageRepository – men foreløpig vet jeg ikke om jeg kan unngå dette.

Når servicen er ferdig kompilert og installert BrainSpill, kan jeg nå legge til en service-referanse i klientprosjekter ved å referere til http://localhost:8000/BrainSpill/service/mex.

En liten detalj jeg oppdaget første gang jeg lagde en service som dette er at jeg også er nødt til å spesifisere InstanceContextMode for WCF tjenesten. Dette er pga. måten jeg oppretter ServiceHosten på – ved å gi inn en instans (fra IoC containeren) i stedet for en type (ref. BrainSpillService, linje 30 og 31). Dette er kanskje ikke ideelt, men inntil videre gjør jeg det slik, ved å spesifisere InstanceContextModeOdbMessageRepository:

   10 ///

   11 /// InstanceContextMode = InstanceContextMode.Single is needed because of the way

   12 /// the service is created (with an IoC container).

   13 ///

   14 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

   15 public class OdbMessageRepository : IMessageRepository, IDisposable

Nå er det ikke så mye igjen.., men for at alt skal fungere må jeg implementere en enkel Installer-komponent for windows servicen. Den ser slik ut:

    8 [RunInstaller(true)]

    9 public partial class BrainSpillInstaller : Installer

   10 {

   11 

   12     private ServiceProcessInstaller _process;

   13     private ServiceInstaller _service;

   14 

   15     public BrainSpillInstaller()

   16     {

   17         _process = new ServiceProcessInstaller();

   18         _process.Account = ServiceAccount.LocalSystem;

   19 

   20         _service = new ServiceInstaller();

   21         _service.ServiceName = BrainSpillService.SERVICE_NAME;

   22 

   23         Installers.Add(_process);

   24         Installers.Add(_service);

   25     }

   26 }

Og så må jeg ha en static void Main() som fyrer igang hele “sulamitten”. Jeg plasserer den for seg selv i Program.cs.

   11 static void Main()

   12 {           

   13     ServiceBase.Run(new BrainSpillService());

   14 }

Nå er jeg klar til å kompilere, og servicen er ferdig. For å ta den i bruk må den installeres og startes, og jeg lager vanligvis batch-filer for dette, sånn at jeg enkelt kan både installere og starte, og stoppe og avinstallere. Merk at du kan få problemer med servicen om du ikke alltid gjør dette i riktig rekkefølge. Om du forsøker å oppdatere servicen uten å stoppe og avinstallere først, kan det hende du må restarte maskinen for å få fjernet servicen.

Install.bat:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil BrainSpill.Service.exe

Start.bat:

net start BrainSpillService

Stop.bat:

net stop BrainSpillService

Uninstall.bat:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil /u BrainSpill.Service.exe

Så slik lager jeg tjenester for tiden. Beskrivelsen ble kanskje litt lang, men prinsippet er veldig enkelt. Spesielt fører bruken av objektdatabase (db4o) til at det blir lite kode. Har du synspunkter på denne fremgangsmåten, eller spørsmål, er du velkommen til å legge igjen en kommentar.

Knagger: , , , , ,

ContikiCenter version 2.0

Jeg har tidligere fortalt at jeg utvikler et overvåkningssenter internt i Contiki. Versjon 1 var ferdig 31. mars, og inneholdt en modul som viser status for alle ansatte – hvor de befinner seg (på jobb, hos kunde, syk osv.). I dag slapp jeg version 2, med en modul som viser status for nattens bygg.

Jeg har laget en fattigmanns-integrasjon mot vår CruiseControl.net som vi bruker til å bygge prosjektene våre, inkludert MSI pakker, språkfiler, kodesignering med mere. Jeg benyttet enkel screen scraping til å hente status og noen detaljer fra den siste build-rapporten for et bestemt build-prosjekt.

Hvis alt er OK vises kun en uskyldig, liten blå boks med dato for siste bygg. Hvis ting ikke har gått som det skal derimot, slik som er tilfelle akkurat nå, blinker byggstatus-modulen faretruende og ser ut som dette:

BuildStatus

Jeg holder på med dette prosjektet for å lære meg WPF, og denne gangen var utfordringen først og fremst animasjoner. Jeg ønsker at meldingen skulle blinke når den var rød, men å starte og stoppe en animasjon var ikke bare enkelt. Jeg fant til slutt en løsning på stackoverflow, men den føles ikke helt god. 

Jeg hadde også tenkt at hvis musepekeren var over boksen så skulle blinkingen stoppe midlertidig, men der gav jeg rett og slett opp. Moralen er at WPF er vanskelig, og jeg må bruke mer tid på å sette meg grundig inn i dette.

Jeg har blitt redaktør på ODP

DMOZJepp, jeg har nå blitt godtatt som frivillig redaktør i Open Directory Project (også kjent som Dmoz). Jeg har sett meg lei på at den norske programmerings-kategorien er så smal og utdatert. Går du inn på World->Norsk->Data->Programmering nå og scroller helt ned så ser du at det står at kategoriredaktør er tormaroe. Og det er meg det :-)

Webkatalogene var store da jeg begynte å bruke internett for rundt 12 år siden. I dag har søkemotorene for det meste tatt over, men katalogene dekker fortsatt en nisje hvor man kan finne frem til lenker i ulike kategorier som er kvalitetssikret av mennesker. Hadde det ikke vært fint om ODP, som en av de elste og mest kjente katalogene, kunne være en guide til programmerings-Norge?

Jeg har ikke kommet igang med redigeringen enda.., leser fortsatt på retningslinjene. Men jeg er svært interessert i å få litt hjelp til å pusse opp kategorien min. Hvis du vet om en norskspråklig side om programmering så er det kjempefint om du gir meg et tips. Du kan enten registrere en ny URL på kategorisiden, eller du kan rett og slett foreslå en link i kommentarfeltet her.

Forecast Exchange beta-testing foregår nå!

TFE.jpgThe Forecast Exchange gikk live i beta for en uke siden; adressen er www.theforecastexchange.com.

Halvard har satt i gang en lukket runde med beta-testing som vil gå frem til 1. mars. 22 spillere konkurrerer om hvem som best kan forutse hva som vil skje i februar 2009. Han har satt opp mange, interessante prediksjoner man kan kjøpe og selge aksjer i.

For hyppigere nyheter om Forecast Exchange bør du følge med på theforecastexchange.blogspot.com, Halvards blog. Kunne du tenke deg å være med å teste kan du kontakte meg eller Halvard (legg f.eks. igjen en kommentar på bloggen). Jeg vet ikke om flere brukere vil bli lagt til i denne runden, men det vil bli flere muligheter senere.

Knagger: ,

En applikasjon på en dag

Av og til er det herlig å bare gjøre et veldig lite utviklingsprosjekt. Man får en ide eller en forespørsel, lar tanken modnes i en dag eller to, og så implementerer man løsningen på ca en arbeidsdag. Jeg har nettopp gjort et slikt prosjekt, hvor jeg utviklet et internt verktøyt på jobben for å samle kunnskap om feilsituasjoner knyttet til utvikling, installasjon og support av produktene våre.

ContikiException er en meget enkel web app for registrering av kjente feilmeldinger med mulige løsninger. Det var Halvard (fra The Forecast Exchange) som kom frem til at vi hadde et behov for dette verktøyet, og gav meg lysten til å gjøre det.

ContikiExceptionSmaller.png

Det fine med slike småprosjekter er at de fungerer som ideelle “prøvefelt” for nye verktøy og teknikker. Man kan teste ut nesten hva som helst når det meste man kan tape er én dags arbeid. Denne gangen valgte jeg å droppe relasjonsdatabase, og brukte i stedet en objectdatabase som heter db4objects. Jeg bruker denne teknologien på et par andre prosjekter også, men ingenting som er ferdig enda.

I slutten av måneden skal jeg holde et innlegg på NNUG i Bergen om objectdatabaser, og hva vi .net utviklere kan bruke dem til.

I tillegg har jeg så smått begynt å bruke dependency injection i prosjektene mine; i ContikiException brukte jeg ninject. Ninject skryter av å være en ultrarask lettvekter, og i stedet for å basere seg på XML-konfigurasjon som mange av de andre DI rammeverkene bruker ninject et såkalt fluent interface. Dette betyr at det er enklere å sette opp avhengighetene – det er i alle fall mitt inntrykk, selv om jeg ikke har mye erfaring. I tillegg kan man dra nytte av code completion og type-safety – noe som i alle fall er et stort pluss.

Dokumentasjoen sier dessuten at “Ninject makes your application more ninja-like”, så da er det uansett det naturlige valget for meg :)

Vil du bli en .NET ninja-spillet

Jeg er i ferd med å planlegge et lite Vil Du Bli En .NET Ninja spill. Jeg tenkte det skulle være et Vil du bli en milionær-lignende spill i WPF – som en liten demo-applikasjon. Kanskje det kunne være noe som var gøy å spille f.eks. i pizzapausen på NNUG.

Men jeg hadde satt pris på litt hjelp. Jeg trenger noen som har lyst til å lage noen spørsmål. Kan du tenke deg å bidra? Det spiller ingen rolle hvor mye du orker – send meg bare en liste med så mange du har lyst til. Fem spørsmål er helt greit. 500 er umenneskelig.

Hvert spørmål må være en forholdsvis kort setning, og ha fire, korte svar, hvorav kun ett er riktig. Og vi bruker engelsk – det er det greieste.

Bruk Excel eller lignende med én kolonne for spørsmålet, kolonne 2 for riktig svar, og kolonne 3 til 5 for tre gale svar. I kolonne 6 er det fint om du kan oppgi vaskelighetsgrad: 1 = banalt enkelt, 2 = sånn passe vanskelig, 3 = veldig vanskelig.

questions.png

Spørsmålene kan omhandle alt du mener en .net ninja bør vite – om .net rammeverket, om programmering generelt, om verktøy man bør kjenne til osv.

Send spørsmålene til torbjorn punktum maro alfakrøll gmail punktum com. Jeg blir deg evig takknemlig, og du vil bli kreditert i spillet!


Alf Kåre Lefdal: Distributed Podcast er også ganske interessant. De tar opp tema som fx. ...

Stian: +1 for 6er til This Developer's Life! Min definitive favoritt. Jeg trengte også...

Torbjørn: Takk for flere tips, Vegard. Deep Fried Bytes ligger på oversikten min fra 2009...

Vegar: Og glemte helt ios: Nsbrief og ideveloper live. Har du hørt på deep fried byt...

Vegar: Mye kjekt her. TDL, hanselminutes og .net rocks ligger i en klasse for seg. Suv...

Torbjørn: Helt enig, arkivet til Software Engineering Radio er en gullgruve om man vet hva...

Einar W. Høst: Jeg synes at det kuleste med se-radio er backloggen av intervjuer... det er noen...

arnab: fantastisk :)...

Olav: Glimrende blogg ! Modellen av hjernens arbeid passer ikke bare på nyskaping: ...

Torbjørn: Ja, flydesign trekkes ofte frem som et eksempel på dette fenomenet. Design av b...

 Hold deg oppdatert

Søk i bloggen

Ferske innlegg

  • NodeJS vs. ASP.NET
  • Pulten min..
  • No ifs and buts
  • Community-fiskebolle på ROOTS 2012
  • Kategorier

  • .net ninja (37)
  • Bøker (18)
  • Diverse prosjekter (37)
  • DSL (10)
  • Erlang (10)
  • F# (5)
  • Hardware (1)
  • Jobb (78)
  • Julekalender (51)
  • kjempekjekt.com (23)
  • LISP/Clojure (34)
  • NDC (4)
  • NNUG / community (63)
  • O/RM & databaser (10)
  • Off topic (118)
  • OO-design/clean code (31)
  • Podcasts (15)
  • Polyglot (82)
  • Ruby (29)
  • Silverlight / RIA (3)
  • Software/verktøy (20)
  • Softwareutvikling (24)
  • Testing / TDD (30)
  • the contiki strip (13)
  • User experience (3)
  • WCF (3)
  • Webutvikling (34)
  • WPF (9)
  • WTF (13)
  • Last ned Wallpaper

    Programmeringsbloggens tøffe skrivebordsbakgrunn med snippets fra ulike språk laster du ned her!

    Abonner via epost

    Om du vil kan du få alle nye blogposter tilsendt til din epost. Abonner nå, det er kjempeenkelt!

    Meta