# 网络网友WPF 一个痛苦、绝望和学习的故事

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

<!-- category -- mostlylucidcouk, Imported -->
好吧,好吧,我太过分戏剧化它了 : () 正如你可能从我身上注意到的 [上一个员额](http://mostlylucid.net/archive/2010/01/07/1331.aspx) 最近我加入了一家名为 [哥兹韦康语Name](http://blog.cozwecan.com),为名为 [@RobertTheGrey @ 罗伯特·格雷](http://twitter.com/robertthegrey)我们的第一个项目是照片销售网站...你知道的有点像Getty images/Smugmug/Flickr, 除了直接的摄影师-客户销售之外。作为唯一的图片销售网站。 [全职开发者](http://blog.cozwecan.com/2010/01/year-and-team-members.html) 我的任务就是建立绝大多数的应用程序(无论是网络、桌面等),

电子商业网站最关键的部分之一显然是要卖的东西。 在我们的例子中,这是世界各地专业摄影师拍摄的非常高质量的照片。 第一个问题:我们如何在网站上获取这些图像,以及我们如何让摄影师在照片上添加信息,让那些想买他们的人找到这些照片? 嗯,这是我第一个被要求解决的问题!

制定要求时,很快发现这必须是一个桌面应用程序,它需要允许多兆字节照片的操作,而多兆字节照片的操作需要允许“窃听 ” 离线。 特别是在初始装货过程中,有10,100万张图像正在制作...强制在线,这增加了一个令人讨厌的滞后,使任务在屁股上变得痛苦。 最重要的是,它需要可靠!  
现在,在我的Dev头部,这导致 一系列决定 关于这些功能的应用程序需要支持:

1. 直觉的、反应灵敏的UI;用户不一定是计算机保存的,需要使用明显的UI隐喻,使其速度足够快,便于使用。
2. 需要做一些离线图像处理; 我们正计划在云上托管这个...。 CPU时间成本为现金: )
3. 应在离线下工作
4. 需要围绕可靠的多兆格文件上传(跨潜在小连接带宽)来构建。

这些是基本需求 从技术上讲,这让我找到一些技术选择:

1. 多行/共通货币(与任何真正的桌面应用程序一样)是关键就像一个体面的桌面技术
2. 我们需要“观察”新文件抵达, 使缩略图透明,
3. 所以,它需要某种持久性... 加上其他的非核心部分需求(例如标记), 我们可能需要一点点,轻量级的DB。
4. 需要基于“ block” 的上传并重试/ 校验上传项目

幸运的是, 我当了相当长一段时间的牧师, 至少知道从何开始, 最大的挑战就是学习!  
首先,显而易见的桌面平台是WPF(WPF)......这实质上是目前WinForms(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) 上 年 月 [WPPF WPPF](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)3周后, 我终于感到舒服了...  
第二,我需要一个DB... 需要轻量级, 需要支持LINQ(什么,我喜欢LINQ:)), 需要支持某种ORM技术。  
最明显的选择是 [SQL 服务器压缩版SQL服务器](http://www.microsoft.com/Sqlserver/2008/en/us/compact.aspx), SQL 服务器被削减了, 这对于桌面应用程序来说是件好事... 但是... 我无法成功! 事实证明它对各种安装组件的版本非常敏感 。 这对我来说是一个红灯... 我需要 DB尽可能独立! 这留下了一个显而易见的选择 :  
SQLite,来自网站:

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 是 [部署最广泛](http://www.sqlite.org/mostdeployed.html) SQL 世界上 SQL 数据库引擎。 SQLite 的源代码在 [公共领域](http://www.sqlite.org/copyright.html).’

完美! [SQLite ADO.NET 提供商](http://sqlite.phxsoftware.com/) (有一个 [C # 只有SQLite 端口](http://code.google.com/p/csharp-sqlite/)部署实际上包括一个参考和创建一个文件。 现在它确实有一个非常严格的限制, 我稍后会覆盖这个限制。 )

我的ORM选择 [怀内](https://www.hibernate.org/343.html) 或EF... 支持SQLite 在这两个平台。  
现在,我用了NHHEVERTA [最近的项目](http://www.trainyourbusinessbrain.com/business-brain-training/) (否认,我在项目中提前离开了 : ) , 令我恼怒的一件事是大量依赖关系,以及这些依赖关系的不同版本之间看起来的脆弱性。 对于一个低影响桌面应用程序来说,这只是一个太大的足迹和太大的风险。 因此我不得不把它计数出来。  
这给我留下了一个选择[实体框架](http://en.wikipedia.org/wiki/ADO.NET_Entity_Framework). 现在,这不是一个容易的选择.我过去曾经使用EF(与EF有关),发现这是一个痛苦的经历.(其他人也有这种经历)。 [导致此结果](http://efvote.wufoo.com/forms/ado-net-entity-framework-vote-of-no-confidence/)现在,我并不是在MS选择推行EF(而且,是的,这是一个政治选择)的背后进入政治,但是V1是相当古怪的,至少对我来说是相当非自然的。 这导致我第二次学习“机会 ” 。 “Master实体框架 ” 。 在我写这封信的时候,我花了2小时的时间才完成我最后一点EF的痛苦...这导致了这个模型:

[![捕获](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();
                        }
                    }
                }
            }
        }
```

```
 
```

正如你们所看到的,这有两个主要元素我用的是“阅读”(我通常会在那里读到) \* 发自\* DB, 在此情况下 umm, I don't : () ) 之后, 我用一个 WriterLock 来包扎我写给 DB 的任何操作。 观察者也会注意到什么看起来像一个错误... I do ent. savechages () 对于循环的每次迭代... 这是另一个 SQLite 奇克, 它不支持批次更新... 如果您尝试的话, 它会给文件锁出一个奇怪的错误 :

我用的小锁舱是来自 [在这里](http://dotnetcommandos.com/blogs/brianr/archive/2008/09/26/thread-safe-dictionary-in-net.aspx)但我把它放在下面供你使用 [哥兹韦康语Name](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);
        }
    }
```