Evernote Sync Via EDAM (代码篇)

预备结构

这里同步某一个特定笔记本的所有笔记的实现,而且笔记都是不带资源的。另外同步笔记是不需要处理重名问题的

在同步之前首先必然需要构造两个类,分别代表服务器端的数据,和本地端的数据,以及他们的一些操作。

下面给出这两个类的定义,详细代码太长。明后天完成整个程序后,会把所有的代码放到GitHub中进行开源。

其中在服务端的构造函数中完成了登入获取NoteStroe等功能,本地端的构造函数完成了从本地文件读取数据的功能

   1: class EvernoteServer
   2: {
   3:     public EvernoteServer()
   4:  
   5:     //创建AdageNotebook
   6:     private Notebook CreateAdageNotebook()
   7:  
   8:     //获取AdageNotebook下的所有的Note的元数据
   9:     //执行这个函数会在更新服务器中Note的Updated
  10:     public List<Note> GetActiveNotes()
  11:  
  12:     //获取在被删除的(在回收站中)Notes的元数据
  13:     //但是这个方法没办法获取永久性删除的数据。
  14:     public List<Note> GetInactiveNotes()
  15:  
  16:     //向服务端添加Note
  17:     //会对参数note的Guid 时间戳 USN等信息做修改
  18:     public void AddNote(Note note)
  19:  
  20:     //向服务端更新Note
  21:     //会对参数note的Guid 时间戳 USN等信息做修改
  22:     public void UpdateNote(Note note)
  23:  
  24:     //从服务端删除Note
  25:     public void DeleteNote(Note note)
  26:  
  27:     //从服务器获取带Content信息的Note
  28:     //执行这个函数会在更新服务器中note的Updated
  29:     public Note GetNote(Note note)
  30:  
  31:     //从服务器获取USN
  32:     public Int32 USN
  33:  
  34: }
   1: class LocalStroage
   2: {
   3:     public List<Note> AllNotes
   4:  
   5:     //获取本地存储的实例,从Config.LocalDataFileName读取获得。
   6:     public LocalStroage()
   7:  
  12:  
  13:     //根据模板创建一个Note,可以不添加到_list
  14:     public Note CreateNote(String adage, Boolean isAdd)
  15:     
  16:     //在本地存储添加Note
  17:     public void AddNote(Note note)
  18:  
  19:     //将于note.guid相同的节点,内容用newAdage替换
  20:     public void UpdateNote(Note note, String newAdage)
  21:  
  22:     //把list中与note.guid相同的节点用note的内容更新
  23:     //这里有可能发生,同步时服务器端把一个标记为deleted的节点更新成active的节点
  24:     public void UpdateNote(Note note)
  25:  
  26:     //从本地存储删除Note
  27:     public void DeleteNote(Note note)
  28:  
  29:     //永久性删除Note
  30:     public void ExpungeNote(Note note)
  31:  
  32:     //清除有删除标记的节点
  33:     public void Clean()
  34:  
  35:     //根据参数note的内容克隆一个Note
  36:     //克隆出来的Guid是不同的
  37:     //参数isAdd,时候将克隆出来的note加入到list
  38:     public Note CloneNote(Note note, Boolean isAdd)
  39:  
  40:     //保存到文件中
  41:     public void Save()
  42:  
  43:     //从文件中读取
  44:     private List<Note> Read()
  45: }

 

细节

从网络端获取的Note和本地的Note是不同对象的引用,所以做所有的本地操作的内部都要进行GUID的比对。

执行向服务器添加Note时,参数中的note的GUID和正在添加到服务器的GUID是不同的。可以通过NoteStore.createNote(...)的返回值进行修正。这个返回值是刚刚添加进去的Note的元数据

与添加和更新时传入的Note参数中的属性中的USN,Created,Updated等参数是不会被更新的,也是要根据返回值进行修正,这点对USN特别重要。

GetActiveNotes 和 GetInactiveNotes在有非常多笔记的情况下要时候多次读取合并,类似于之前理论中的同步块问题

进行读取操作(GetActiveNotes GetInactiveNotes GetNote)时会刷新服务器端的Updated时间戳,这个直接导致不能使用Updated判断服务器端是否被修改

本地要设计一个彻底删除的方法,不然本地的数据文件会越来越大

 

数据分析

通过服务器端的特性和本地操作时的一些设置。我设计出一下的数据

//本地新建的数据 是本地没有被删除的Notes和服务端所有(包括回收站中)Notes的差集

