This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
WPF Web-kaverille: tarina kivusta, epätoivosta ja oppimisesta
Monday, 16 August 2010
No, OK, en dramatisoi sitä hieman :) Kuten olet ehkä huomannut minun viimeinen viesti Olen alkanut työskennellä joitakin asioita, jotka ovat minun mukavuusalueeni ulkopuolella. Liityin äskettäin yritys nimeltä Cozwecan, joka työskentelee kaverille nimeltä @RobertTheGrey, Ensimmäinen projekti, jonka parissa työskentelemme, on kuvamyyntisivusto... tiedät kuten Getty Images / Smugmug / Flickr lukuun ottamatta suoraa valokuvaaja-asiakasmyyntiä. kokopäiväinen kehittäjä (tähän mennessä!) Olen saanut tehtäväkseni rakentaa suurimman osan sovelluksista (olivatpa ne sitten verkkoja, työpöytiä jne.), joita tarvitaan, jotta yritys voi toimia.
Meidän tapauksessamme tämä on todella korkealaatuisten valokuvien varasto, jonka ammattikuvaajat ovat ottaneet ympäri maailmaa. Ensimmäinen kysymys; miten saamme nämä kuvat sivustolle, ja miten annamme valokuvaajien lisätä kuviaan, jotta ihmiset, jotka haluavat ostaa niitä, voivat löytää ne? No, tämä on ensimmäinen ongelma, jota minua on pyydetty ratkaisemaan!
Vaatimusten parissa työskennellessä kävi nopeasti ilmi, että tämän piti olla työpöytäsovellus, sen piti mahdollistaa monimegatavuisten valokuvien toiminta, jota tarvittiin, jotta "taggaaminen ei olisi mahdollista verkossa. Varsinkin alkulatauksen aikana 10-100-lukujen tuhansien kuvien saaminen tehdyksi... tämän pakottaminen verkossa lisää ikävää viivettä ja tekee tehtävästä tuskallisen. Ennen kaikkea sen piti olla luotettavaa!
Nyt, minun päässäni tämä johti useisiin päätöksiin ominaisuuksista, joita sovellus tarvitsee tukeakseen:
- Intuitiivinen, reagoiva käyttöliittymä; käyttäjät eivät välttämättä ole tietokoneohjattavia, heidän täytyy käyttää ilmeisiä käyttöliittymän metaforia ja tehdä siitä niin nopea, että se on miellyttävää käyttää.
- Täytyy tehdä joitakin offline kuvankäsittely; aiomme isännöidä tätä pilvipalvelussa....CPU aika maksaa käteisellä :)
- Toimii offline-tilassa
- On rakennettava useiden megatiedostojen luotettavan lataamisen ympärille (mahdollisesti pienten yhteyksien kaistanleveyksien yli).
Nämä ovat perustarpeet. Teknisesti tämä johtaa minut joihinkin teknologiavalintoihin:
- Multi-threading / concurrency (kuten mikä tahansa oikea työpöytäsovellus) on avainkuten on kunnollinen Desktop-tekniikka
- Katso yllä, meidän on "tarkkailtava uusien tiedostojen saapumista, tehtävä kuvakynnet läpinäkyviksi jne
- Joten, se tarvitsee jonkinlaista sinnikkyyttä...yhdessä muiden ei-ydinsovellusvaatimusten (esim. merkintä) kanssa tarvitsemme todennäköisesti hieman kevyttä DB:tä.
- "Tarvitsee "block-pohjaisia latauksia, joissa ladattavien kohteiden uudelleenyrittäjyys/varmennus
Onneksi olen ollut dev jo jonkin aikaa ja minulla on ollut edes jonkinlainen käsitys siitä, mistä aloittaasuurin haaste olisi oppiminen!
Ensinnäkin, ilmeinen pöytäalusta de jour on WPF...tämä on pohjimmiltaan nykyinen korvaaja WinForms, se on erittäin muokattavissa, mutta on hieman pahamaineisen jyrkkä oppimiskäyrä. Olen käyttänyt hieman rahaa kirjat Euroopan parlamentin ja neuvoston asetus (EU) N:o 1306/2013, annettu 11 päivänä joulukuuta 2013, Euroopan parlamentin ja neuvoston asetusten (EU) N:o 1306/2013, (EU) N:o 1306/2013, (EU) N:o 1306/2013, (EU) N:o 1306/2013, (EU) N:o 1306/2013, (EU) N:o 1306/2013, WPF, seurasivat kaikkia PDC:n esityksiä, podcasteja jne. aiheesta. Kolmen viikon kuluttua olen vihdoin tyytyväinen siihen...
Toiseksi tarvitsin DB:n, jonka piti olla kevyt ja tukea LINQ:ta (mitä pidän LINQ:sta :)) jonkinlaisen ORM-teknologian tueksi.
Ilmeisin valinta oli SQL-palvelimen kompakti versio, eräänlainen leikata alas SQL-palvelin, joka on hyvä pöytäsovelluksille... kuitenkin...En saanut sitä toimimaan! Osoittautuu, että se on todella herkkä versioita eri asennettuja komponentteja. Tämä oli punainen valo minulle...Tarvitsin DB olla niin itsenäinen kuin mahdollista! Se jätti yhden ilmeisen valinnan:
SQLite, verkkosivuilta:
"SQLite on ohjelmistokirjasto, joka toteuttaa irrallinen, palvelinkyvytön, nollakonfiguraatio, Transactional SQL tietokanta moottori. SQLite on Yleisimmin käytössä SQL tietokanta moottori maailmassa. Lähdekoodi SQLite on Julkisuus.’
Täydellinen! .NET-tuella, käytin SQLite ADO.NET -palveluntarjoaja (on olemassa C# SQLiten ainoa portti, mutta se on hitaampi ja ei tarvitse minun skenaario. Käyttöönotto on kirjaimellisesti mukaan lukien viite ja luoda tiedoston ... nyt se ei ole yksi aika vakava rajoitus, joka kattaa myöhemmin :)
ORM-valintani olivat nHibernaatti SQLite saa tukea molemmilta alustoilta.
Nyt olen käyttänyt nHibernate tuore projekti (Väittelijä, lähdin aikaisessa vaiheessa projektia :)) ja yksi minua ärsytti suuri riippuvuus ja näennäisen hauraus eri versioissa näistä riippuvuuksista. Matalan vaikutuksen työpöytäsovellukselle tämä oli vain liian suuri jalanjälki ja liian suuri riski. Joten jouduin laskemaan sen pois.
Tämä jätti minulle yhden vaihtoehdon:"Yhteenkuuluvuuskehys". Tämä ei ollut helppo valinta...Olen käyttänyt EF:ää aiemmin (niin sanottuna) ja pitänyt sitä tuskallisena kokemuksena...(muillakin on, että johti tähän). Nyt, En ole pääsemässä politiikkaan MS valinnan takana työntää EF (ja kyllä, se oli poliittinen valinta), mutta V1 on melko ökyrikas, ja minulle ainakin melko luonnoton. Tämä johti minun toinen oppiminen "mahdollisuus"...Master Entity Framework! Kun kirjoitan tätä, se on kirjaimellisesti 2 tuntia siitä, kun olen treenannut minun viimeinen EF kipu ..., joka johti tähän malliin:
Näyttää yksinkertaiselta...no, joo...mutta se vei minut AGES päästä tähän...Tiedän SQL todella hyvin, C# Todella hyvin, mutta sekoitus hämäriä käsitteitä, huono dokumentointi ja obfuscated virheviestejä tekee työskentelyn / oppiminen EF aivan liian kovaa! EF 4 (VS 2010 / .NET 4) parantaa tätä...ja se voi tulla tarpeeksi pian!
Joten, ettäToivon :)
Joten, Mainitsin aiemmin, että SQLite on hieman ongelma ... se ei todellakaan pidä concurrency. No, että on liioiteltu...se on monilukuinen, yksikirjaiminen järjestelmä. Kun tajuat, että se on kysymys se on melko helppo ratkaista. Käytän tätä kaavaa:
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();
}
}
}
}
}
Kuten näette, tässä on kaksi pääelementtiäKäytän ReadLockia (jossa normaalisti lukisin *alkaen* DB, tässä tapauksessa umm, En dont :)) Sitten käytän WriteLock kääriä minkä tahansa operaation, jossa kirjoitan DB. Tarkkailija huomaa myös, mikä näyttää vika...Teen ent.SaveChages() jokaista iterointia varten silmukan...Tämä on toinen SQLite quirk, se ei tue erän päivityksiä ... ja antaa oudon virheen tiedostojen lukkoja, jos yrität :)
Käyttämäni pieni lukitusluokka on peräisin täällä, mutta olen laittanut sen alle käyttöösi joka tapauksessa, tämä on osa useita osia kokemuksistani työstää CozwecanPysy kanavalla!
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);
}
}