# ФБ для веб-хлопчика історія болю, відчаю і навчання

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

<!-- category -- mostlylucidcouk, Imported -->
Гаразд, гаразд, я більше ніж драматург:) Як ви, можливо, помітили з мого [останній допис](http://mostlylucid.net/archive/2010/01/07/1331.aspx) Я почав працювати над чимось, що знаходиться поза зоною комфорту. [czwecan](http://blog.cozwecan.com), Працюючи на хлопця на ім'я [@ RobertTheGrey](http://twitter.com/robertthegrey), перший проект, над яким ми працюємо це сайт продажу фотографій ви знаєте свого роду як Getty Images / Smugmug / Flickr, крім прямих фотограф-виробників. [Повночасний розробник](http://blog.cozwecan.com/2010/01/year-and-team-members.html) Я отримав завдання побудувати переважну більшість програм (безпечно, на стільниці тощо), які нам потрібні для роботи.

Очевидно, одним з найбільш критичних бітів для сайту e-comerce є речі, які можна продавати! У нашому випадку це сукупність дійсно високоякісних фотографій зроблених професійними фотографами по всьому світу.

Працюючи над вимогами, швидко стало зрозуміло, що це повинна бути програма для стільниці, вона повинна була дозволити виконання дій на фотографіях з декількома мегабайтами, які повинні були дозволити  ведьмсити поза мережею. Особливо під час початкового завантаження, там є 10- 100- 100 зображень, над якими можна працювати... допускаючи, що це буде онлайн додає неприємну затримку і робить завдання болем у сідниці, щоб завершити роботу. Понад усе, воно повинно бути надійним!  
Тепер, в моїй голові dev це призвело до кількох рішень щодо можливостей, які програма повинна підтримувати:

1. Інтуїтивний, реагуючий інтерфейс інтерфейсу користувача; користувачі не обов' язково повинні використовувати очевидні метафори інтерфейсу користувача і робити його достатньо швидким, щоб бути приємним для користування.
2. Потрібно зробити якусь автономну обробку зображень; ми плануємо розмістити це на хмарі.. CPU витрати часу готівкою:)
3. Чи має працювати у автономному режимі
4. Потрібно зібратися на надійному вивантаженні файлів meg (ймовірно крихітних канальців з' єднання).

Отже, це основні потреби технічно це приводить мене до певного технологічного вибору:

1. Multi-threading / application (так як з будь- яким іншим комп' ютерним додатком) є ключ так само, як і пристойна технологія стільниці
2. Дивіться вище, нам потрібно  ведьйок, щоб з'явилися нові файли, зробити мініатюри прозоро і т.д
3. Таким чином, вона потребує певної наполегливості... тісноти з іншими некоштовними вимогами додатку (наприклад, taging) нам, ймовірно, потрібно трохи, легкого КБ.
4. Потребує вивантажень з повторенням або перевірки вивантажених об' єктів

То був би досить довгий час і мав би хоча б якесь уявлення з чого почати найбільшим викликом було б навчання!  
По-перше, очевидною стільничною платформою є WPF... це, по суті, поточна заміна WinForms, вона має широку форму, але має дещо ганебно круту криву навчання. я витратив трохи грошей на [книги](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) увімкнено [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)...посліду всіх презентацій DC, трансляції і т.д. на тему.  
По-друге, мені потрібен був DB ... потрібен був, щоб підтримувати LINQ (що, мені подобається LINQ:), щоб підтримувати якусь технологію ORM.  
Найочевидніший вибір був [Компактне редагування сервера SQL](http://www.microsoft.com/Sqlserver/2008/en/us/compact.aspx), вид вирізання сервера SQL, який є чудовим для стільничних програм.... У будь- якому разі... я не міг цього зробити! Виявляється, що він дійсно чутливий до версій різноманітних встановлених компонентів. Це було червоне світло для мене... Мені потрібно, щоб DB був якомога один! Це залишило один очевидний вибір:  
SQLite, з веб-сайту:

⇩SQLite - це бібліотека програмного забезпечення, яка реалізує а [Самодостатнє](http://www.sqlite.org/selfcontained.html), [без сервера](http://www.sqlite.org/serverless.html), [нульове налаштування](http://www.sqlite.org/zeroconf.html), [Операційний](http://www.sqlite.org/transactional.html) Рушій бази даних SQL. SQLite - це рушій бази даних SQL [Найпоширеніший](http://www.sqlite.org/mostdeployed.html) Рушій бази даних SQL у світі. Вихідні коди SQLite знаходяться у базі даних SQLite [публічний домен](http://www.sqlite.org/copyright.html).’

Чудово! я використовував його [Постачальник ADO. NET SQLite](http://sqlite.phxsoftware.com/) (там є [C# Лише порт SQLite](http://code.google.com/p/csharp-sqlite/)Обмеження буквально включає посилання і створення файла... зараз воно має одне досить серйозне обмеження, яке я потім огорну:

Мій вибір ORM: [nHibernate](https://www.hibernate.org/343.html) або ЕФ... підтримує SQLite на обох цих платформах.  
Я використав NHibernate для [нещодавній проект](http://www.trainyourbusinessbrain.com/business-brain-training/) (невизначений, я залишив досить рано в проекті:) і одна з речей, яка дратувала мене була велика кількість залежностей, і, здавалося б, нестабільність між версіями цих залежностей. для низької робочої програми, це було просто занадто великим слідом і занадто великим ризиком. Отже, я повинен був порахувати це.  
Це лишило мені один вибір[Робота з блоками сутностей](http://en.wikipedia.org/wiki/ADO.NET_Entity_Framework)Тепер, це був простий вибір... я використовував ЕФС в минулому (зіткнувшись з ним) і виявив, що це болісний досвід... }Інші теж мають те, що [ведучий до цього](http://efvote.wufoo.com/forms/ado-net-entity-framework-vote-of-no-confidence/). (і так, це був політичний вибір), але V1 - досить макіяж, і для мене, принаймні, цілком неприродно.

[![Захоплення](Capture_thumb_3C08CB45.jpg "Capture")](http://www.mostlylucid.net/Images/WPFfortheWebguyastoryofpaindespairandlea_126C4/Capture.jpg)

Це виглядає просто, так... але вона забирає мене до цього... я добре знаю SQL дійсно добре, C # дійсно добре, але суміш неясних концепцій, погана документація і заплутані повідомлення про помилки роблять працю / навчання EF занадто складним! EF 4 (у VS 2010 /.NET 4) покращує це... і це може прийти досить швидко!

Отже, це спрацювало Надіюсь:

Отже, раніше я згадував, що SQLite має частину спірного питання... це дійсно не те саме, що співпадіння.

```
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();
                        }
                    }
                }
            }
        }
```

```
 
```

Як ви бачили, тут є два основних елементи Я використовую ReadLock (де я зазвичай читаю \* from\* DB, у цьому випадку mm, я не можу:) тоді я використовую writeLock для того, щоб завершити будь- яку дію, де я пишу до DB. Сценарій також буде помічати те, що виглядає як вада... Я роблю ent. SaveChages) для кожної ітерації петлі... це ще одна річ SQLite, вона не підтримує пакетні оновлення... і надає дивну помилку щодо блокування файлів, якщо ви спробуєте:)

Невеличкий курс блокування, який я використовую, з [тут](http://dotnetcommandos.com/blogs/brianr/archive/2008/09/26/thread-safe-dictionary-in-net.aspx)Але я поклав її для вашого використання в будь-якому випадку, це частина мого досвіду в роботі над [czwecan](http://blog.cozwecan.com)тише!

```
  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);
        }
    }
```