# WPF voor de Webmaneen verhaal van pijn, wanhoop en leren.

<datetime class="hidden">2010-08-16T00:00</datetime>

<!-- category -- mostlylucidcouk, Imported -->
Nou, OK ik ben over dramatiseren het een beetje:) Zoals je misschien hebt gemerkt van mijn [laatste post](http://mostlylucid.net/archive/2010/01/07/1331.aspx) Ik ben begonnen met het werken van een aantal dingen die buiten mijn comfort zone. Ik ben onlangs toegetreden tot een bedrijf genaamd [cozwecanunit synonyms for matching user input](http://blog.cozwecan.com),werken voor een kerel genaamd [@RobertTheGrey](http://twitter.com/robertthegrey), het eerste project waar we aan werken is een foto verkoop site...je weet wel een soort van zoals Getty Images / Smugmug / Flickr behalve voor directe fotograaf-klant verkoop . Als de enige [full-time ontwikkelaar](http://blog.cozwecan.com/2010/01/year-and-team-members.html) (tot nu toe!) Ik ben belast met het bouwen van de overgrote meerderheid van de toepassingen (zij het web, desktop, enz..) die we nodig hebben voor het bedrijf om te werken.

Uiteraard een van de meest kritische stukjes voor een e-commerce site is dingen te verkopen! In ons geval is dit een voorraad van echt hoge kwaliteit foto's genomen door professionele fotografen over de hele wereld. Eerste vraag; hoe krijgen we deze beelden op de site, en hoe laten we de fotografen toe te voegen informatie aan hun foto's om ze te laten worden gevonden door mensen die ze willen kopen? Nou, dat is het eerste probleem dat ik ben gevraagd om op te lossen!

Bij het werken aan de vereisten, werd het al snel duidelijk dat dit een desktop-app moest zijn, het nodig om operaties op multi-megabyte foto's, die nodig zijn om het mogelijk te maken . tagging . Vooral tijdens het eerste laden, er zijn 10-100s van duizenden beelden die worden gewerkt aan...forcing this to be online voegt een vervelende vertraging en maakt de taak een pijn in de kont te voltooien. Boven alles, het moest betrouwbaar zijn!  
Nu, in mijn dev hoofd leidde dit tot een aantal beslissingen over de functies die de app nodig zou hebben om te ondersteunen:

1. Intuïtieve, responsieve UI; de gebruikers zijn niet noodzakelijk computer-savvy, moeten duidelijke UI metaforen te gebruiken en snel genoeg om aangenaam te zijn om te gebruiken.
2. Needs to do some offline image processing; we zijn van plan op het hosten van dit op de cloud....CPU tijd kost contant geld:)
3. Zou offline moeten werken
4. Moet worden gebouwd rond betrouwbare upload van multi-meg bestanden (over potentieel kleine verbindingsbandbreedtes).

Dus, dit zijn de basisbehoeften  technisch gezien leidt dit me naar een aantal technologische keuzes:

1. Multi-threading / concurrency (zoals met elke echte desktop app) is de sleutelas is een fatsoenlijke Desktop technologie
2. Zie hierboven, moeten we kijken naar nieuwe bestanden die aankomen, maken miniaturen transparant etc
3. Dus, het moet een soort van persistentie...samen met andere niet-core app eisen (bijv. tagging) we waarschijnlijk een beetje, lichtgewicht DB nodig hebben.
4. Behoefte aan block op basis van uploads met retry / verificatie van geüploade items

