PageRenderTime 60ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/MVVMSidekick/MVVMSidekick.Shared/Storages.cs

https://github.com/zoujuny/MVVM-Sidekick
C# | 824 lines | 568 code | 222 blank | 34 comment | 17 complexity | 988c77132f24bd03135a7641bca95f0f MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.ComponentModel;
  6. using System.Linq.Expressions;
  7. using System.Runtime.Serialization;
  8. using System.Reflection;
  9. using System.Threading.Tasks;
  10. using System.Threading;
  11. using System.Windows.Input;
  12. using MVVMSidekick.ViewModels;
  13. using MVVMSidekick.Commands;
  14. using System.Runtime.CompilerServices;
  15. using MVVMSidekick.Reactive;
  16. using System.Reactive.Linq;
  17. using System.Reactive.Subjects;
  18. using System.Reactive;
  19. using MVVMSidekick.EventRouting;
  20. using MVVMSidekick.Utilities;
  21. using System.Collections.ObjectModel;
  22. using System.Collections.Specialized;
  23. using System.IO;
  24. using System.Collections;
  25. #if NETFX_CORE
  26. using Windows.UI.Xaml;
  27. using Windows.UI.Xaml.Data;
  28. using Windows.UI.Xaml.Controls;
  29. using System.Collections.Concurrent;
  30. using Windows.UI.Xaml.Navigation;
  31. using Windows.UI.Xaml.Controls.Primitives;
  32. using Windows.Storage;
  33. #elif WPF
  34. using System.Windows;
  35. using System.Windows.Controls;
  36. using System.Windows.Data;
  37. using System.Collections.Concurrent;
  38. using System.Windows.Navigation;
  39. using MVVMSidekick.Views;
  40. using System.Windows.Controls.Primitives;
  41. #elif SILVERLIGHT_5||SILVERLIGHT_4
  42. using System.Windows;
  43. using System.Windows.Controls;
  44. using System.Windows.Data;
  45. using System.Windows.Navigation;
  46. using System.Windows.Controls.Primitives;
  47. using System.IO.IsolatedStorage;
  48. #elif WINDOWS_PHONE_8||WINDOWS_PHONE_7
  49. using System.Windows;
  50. using System.Windows.Controls;
  51. using Microsoft.Phone.Controls;
  52. using System.Windows.Data;
  53. using System.Windows.Navigation;
  54. using System.Windows.Controls.Primitives;
  55. using System.IO.IsolatedStorage;
  56. #endif
  57. namespace MVVMSidekick
  58. {
  59. namespace Storages
  60. {
  61. /// <summary>
  62. /// <para>Simple storage interface, for persistence.</para>
  63. /// <para>简单的持久化存储类型接口</para>
  64. /// </summary>
  65. /// <typeparam name="T">
  66. /// <para>The Type needs to be save/load</para>
  67. /// <para>需要存取的类型</para>
  68. /// </typeparam>
  69. public interface IStorage<T>
  70. {
  71. /// <summary>
  72. /// <para>Ignore current changes, load from storage</para>
  73. /// <para>忽略当前值的变化,从持久化存储中读取</para>
  74. /// </summary>
  75. /// <returns>Async Task</returns>
  76. System.Threading.Tasks.Task<T> RefreshAsync();
  77. /// <summary>
  78. /// <para>Save current changes to storage</para>
  79. /// <para>把当前值的变化写入持久化存储中</para>
  80. /// </summary>
  81. /// <returns>Async Task</returns>
  82. System.Threading.Tasks.Task SaveAsync(T value);
  83. /// <summary>
  84. /// <para>Current value</para>
  85. /// <para>当前值</para>
  86. /// </summary>
  87. T Value { get; }
  88. }
  89. /// <summary>
  90. /// <para>Simple storage interface, for persistence.</para>
  91. /// <para>简单的持久化存储类型接口</para>
  92. /// </summary>
  93. /// <typeparam name="TToken">
  94. /// <para>The Token/token Type needs to be save/load</para>
  95. /// <para>需要存取的凭据类型</para>
  96. /// </typeparam>
  97. /// <typeparam name="TValue">
  98. /// <para>The Value Type needs to be save/load</para>
  99. /// <para>需要存取的类型</para>
  100. /// </typeparam>
  101. public interface IStorageHub<TToken, TValue>
  102. {
  103. System.Threading.Tasks.Task<TValue> LoadAsync(TToken token, bool forceRefresh);
  104. System.Threading.Tasks.Task SaveAsync(TToken token, TValue value);
  105. }
  106. public class StorageHub<TToken, TValue> : IStorageHub<TToken, TValue>
  107. {
  108. public StorageHub(Func<TToken, IStorage<TValue>> storageFactory, Func<Task<TToken[]>> storageTokensSelector = null)
  109. {
  110. _storageFactory = storageFactory;
  111. _storageTokensSelector = storageTokensSelector;
  112. }
  113. IStorage<TValue> GetOrCreatStorage(TToken token)
  114. {
  115. return _dic.GetOrAdd(token, _storageFactory);
  116. }
  117. Func<TToken, IStorage<TValue>> _storageFactory;
  118. Func<Task<TToken[]>> _storageTokensSelector;
  119. public async Task<TToken[]> GetExistsTokens()
  120. {
  121. if (_storageTokensSelector != null)
  122. {
  123. return await _storageTokensSelector();
  124. }
  125. else
  126. {
  127. throw new NotImplementedException("Current storageTokensSelector is not set in constructor. ");
  128. }
  129. }
  130. ConcurrentDictionary<TToken, IStorage<TValue>> _dic = new ConcurrentDictionary<TToken, IStorage<TValue>>();
  131. public async Task<TValue> LoadAsync(TToken token, bool forceRefresh)
  132. {
  133. var storage = GetOrCreatStorage(token);
  134. if (forceRefresh)
  135. {
  136. return await storage.RefreshAsync();
  137. }
  138. else
  139. {
  140. return storage.Value;
  141. }
  142. }
  143. public async Task SaveAsync(TToken token, TValue value)
  144. {
  145. var storage = GetOrCreatStorage(token);
  146. await storage.SaveAsync(value);
  147. }
  148. #region Json
  149. #if NETFX_CORE
  150. public static StorageHub<TToken, TValue> CreateJsonDatacontractFileStorageHub(
  151. Func<TToken, string> fileNameFactory,
  152. StorageFolder folder = null,
  153. Func<Task<TToken[]>> storageTokensSelector = null)
  154. {
  155. var hub = new JsonDataContractStreamStorageHub<TToken, TValue>(
  156. async (tp, tk) =>
  157. {
  158. folder = folder ?? Windows.Storage.ApplicationData.Current.LocalFolder;
  159. switch (tp)
  160. {
  161. case StreamOpenType.Read:
  162. {
  163. var file = await folder.CreateFileAsync(fileNameFactory(tk), CreationCollisionOption.OpenIfExists);
  164. return await file.OpenStreamForReadAsync();
  165. }
  166. case StreamOpenType.Write:
  167. {
  168. var file = await folder.CreateFileAsync(fileNameFactory(tk), CreationCollisionOption.ReplaceExisting);
  169. return await file.OpenStreamForWriteAsync();
  170. }
  171. default:
  172. return null;
  173. }
  174. },
  175. storageTokensSelector
  176. );
  177. return hub;
  178. }
  179. #elif WPF
  180. public static StorageHub<TToken, TValue> CreateJsonDatacontractFileStorageHub(
  181. Func<TToken, string> fileNameFactory,
  182. string folder = null,
  183. Func<Task<TToken[]>> storageTokensSelector = null)
  184. {
  185. var hub = new JsonDataContractStreamStorageHub<TToken, TValue>(
  186. async (tp, tk) =>
  187. {
  188. folder = folder ?? Environment.CurrentDirectory;
  189. var filepath = Path.Combine(folder, fileNameFactory(tk));
  190. switch (tp)
  191. {
  192. case StreamOpenType.Read:
  193. return await TaskExHelper.FromResult(new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read));
  194. case StreamOpenType.Write:
  195. return await TaskExHelper.FromResult(new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite));
  196. default:
  197. return null;
  198. }
  199. },
  200. storageTokensSelector
  201. );
  202. return hub;
  203. }
  204. #elif WINDOWS_PHONE_8|| WINDOWS_PHONE_7
  205. public static StorageHub<TToken, TValue> CreateJsonDatacontractIsolatedStorageHub(
  206. Func<TToken, string> fileNameFactory,
  207. IsolatedStorageFile folder = null,
  208. Func<Task<TToken[]>> storageTokensSelector = null)
  209. {
  210. var hub = new JsonDataContractStreamStorageHub<TToken, TValue>(
  211. async (tp, tk) =>
  212. {
  213. folder = folder ?? IsolatedStorageFile.GetUserStoreForApplication();
  214. var filepath=fileNameFactory(tk);
  215. switch (tp)
  216. {
  217. case StreamOpenType.Read:
  218. return folder.OpenFile(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read);
  219. case StreamOpenType.Write:
  220. return folder.OpenFile(filepath, FileMode.Create, FileAccess.Write, FileShare.None);
  221. default:
  222. return null;
  223. }
  224. },
  225. storageTokensSelector
  226. );
  227. return hub;
  228. }
  229. #elif SILVERLIGHT_5
  230. public static StorageHub<TToken, TValue> CreateJsonDatacontractFileStorageHub(
  231. Func<TToken, string> fileNameFactory,
  232. string folder = null,
  233. Func<Task<TToken[]>> storageTokensSelector = null)
  234. {
  235. var hub = new JsonDataContractStreamStorageHub<TToken, TValue>(
  236. async (tp, tk) =>
  237. {
  238. folder = folder ?? Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);
  239. var filepath = Path.Combine(folder, fileNameFactory(tk));
  240. switch (tp)
  241. {
  242. case StreamOpenType.Read:
  243. return new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read);
  244. case StreamOpenType.Write:
  245. return new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
  246. default:
  247. return null;
  248. }
  249. },
  250. storageTokensSelector
  251. );
  252. return hub;
  253. }
  254. public static StorageHub<TToken, TValue> CreateJsonDatacontractIsolatedStorageHub(
  255. Func<TToken, string> fileNameFactory,
  256. IsolatedStorageFile folder = null,
  257. Func<Task<TToken[]>> storageTokensSelector = null)
  258. {
  259. var hub = new JsonDataContractStreamStorageHub<TToken, TValue>(
  260. async (tp, tk) =>
  261. {
  262. await TaskEx.Yield();
  263. folder = folder ?? IsolatedStorageFile.GetUserStoreForApplication();
  264. var filepath=fileNameFactory(tk);
  265. switch (tp)
  266. {
  267. case StreamOpenType.Read:
  268. return folder.OpenFile(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read);
  269. case StreamOpenType.Write:
  270. return folder.OpenFile(filepath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
  271. default:
  272. return null;
  273. }
  274. },
  275. storageTokensSelector
  276. );
  277. return hub;
  278. }
  279. #endif
  280. #endregion
  281. #region Xml
  282. #if NETFX_CORE
  283. public static StorageHub<TToken, TValue> CreateXmlDatacontractFileStorageHub(
  284. Func<TToken, string> fileNameFactory,
  285. StorageFolder folder = null,
  286. Func<Task<TToken[]>> storageTokensSelector = null)
  287. {
  288. var hub = new XmlDataContractStreamStorageHub<TToken, TValue>(
  289. async (tp, tk) =>
  290. {
  291. folder = folder ?? Windows.Storage.ApplicationData.Current.LocalFolder;
  292. switch (tp)
  293. {
  294. case StreamOpenType.Read:
  295. {
  296. var file = await folder.CreateFileAsync(fileNameFactory(tk), CreationCollisionOption.OpenIfExists);
  297. return await file.OpenStreamForReadAsync();
  298. }
  299. case StreamOpenType.Write:
  300. {
  301. var file = await folder.CreateFileAsync(fileNameFactory(tk), CreationCollisionOption.ReplaceExisting);
  302. return await file.OpenStreamForWriteAsync();
  303. }
  304. default:
  305. return null;
  306. }
  307. },
  308. storageTokensSelector
  309. );
  310. return hub;
  311. }
  312. #elif WPF
  313. public static StorageHub<TToken, TValue> CreateXmlDatacontractFileStorageHub(
  314. Func<TToken, string> fileNameFactory,
  315. string folder = null,
  316. Func<Task<TToken[]>> storageTokensSelector = null)
  317. {
  318. var hub = new XmlDataContractStreamStorageHub<TToken, TValue>(
  319. async (tp, tk) =>
  320. {
  321. folder = folder ?? Environment.CurrentDirectory;
  322. var filepath = Path.Combine(folder, fileNameFactory(tk));
  323. switch (tp)
  324. {
  325. case StreamOpenType.Read:
  326. return await TaskExHelper.FromResult(new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read));
  327. case StreamOpenType.Write:
  328. return await TaskExHelper.FromResult(new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite));
  329. default:
  330. return null;
  331. }
  332. },
  333. storageTokensSelector
  334. );
  335. return hub;
  336. }
  337. #elif WINDOWS_PHONE_8|| WINDOWS_PHONE_7
  338. public static StorageHub<TToken, TValue> CreateXmlDatacontractIsolatedStorageHub(
  339. Func<TToken, string> fileNameFactory,
  340. IsolatedStorageFile folder = null,
  341. Func<Task<TToken[]>> storageTokensSelector = null)
  342. {
  343. var hub = new XmlDataContractStreamStorageHub<TToken, TValue>(
  344. async (tp, tk) =>
  345. {
  346. folder = folder ?? IsolatedStorageFile.GetUserStoreForApplication();
  347. var filepath=fileNameFactory(tk);
  348. switch (tp)
  349. {
  350. case StreamOpenType.Read:
  351. return folder.OpenFile(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read);
  352. case StreamOpenType.Write:
  353. return folder.OpenFile(filepath, FileMode.Create, FileAccess.Write, FileShare.None);
  354. default:
  355. return null;
  356. }
  357. },
  358. storageTokensSelector
  359. );
  360. return hub;
  361. }
  362. #elif SILVERLIGHT_5
  363. public static StorageHub<TToken, TValue> CreateXmlDatacontractFileStorageHub(
  364. Func<TToken, string> fileNameFactory,
  365. string folder = null,
  366. Func<Task<TToken[]>> storageTokensSelector = null)
  367. {
  368. var hub = new XmlDataContractStreamStorageHub<TToken, TValue>(
  369. async (tp, tk) =>
  370. {
  371. folder = folder ?? Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);
  372. var filepath = Path.Combine(folder, fileNameFactory(tk));
  373. switch (tp)
  374. {
  375. case StreamOpenType.Read:
  376. return new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read);
  377. case StreamOpenType.Write:
  378. return new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
  379. default:
  380. return null;
  381. }
  382. },
  383. storageTokensSelector
  384. );
  385. return hub;
  386. }
  387. public static StorageHub<TToken, TValue> CreateXmlDatacontractIsolatedStorageHub(
  388. Func<TToken, string> fileNameFactory,
  389. IsolatedStorageFile folder = null,
  390. Func<Task<TToken[]>> storageTokensSelector = null)
  391. {
  392. var hub = new XmlDataContractStreamStorageHub<TToken, TValue>(
  393. async (tp, tk) =>
  394. {
  395. await TaskEx.Yield();
  396. folder = folder ?? IsolatedStorageFile.GetUserStoreForApplication();
  397. var filepath=fileNameFactory(tk);
  398. switch (tp)
  399. {
  400. case StreamOpenType.Read:
  401. return folder.OpenFile(filepath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read);
  402. case StreamOpenType.Write:
  403. return folder.OpenFile(filepath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
  404. default:
  405. return null;
  406. }
  407. },
  408. storageTokensSelector
  409. );
  410. return hub;
  411. }
  412. #endif
  413. #endregion
  414. }
  415. public enum StreamOpenType
  416. {
  417. Read,
  418. Write
  419. }
  420. public class JsonDataContractStreamStorageHub<TToken, TValue> : StorageHub<TToken, TValue>
  421. {
  422. public JsonDataContractStreamStorageHub(Func<StreamOpenType, TToken, Task<Stream>> streamOpener, Func<Task<TToken[]>> storageTokensSelector = null)
  423. : base
  424. (tk => new JsonDataContractStreamStorage<TValue>(async tp => await streamOpener(tp, tk)), storageTokensSelector)
  425. {
  426. }
  427. }
  428. public class JsonDataContractStreamStorage<TValue> : IStorage<TValue>
  429. {
  430. #if NET45
  431. ConcurrentExclusiveSchedulerPair _sch = new ConcurrentExclusiveSchedulerPair();
  432. #else
  433. TaskScheduler _sch = new LimitedConcurrencyLevelTaskScheduler(1);
  434. #endif
  435. public JsonDataContractStreamStorage(Func<StreamOpenType, Task<Stream>> streamOpener, params Type[] knownTypes)
  436. {
  437. _streamOpener = streamOpener;
  438. _knownTypes = knownTypes;
  439. }
  440. Func<StreamOpenType, Task<Stream>> _streamOpener;
  441. Type[] _knownTypes;
  442. public Type[] KnownTypes
  443. {
  444. get { return _knownTypes; }
  445. set { _knownTypes = value; }
  446. }
  447. public async Task<TValue> RefreshAsync()
  448. {
  449. var kts = _knownTypes;
  450. return await await
  451. Task.Factory.StartNew(
  452. new Func<Task<TValue>>(
  453. async () =>
  454. {
  455. var ms = new MemoryStream();
  456. var ser = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(TValue), kts);
  457. using (var strm = await _streamOpener(StreamOpenType.Read))
  458. {
  459. await strm.CopyToAsync(ms);
  460. }
  461. if (ms.Length == 0)
  462. {
  463. return default(TValue);
  464. }
  465. ms.Position = 0;
  466. var obj = (TValue)ser.ReadObject(ms);
  467. Value = obj;
  468. return obj;
  469. }),
  470. CancellationToken.None,
  471. TaskCreationOptions.None ,
  472. #if NET45
  473. _sch.ConcurrentScheduler
  474. #else
  475. _sch
  476. #endif
  477. );
  478. }
  479. public async Task SaveAsync(TValue value)
  480. {
  481. var kts = _knownTypes;
  482. await await Task.Factory.StartNew(
  483. async () =>
  484. {
  485. var ms = new MemoryStream();
  486. var ser = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(TValue), kts);
  487. Value = value;
  488. ser.WriteObject(ms, value);
  489. ms.Position = 0;
  490. using (var strm = await _streamOpener(StreamOpenType.Write))
  491. {
  492. await ms.CopyToAsync(strm);
  493. await strm.FlushAsync();
  494. }
  495. },
  496. CancellationToken.None,
  497. TaskCreationOptions.None,
  498. #if NET45
  499. _sch.ExclusiveScheduler
  500. #else
  501. _sch
  502. #endif
  503. );
  504. }
  505. public TValue Value
  506. {
  507. get;
  508. private set;
  509. }
  510. }
  511. public class XmlDataContractStreamStorageHub<TToken, TValue> : StorageHub<TToken, TValue>
  512. {
  513. public XmlDataContractStreamStorageHub(Func<StreamOpenType, TToken, Task<Stream>> streamOpener, Func<Task<TToken[]>> storageTokensSelector = null)
  514. : base
  515. (tk => new XmlDataContractStreamStorage<TValue>(async tp => await streamOpener(tp, tk)), storageTokensSelector)
  516. {
  517. }
  518. }
  519. public class XmlDataContractStreamStorage<TValue> : IStorage<TValue>
  520. {
  521. #if NET45
  522. ConcurrentExclusiveSchedulerPair _sch = new ConcurrentExclusiveSchedulerPair();
  523. #else
  524. TaskScheduler _sch = new LimitedConcurrencyLevelTaskScheduler(1);
  525. #endif
  526. public XmlDataContractStreamStorage(Func<StreamOpenType, Task<Stream>> streamOpener, params Type[] knownTypes)
  527. {
  528. _streamOpener = streamOpener;
  529. _knownTypes = knownTypes;
  530. }
  531. Func<StreamOpenType, Task<Stream>> _streamOpener;
  532. Type[] _knownTypes;
  533. public Type[] KnownTypes
  534. {
  535. get { return _knownTypes; }
  536. set { _knownTypes = value; }
  537. }
  538. public async Task<TValue> RefreshAsync()
  539. {
  540. var kts = _knownTypes;
  541. return await await
  542. Task.Factory.StartNew(
  543. async () =>
  544. {
  545. var ms = new MemoryStream();
  546. var ser = new System.Runtime.Serialization.DataContractSerializer(typeof(TValue), kts);
  547. using (var strm = await _streamOpener(StreamOpenType.Read))
  548. {
  549. await strm.CopyToAsync(ms);
  550. }
  551. if (ms.Length == 0)
  552. {
  553. return default(TValue);
  554. }
  555. ms.Position = 0;
  556. var obj = (TValue)ser.ReadObject(ms);
  557. Value = obj;
  558. return obj;
  559. },
  560. CancellationToken.None,
  561. TaskCreationOptions.AttachedToParent,
  562. #if NET45
  563. _sch.ConcurrentScheduler
  564. #else
  565. _sch
  566. #endif
  567. );
  568. }
  569. public async Task SaveAsync(TValue value)
  570. {
  571. var kts = _knownTypes;
  572. await await Task.Factory.StartNew(
  573. async () =>
  574. {
  575. var ms = new MemoryStream();
  576. var ser = new System.Runtime.Serialization.DataContractSerializer(typeof(TValue), kts);
  577. Value = value;
  578. ser.WriteObject(ms, value);
  579. ms.Position = 0;
  580. using (var strm = await _streamOpener(StreamOpenType.Write))
  581. {
  582. await ms.CopyToAsync(strm);
  583. await strm.FlushAsync();
  584. }
  585. },
  586. CancellationToken.None,
  587. TaskCreationOptions.None,
  588. #if NET45
  589. _sch.ExclusiveScheduler
  590. #else
  591. _sch
  592. #endif
  593. );
  594. }
  595. public TValue Value
  596. {
  597. get;
  598. private set;
  599. }
  600. }
  601. }
  602. }