//本地删除的数据 Active == false 这个在本地执行删除时要设置
//本地修改的数据 Updated > Config.LastSyncTime 本地执行时要修改Note.Updated 因为时间我只用来判断本地数据是否被修改,所以我的LastSyncTime其实是本地时间

//服务器新建的数据 服务器没有被删除的Notes和本地所有的(包括被标记为删除的)Notes的差集

//服务器删除的数据 通过GetInactiveNotes 获取到的数据
//服务器修改的数据 USN> Config.LastUSN

同步策略

有两个个会冲突的地方

冲突1:X端删除了note1,Y端修改了note1,处理方式两端都保留被Y端删除过的note1

冲突2:X端修改了note1,Y端也修改了note1,报告给用户,让用户决定保存那个端的,或者保存两端。保存两端即做一个克隆,个头克隆出来note一个新的GUID。关于这个冲突处理,我设计了一个事件机制,里面放了一个ConfilctResolution的枚举,通过事件传递到外部,让外部处理。或者是采用默认方案(保存两侧)

同步代码

主要分为查询和执行两个部分。

执行部分的顺序是 新建 更新除了冲突2以外的东西 删除 处理冲突2

这个顺序先更新后删除很自然的处理好了冲突1。

查询部分大量的使用了LINQ语句,为了调试方便,没有进行太多优化(我也不太会优化这个东西)