Gelukkig ben ik al een tijdje een dev en had tenminste een idee waar te beginnende grootste uitdaging zou het leren zijn!  
Ten eerste, de voor de hand liggende desktop platform de jour is WPF...dit is in wezen de huidige vervanging voor WinForms, het is zeer aanpasbaar, maar heeft een enigszins berucht steile leercurve. Ik heb besteed een beetje geld op [boeken](http://www.amazon.co.uk/Pro-WPF-2008-Presentation-Foundation/dp/1590599551/ref=sr_1_1?ie=UTF8&s=books&qid=1264019165&sr=8-1) op [WPF](http://www.amazon.co.uk/WPF-Action-Visual-Studio-2008/dp/1933988223/ref=sr_1_1?ie=UTF8&s=books&qid=1264019184&sr=8-1), volgde alle PDC-presentaties, podcasts etc.. over het onderwerp. Na 3 weken ik eindelijk krijgen comfortabel mee...  
Ten tweede had ik een DB nodig... die licht van gewicht moest zijn, nodig om LINQ te ondersteunen (wat, ik hou van LINQ:)), nodig om een soort ORM-technologie te ondersteunen.  
De meest voor de hand liggende keuze was [SQL-server Compacte Edition](http://www.microsoft.com/Sqlserver/2008/en/us/compact.aspx), een soort van knippen SQL server die geweldig is voor desktop apps...however...Ik kon het niet aan de slag! Blijkt dat het echt gevoelig is voor de versies van verschillende geïnstalleerde componenten. Dit was een rood licht voor mij...Ik had de DB zo stand-alone mogelijk! Dat liet een voor de hand liggende keuze:  
SQLite, van de website:

SQLite is een softwarebibliotheek die een [op zichzelf staande](http://www.sqlite.org/selfcontained.html), [serverloos](http://www.sqlite.org/serverless.html), [nulconfiguratie](http://www.sqlite.org/zeroconf.html), [transactie](http://www.sqlite.org/transactional.html) SQL database engine. SQLite is de [meest ingezet](http://www.sqlite.org/mostdeployed.html) SQL database engine in de wereld. De broncode voor SQLite is in de [publiek domein](http://www.sqlite.org/copyright.html).’

Perfect! Voor .NET ondersteuning, ik gebruikte de [SQLite ADO.NET provider](http://sqlite.phxsoftware.com/) (er is een [C# alleen poort van SQLite](http://code.google.com/p/csharp-sqlite/), maar het is langzamer en niet nodig voor mijn scenario. Implementatie is letterlijk inclusief een referentie en het maken van een bestand...nu heeft het een vrij ernstige beperking die ik later zal dekken:)

Mijn ORM keuzes waren [nHibernate](https://www.hibernate.org/343.html) of EF... er ondersteuning voor SQLite in beide platformen.  
Nu, ik heb gebruikt nHibernate voor een [recent project](http://www.trainyourbusinessbrain.com/business-brain-training/) (disclaimer, Ik vertrok vrij vroeg in het project:)) en een van de dingen die me irriteerde was het grote aantal afhankelijkheden, en de schijnbare kwetsbaarheid over versies van deze afhankelijkheden. Voor een lage impact desktop app, dit was gewoon te groot een voetafdruk en een te groot risico. Dus ik moest het uit te tellen.  
Dit liet me met één keuze[Entiteitskader](http://en.wikipedia.org/wiki/ADO.NET_Entity_Framework). Nu, dit was niet een gemakkelijke keuze...Ik heb EF gebruikt in het verleden (getinkerd met het) en vond het een pijnlijke ervaring...(anderen hebben ook die [leidde tot dit](http://efvote.wufoo.com/forms/ado-net-entity-framework-vote-of-no-confidence/)Nu, ik ben niet het krijgen in de politiek achter MS keuze om EF te duwen (en ja, het was een politieke keuze), maar V1 is vrij buggy, en voor mij in ieder geval redelijk onnatuurlijk. Dit leidde tot mijn tweede leren en...Master Entity Framework! Als ik dit schrijf, het is letterlijk 2 uur sinds ik mijn laatste beetje EF pijn...die leidde tot dit model:

[![Opname](Capture_thumb_3C08CB45.jpg "Capture")](http://www.mostlylucid.net/Images/WPFfortheWebguyastoryofpaindespairandlea_126C4/Capture.jpg)

Het ziet er eenvoudig uit... nou ja, ja...maar het heeft me nodig AGES om dit te krijgen...Ik ken SQL echt goed, C# ECHT goed, maar de mix van obscure concepten, slechte documentatie en verduisterde foutmeldingen maakt werken / leren EF veel te moeilijk! EF 4 (in VS 2010 / .NET 4) verbetert dit...en het kan niet snel genoeg komen!

Dus, dat heeft gewerkt  Ik hoop :)

Dus, Ik heb eerder vermeld dat SQLite heeft een beetje van een probleem...het doet niet echt als concurrency. Nou, dat is een overstatement...het is een multi-lees, single write systeem. Zodra je beseft dat het een probleem het is vrij gemakkelijk op te lossen. Ik gebruik dit patroon:

```
public static void AddRange(IEnumerable<UploadFile> files)
        {
            using (PixEntities ent = new PixEntities())
            {
                using (new ReadLock(entityLock))
                {

                    using (new WriteLock(entityLock))
                    {
                        foreach (var file in files)
                        {
                            ent.AddToUploadFiles(file);
                            ent.SaveChanges();
                        }
                    }
                }
            }
        }
```

```
 
```

Zoals je zag, heeft dit twee hoofdelementenIk gebruik een ReadLock (waar ik normaal zou lezen \*van\* de DB, in dit geval umm, ik doe niet :)) dan gebruik ik een WriteLock om elke bewerking waar ik schrijf naar de DB in te pakken. De observant zal ook merken wat eruit ziet als een bug...ik doe ent.SaveChages() voor elke iteratie van de lus...dit is een andere SQLite-quirk, het doet geen ondersteuning batch updates...en geeft een vreemde fout over bestand vergrendelt als je probeert :)

De kleine sluitklasse die ik gebruik is van [Hier.](http://dotnetcommandos.com/blogs/brianr/archive/2008/09/26/thread-safe-dictionary-in-net.aspx), maar ik heb het hieronder gezet voor uw gebruik  hoe dan ook, dit is deel een van verschillende delen van mijn ervaringen in het werken aan [cozwecanunit synonyms for matching user input](http://blog.cozwecan.com)Blijf kijken.

```
  public static class Locks
    {
        public static void GetReadLock(ReaderWriterLockSlim locks)
        {
            bool lockAcquired = false;
            while (!lockAcquired)
                lockAcquired = locks.TryEnterUpgradeableReadLock(1);
        }

        public static void GetReadOnlyLock(ReaderWriterLockSlim locks)
        {
            bool lockAcquired = false;
            while (!lockAcquired)
                lockAcquired = locks.TryEnterReadLock(1);
        }

        public static void GetWriteLock(ReaderWriterLockSlim locks)
        {
            bool lockAcquired = false;
            while (!lockAcquired)
                lockAcquired = locks.TryEnterWriteLock(1);
        }

        public static void ReleaseReadOnlyLock(ReaderWriterLockSlim locks)
        {
            if (locks.IsReadLockHeld)
                locks.ExitReadLock();
        }

        public static void ReleaseReadLock(ReaderWriterLockSlim locks)
        {
            if (locks.IsUpgradeableReadLockHeld)
                locks.ExitUpgradeableReadLock();
        }

        public static void ReleaseWriteLock(ReaderWriterLockSlim locks)
        {
            if (locks.IsWriteLockHeld)
                locks.ExitWriteLock();
        }

        public static void ReleaseLock(ReaderWriterLockSlim locks)
        {
            ReleaseWriteLock(locks);
            ReleaseReadLock(locks);
            ReleaseReadOnlyLock(locks);
        }

        public static ReaderWriterLockSlim GetLockInstance()
        {
            return GetLockInstance(LockRecursionPolicy.SupportsRecursion);
        }

        public static ReaderWriterLockSlim GetLockInstance(LockRecursionPolicy recursionPolicy)
        {
            return new ReaderWriterLockSlim(recursionPolicy);
        }
    }

    public abstract class BaseLock : IDisposable
    {
        protected ReaderWriterLockSlim _Locks;

        public BaseLock(ReaderWriterLockSlim locks)
        {
            _Locks = locks;
        }

        public abstract void Dispose();
    }

    public class ReadLock : BaseLock
    {
        public ReadLock(ReaderWriterLockSlim locks)
            : base(locks)
        {
            Locks.GetReadLock(this._Locks);
        }

        public override void Dispose()
        {
            Locks.ReleaseReadLock(this._Locks);
        }
    }

    public class ReadOnlyLock : BaseLock
    {
        public ReadOnlyLock(ReaderWriterLockSlim locks)
            : base(locks)
        {
            Locks.GetReadOnlyLock(this._Locks);
        }

        public override void Dispose()
        {
            Locks.ReleaseReadOnlyLock(this._Locks);
        }
    }

    public class WriteLock : BaseLock
    {
        public WriteLock(ReaderWriterLockSlim locks)
            : base(locks)
        {
            Locks.GetWriteLock(this._Locks);
        }

        public override void Dispose()
        {
            Locks.ReleaseWriteLock(this._Locks);
        }
    }
```