具体代码如下

   1: //将本地数据文件与网络同步
   2:        public void Sync()
   3:        {
   4:            //本地新建的数据 serverNotesList.Exists(s => s.Guid == l.Guid) == false
   5:            //本地删除的数据 Active == false
   6:            //本地修改的数据 Updated > Config.LastSyncTime
   7:  
   8:            //服务器新建的数据 loaclNotesList.Exists(l=>l.Guid==s.Guid) == false
   9:            //服务器删除的数据 InactiveNotesList
  10:            //服务器修改的数据 USN > Config.LastUSN
  11:  
  12:            _server = new EvernoteServer();
  13:  
  14:            #region 查询
  15:  
  16:            //获取本地的所有Note
  17:            List<Note> localAllNotes = new List<Note>();
  18:            localAllNotes.AddRange(_local.AllNotes);
  19:            //获取本地活动的Note
  20:            List<Note> localActiveNotes =  localAllNotes.FindAll(l => l.Active == true);
  21:            //获取本地删除掉的Notes
  22:            List<Note> localInactiveList = localAllNotes.FindAll(n => n.Active == false);
  23:  
  24:            //获取服务器端中活动的Note
  25:            List<Note> serverActiveNotes = _server.GetActiveNotes();
  26:            //获取服务器端回收站中的Note
  27:            List<Note> serverInactiveNotes = _server.GetInactiveNotes();
  28:            //获取服务器端所有的Note
  29:            List<Note> serverAllNotes = new List<Note>();
  30:            serverAllNotes.AddRange(serverActiveNotes);
  31:            serverAllNotes.AddRange(serverInactiveNotes);
  32:  
  33:            //需要添加到本地的Notes////////////////////
  34:            var localNeedAddNotes = serverActiveNotes.FindAll(
  35:                                 s => !localAllNotes.Exists(l => l.Guid == s.Guid));
  36:  
  37:            //需要添加到服务器的Nots///////////////////
  38:            var serverNeedAddNotes = localActiveNotes.FindAll(
  39:                                 l => !serverAllNotes.Exists(s => s.Guid == l.Guid));
  40:  
  41:  
  42:            //本地修改过的Notes
  43:            var localUpdatedNotes = 
  44:                localActiveNotes.FindAll(l =>
  45:                    serverAllNotes.Exists(s => s.Guid == l.Guid) &&
  46:                                             l.Updated > Config.LastSyncTime);
  47:            //服务器端修改过的Notes
  48:            var serverUpdatedNotes = 
  49:                serverActiveNotes.FindAll(s =>
  50:                    localAllNotes.Exists(l => l.Guid == s.Guid) &&
  51:                    s.UpdateSequenceNum > Config.LastUSN);
  52:  
  53:            //出现冲突的Notes///////////////////////
  54:            var conflictList = localUpdatedNotes.FindAll(
  55:                                l => serverUpdatedNotes.Exists(s => s.Guid == l.Guid));
  56:  
  57:            //本地需要更新的Notes/////////////////////
  58:            var localNeedUpdateNotes = serverUpdatedNotes.FindAll(
  59:                                     s => !conflictList.Exists(c => c.Guid == s.Guid));
  60:  
  61:  
  62:            //服务器需要更新的Notes////////////////////
  63:            var serverNeedUpdateNotes = localUpdatedNotes.FindAll(
  64:                                l => !conflictList.Exists(c => c.Guid == l.Guid));
  65:  
  66:  
  67:  
  68:            //需要删除的本地Notes/////////////////////////
  69:            var localNeedDeleteNotes = serverInactiveNotes.FindAll(
  70:                       i => localAllNotes.Exists(l => l.Guid == i.Guid) &&
  71:                            !localUpdatedNotes.Exists(l => l.Guid == i.Guid));
  72:  
  73:  
  74:            //需要删除的服务器端的Notes////////////////////
  75:            var serverNeedDeleteNotes = localInactiveList.FindAll(
  76:                        l => serverActiveNotes.Exists(s => s.Guid == l.Guid) &&
  77:                             !serverUpdatedNotes.Exists(s => s.Guid == l.Guid));
  78:            #endregion
  79:  
  80:            #region 操作
  81:            //向本地添加Notes
  82:            foreach(Note metadata in localNeedAddNotes)
  83:            {
  84:                Note note = _server.GetNote(metadata);
  85:                _local.AddNote(note);
  86:            }
  87:            //向服务器添加Notes
  88:            foreach(Note note in serverNeedAddNotes)
  89:            {
  90:                _server.AddNote(note);
  91:            }
  92:  
  93:            //更新本地
  94:            foreach(Note metadata in localNeedUpdateNotes)
  95:            {
  96:                Note note = _server.GetNote(metadata);
  97:                _local.UpdateNote(note);
  98:            }
  99:            //更新服务器
 100:            foreach(Note note in serverNeedUpdateNotes)
 101:            {
 102:                _server.UpdateNote(note);
 103:            }
 104:  
 105:            //本地删除
 106:            foreach(Note note in localNeedDeleteNotes)
 107:            {
 108:                _local.ExpungeNote(note);
 109:            }
 110:            //从服务器删除
 111:            foreach(Note note in serverNeedDeleteNotes)
 112:            {
 113:                _server.DeleteNote(note);
 114:                _local.ExpungeNote(note);
 115:            }
 116:            ////////////冲突处理//////////
 117:            //报告冲突,获得处理方法
 118:            //这里只实现在在两端都保留的方法
 119:            foreach(Note localNote in conflictList)
 120:            {
 121:                //获取服务器冲突的Note
 122:                Note serverNote = _server.GetNote(localNote);
 123:  
 124:                //尝试交给外部,获取冲突解决方案
 125:                if(ConfilctHappen  != null)
 126:                {
 127:                    _confilctEvenArgs.LocalAdage = localNote.Title;
 128:                    _confilctEvenArgs.ServerAdage = serverNote.Title;
 129:                    ConfilctHappen(this, _confilctEvenArgs);
 130:                }
 131:  
 132:                switch(_confilctEvenArgs.Result)
 133:                {
 134:                    case ConfilctResolution.SaveBoth:
 135:                        //将localNote进行一个克隆,并且把克隆的Note加入到local
 136:                        Note cloneNote = _local.CloneNote(localNote, true);
 137:                        //把localNote更新成serverNote
 138:                        _local.UpdateNote(serverNote);
 139:                        //把cloneNote提交到服务器
 140:                        _server.AddNote(cloneNote);
 141:                        break;
 142:                    case ConfilctResolution.SaveServer:
 143:                        _local.ExpungeNote(localNote);
 144:                        _local.AddNote(serverNote);
 145:                        break;
 146:                    case ConfilctResolution.SaveLocal:
 147:                        _server.DeleteNote(localNote);
 148:                        _server.AddNote(localNote);
 149:                        break;
 150:                    default:
 151:                        break;
 152:                }
 153:  
 154:                //消除这一次验证的选择
 155:                _confilctEvenArgs.Result = ConfilctResolution.SaveBoth;
 156:            }
 157:  
 158:            //记录LastSyncTime
 159:            Config.LastSyncTime = Config.NowTime;
 160:            //记录LastUSN
 161:            Config.LastUSN = _server.USN;
 162:            //保持local,因为在server.addNote等方法中可能会改变Note的部分属性
 163:            _local.Save();
 164:            //清理local中没有用到的标记为删除的节点
 165:            _local.Clean();
 166:            #endregion
 167:  
 168:            //刷新内存中的列表
 169:            RefreshAdageCollection();
 170:            this.GenRandomAdage();
 171:        }
原文地址:https://www.cnblogs.com/atskyline/p/2709414.html