PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/RapidRepository/RapidRepository/Context/RapidContext.cs

#
C# | 534 lines | 371 code | 104 blank | 59 comment | 46 complexity | 8f550d823380b26f11ed52a9856975bf MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using RapidRepository.Cache;
  6. using RapidRepository.Context;
  7. using RapidRepository.View;
  8. namespace RapidRepository
  9. {
  10. /// <summary>
  11. /// The RapidContext tracks usage of the repository and manages access to the document storage.
  12. /// </summary>
  13. public class RapidContext
  14. {
  15. #region Declarations
  16. static object syncRoot = new object();
  17. static RapidContext context = null;
  18. IFileManager fileManager;
  19. ISerialiser serialiser;
  20. List<OperationRequest> operationRequests = new List<OperationRequest>();
  21. internal static List<IRapidViewDataInternal> rapidViews = new List<IRapidViewDataInternal>();
  22. List<EntitiesLoaded> entitiesLoaded = new List<EntitiesLoaded>();
  23. private class RapidViewKey
  24. {
  25. public IRapidViewDataInternal RapidViewData { get; set; }
  26. public ViewKey ViewKey { get; set; }
  27. }
  28. #endregion
  29. #region Constructors
  30. /// <summary>
  31. /// Initializes a new instance of the <see cref="RapidContext"/> class.
  32. /// </summary>
  33. internal RapidContext() : this(new FileManager(), new Serialiser()) { }
  34. /// <summary>
  35. /// Initializes a new instance of the <see cref="RapidContext"/> class.
  36. /// </summary>
  37. /// <param name="fileManager">The file manager.</param>
  38. /// <param name="serialiser">The serialiser.</param>
  39. internal RapidContext(IFileManager fileManager, ISerialiser serialiser)
  40. {
  41. Contract.Requires("fileManager", fileManager != null);
  42. Contract.Requires("serialiser", serialiser != null);
  43. this.fileManager = fileManager;
  44. this.serialiser = serialiser;
  45. }
  46. #endregion
  47. #region CurrentContext
  48. /// <summary>
  49. /// Gets the current context.
  50. /// </summary>
  51. /// <value>The current context.</value>
  52. public static RapidContext CurrentContext
  53. {
  54. get
  55. {
  56. if (context == null)
  57. {
  58. lock (syncRoot)
  59. {
  60. if (context == null)
  61. {
  62. context = new RapidContext();
  63. }
  64. }
  65. }
  66. return context;
  67. }
  68. }
  69. #endregion
  70. #region SaveChanges
  71. /// <summary>
  72. /// Saves the changes.
  73. /// </summary>
  74. public void SaveChanges()
  75. {
  76. List<ViewKey> rapidViews = new List<ViewKey>();
  77. foreach (OperationRequest request in this.operationRequests)
  78. {
  79. switch (request.OperationRequestType)
  80. {
  81. case OperationRequestType.Add:
  82. if (((IRapidEntity)request.Entity).Id == Guid.Empty)
  83. {
  84. ((IRapidEntity)request.Entity).Id = Guid.NewGuid();
  85. }
  86. var addString = this.serialiser.Serialise(request.EntityType, request.Entity);
  87. this.fileManager.Save(request.EntityType, ((IRapidEntity)request.Entity).Id, addString);
  88. AddToCache(request.EntityType, (IRapidEntity)request.Entity);
  89. AddToRapidViewsCollectionForSave(rapidViews, AddToView(request));
  90. break;
  91. case OperationRequestType.Update:
  92. var updateString = this.serialiser.Serialise(request.EntityType, request.Entity);
  93. this.fileManager.Save(request.EntityType, request.EntityId, updateString);
  94. EntityCache.Update(request.EntityType, request.EntityId, request.Entity);
  95. AddToRapidViewsCollectionForSave(rapidViews, UpdateToView(request));
  96. break;
  97. case OperationRequestType.Delete:
  98. this.fileManager.Delete(request.EntityType, request.EntityId);
  99. EntityCache.Remove(request.EntityType, request.EntityId);
  100. AddToRapidViewsCollectionForSave(rapidViews, DeleteToView(request));
  101. break;
  102. }
  103. }
  104. SaveViews(rapidViews);
  105. DeleteAllOperationRequests();
  106. }
  107. #endregion
  108. #region GetOperationRequests
  109. /// <summary>
  110. /// Gets the operation requests.
  111. /// </summary>
  112. /// <value>The operation requests.</value>
  113. public List<OperationRequest> OperationRequests
  114. {
  115. get
  116. {
  117. lock (syncRoot)
  118. {
  119. return this.operationRequests;
  120. }
  121. }
  122. }
  123. #endregion
  124. #region AddOperationRequest
  125. /// <summary>
  126. /// Adds the operation request.
  127. /// </summary>
  128. /// <param name="operationRequest">The operation request.</param>
  129. internal void AddOperationRequest(OperationRequest operationRequest)
  130. {
  131. Contract.Requires("operationRequest", operationRequest != null);
  132. this.operationRequests.Add(operationRequest);
  133. }
  134. #endregion
  135. #region DeleteOperationRequest
  136. /// <summary>
  137. /// Deletes the operation request.
  138. /// </summary>
  139. /// <param name="operationRequest">The operation request.</param>
  140. public void DeleteOperationRequest(OperationRequest operationRequest)
  141. {
  142. Contract.Requires("operationRequest", operationRequest != null);
  143. this.operationRequests.Remove(operationRequest);
  144. }
  145. #endregion
  146. #region DeleteAllOperationRequests
  147. /// <summary>
  148. /// Deletes all operation requests.
  149. /// </summary>
  150. public void DeleteAllOperationRequests()
  151. {
  152. this.operationRequests.Clear();
  153. }
  154. #endregion
  155. #region Exists
  156. /// <summary>
  157. /// Returns true if the entity exists.
  158. /// </summary>
  159. /// <typeparam name="TEntity">The type of the entity.</typeparam>
  160. /// <param name="id">The id.</param>
  161. internal bool Exists<TEntity>(Guid id)
  162. {
  163. Contract.Requires("id", id != Guid.Empty);
  164. var cachedEntity = EntityCache.GetById<TEntity>(id);
  165. if (cachedEntity != null)
  166. {
  167. return true;
  168. }
  169. return this.fileManager.Exists<TEntity>(id);
  170. }
  171. #endregion
  172. #region GetById
  173. /// <summary>
  174. /// Gets the entity by id.
  175. /// </summary>
  176. /// <typeparam name="TEntity">The type of the entity.</typeparam>
  177. /// <param name="id">The id.</param>
  178. internal TEntity GetById<TEntity>(Guid id)
  179. {
  180. Contract.Requires("id", id != Guid.Empty);
  181. var cachedEntity = EntityCache.GetById<TEntity>(id);
  182. if (cachedEntity != null)
  183. {
  184. return cachedEntity;
  185. }
  186. string fileContent = this.fileManager.GetFile<TEntity>(id);
  187. var entity = (IRapidEntity)this.serialiser.Deserialise<TEntity>(fileContent);
  188. AddToCache(typeof(TEntity), entity);
  189. return (TEntity)entity;
  190. }
  191. #endregion
  192. #region GetAll
  193. /// <summary>
  194. /// Gets a list of all entities of the specified type.
  195. /// </summary>
  196. /// <typeparam name="TEntity">The type of the entity.</typeparam>
  197. internal IList<TEntity> GetAll<TEntity>()
  198. {
  199. lock (syncRoot)
  200. {
  201. if (entitiesLoaded.Any(x => x.EntityType == typeof(TEntity)))
  202. {
  203. var cachedEntities = EntityCache.GetAll<TEntity>();
  204. if (cachedEntities != null && cachedEntities.Count > 0)
  205. {
  206. return cachedEntities;
  207. }
  208. }
  209. }
  210. string[] entityList = this.fileManager.LoadFiles<TEntity>();
  211. IList<TEntity> entities = new List<TEntity>();
  212. foreach (var entityContent in entityList)
  213. {
  214. var entity = this.serialiser.Deserialise<TEntity>(entityContent);
  215. entities.Add(entity);
  216. AddToCache(typeof(TEntity), (IRapidEntity)entity);
  217. }
  218. SetEntityLoaded<TEntity>();
  219. return entities;
  220. }
  221. internal void SetEntityLoaded<TEntity>()
  222. {
  223. lock (syncRoot)
  224. {
  225. entitiesLoaded.Add(new EntitiesLoaded { EntityType = typeof(TEntity) });
  226. }
  227. }
  228. #endregion
  229. #region AddView
  230. /// <summary>
  231. /// Adds a rapid view.
  232. /// </summary>
  233. /// <param name="view">The view.</param>
  234. internal static void AddRapidView(IRapidViewDataInternal view)
  235. {
  236. lock (syncRoot)
  237. {
  238. if (!rapidViews.Any(x => x.ViewType == view.ViewType))
  239. {
  240. rapidViews.Add(view);
  241. }
  242. }
  243. }
  244. #endregion
  245. #region SaveView
  246. private void SaveViews(List<ViewKey> viewsToSave)
  247. {
  248. foreach (ViewKey viewToSave in (List<ViewKey>)viewsToSave)
  249. {
  250. IRapidViewDataInternal rapidViewData = LoadRapidViewData(viewToSave);
  251. RapidViewKey rapidViewKey = new RapidViewKey
  252. {
  253. RapidViewData = rapidViewData,
  254. ViewKey = viewToSave
  255. };
  256. if (!rapidViewData.IsLoaded)
  257. {
  258. LoadView(rapidViewData.EntityType, rapidViewData.ViewType);
  259. }
  260. SaveView(rapidViewKey);
  261. }
  262. }
  263. private void SaveView(object rapidViewKey)
  264. {
  265. RapidViewKey rapidViewKeyData = (RapidViewKey)rapidViewKey;
  266. System.Collections.IList views = (System.Collections.IList)rapidViewKeyData.RapidViewData.LoadView();
  267. StringBuilder serialisedView = new StringBuilder();
  268. foreach (var view in views)
  269. {
  270. serialisedView.AppendLine(this.serialiser.Serialise(rapidViewKeyData.ViewKey.ViewType, view));
  271. }
  272. this.fileManager.SaveViewFile(rapidViewKeyData.ViewKey.ViewType, serialisedView.ToString());
  273. }
  274. #endregion
  275. #region LoadView
  276. /// <summary>
  277. /// Loads the view.
  278. /// </summary>
  279. /// <typeparam name="TView">The type of the view.</typeparam>
  280. internal List<TView> LoadView<TView>(Type entityType, Type viewType)
  281. {
  282. IRapidViewDataInternal rapidView = LoadRapidViewData(new ViewKey { EntityType = entityType, ViewType = viewType });
  283. if (!rapidView.IsLoaded)
  284. {
  285. var viewFile = this.fileManager.GetViewFile(viewType);
  286. if (viewFile == null)
  287. {
  288. return new List<TView>();
  289. }
  290. List<string> splitView = new List<string>(viewFile.Split(new string[] { Environment.NewLine }, new StringSplitOptions()));
  291. List<object> rapidViews = new List<object>();
  292. foreach (var item in splitView)
  293. {
  294. if (!string.IsNullOrEmpty(item))
  295. {
  296. rapidViews.Add((IRapidView)this.serialiser.Deserialise(viewType, item));
  297. }
  298. }
  299. rapidView.InsertView(rapidViews);
  300. rapidView.IsLoaded = true;
  301. }
  302. var views= rapidView.LoadView();
  303. List<TView> viewsToReturn = new List<TView>();
  304. foreach (var view in views)
  305. {
  306. viewsToReturn.Add((TView)view);
  307. }
  308. return viewsToReturn;
  309. }
  310. /// <summary>
  311. /// Loads the view.
  312. /// </summary>
  313. /// <typeparam name="TView">The type of the view.</typeparam>
  314. internal void LoadView(Type entityType, Type viewType)
  315. {
  316. IRapidViewDataInternal rapidView = LoadRapidViewData(new ViewKey { EntityType = entityType, ViewType = viewType });
  317. if (!rapidView.IsLoaded)
  318. {
  319. var viewFile = this.fileManager.GetViewFile(viewType);
  320. if (viewFile == null)
  321. {
  322. return;
  323. }
  324. List<string> splitView = new List<string>(viewFile.Split(new string[] { Environment.NewLine }, new StringSplitOptions()));
  325. List<object> rapidViews = new List<object>();
  326. foreach (var item in splitView)
  327. {
  328. if (!string.IsNullOrEmpty(item))
  329. {
  330. rapidViews.Add((IRapidView)this.serialiser.Deserialise(viewType, item));
  331. }
  332. }
  333. rapidView.InsertView(rapidViews);
  334. rapidView.IsLoaded = true;
  335. }
  336. }
  337. #endregion
  338. #region SynchroniseView
  339. internal static void SynchroniseView<TEntity, TView>()
  340. where TEntity : IRapidEntity
  341. where TView : IRapidView
  342. {
  343. IList<TEntity> entities = CurrentContext.GetAll<TEntity>();
  344. ViewKey viewKey = new ViewKey { EntityType = typeof(TEntity), ViewType = typeof(TView)};
  345. IRapidViewDataInternal rapidView = LoadRapidViewData(viewKey);
  346. RapidViewKey rapidViewKey = new RapidViewKey
  347. {
  348. ViewKey = viewKey,
  349. RapidViewData = rapidView
  350. };
  351. rapidView.InsertView(new List<object>());
  352. foreach (var entity in entities)
  353. {
  354. rapidView.Add(entity);
  355. }
  356. CurrentContext.SaveView(rapidViewKey);
  357. }
  358. #endregion
  359. #region Private Methods
  360. private static void AddToCache(Type entityType, IRapidEntity entity)
  361. {
  362. EntityCache.Add(entityType, ((IRapidEntity)entity).Id, entity);
  363. }
  364. private static IRapidViewDataInternal LoadRapidViewData(ViewKey viewKey)
  365. {
  366. var rapidView = rapidViews.Where(x => x.ViewType == viewKey.ViewType).FirstOrDefault();
  367. if (rapidView == null)
  368. {
  369. throw new InvalidOperationException(string.Format("The view type {0} has not been registered", viewKey.ViewType.FullName));
  370. }
  371. return rapidView;
  372. }
  373. private List<ViewKey> AddToView(OperationRequest request)
  374. {
  375. List<ViewKey> viewTypesForSaving = new List<ViewKey>();
  376. List<IRapidViewDataInternal> views = rapidViews.Where(x => x.EntityType == request.EntityType).ToList();
  377. foreach (var view in views)
  378. {
  379. if (!view.IsLoaded)
  380. {
  381. LoadView(view.EntityType, view.ViewType);
  382. }
  383. view.Add(request.Entity);
  384. viewTypesForSaving.Add(new ViewKey { EntityType = request.EntityType, ViewType = view.ViewType });
  385. }
  386. return viewTypesForSaving;
  387. }
  388. private List<ViewKey> UpdateToView(OperationRequest request)
  389. {
  390. List<ViewKey> viewTypesForSaving = new List<ViewKey>();
  391. List<IRapidViewDataInternal> views = rapidViews.Where(x => x.EntityType == request.EntityType).ToList();
  392. foreach (var view in views)
  393. {
  394. if (!view.IsLoaded)
  395. {
  396. LoadView(view.EntityType, view.ViewType);
  397. }
  398. view.Update(request.Entity);
  399. viewTypesForSaving.Add(new ViewKey { EntityType = request.EntityType, ViewType = view.ViewType });
  400. }
  401. return viewTypesForSaving;
  402. }
  403. private List<ViewKey> DeleteToView(OperationRequest request)
  404. {
  405. List<ViewKey> viewTypesForSaving = new List<ViewKey>();
  406. List<IRapidViewDataInternal> views = rapidViews.Where(x => x.EntityType == request.EntityType).ToList();
  407. foreach (var view in views)
  408. {
  409. if (!view.IsLoaded)
  410. {
  411. LoadView(view.EntityType, view.ViewType);
  412. }
  413. view.Delete(request.EntityId);
  414. viewTypesForSaving.Add(new ViewKey { EntityType = request.EntityType, ViewType = view.ViewType });
  415. }
  416. return viewTypesForSaving;
  417. }
  418. private static void AddToRapidViewsCollectionForSave(List<ViewKey> rapidViews, List<ViewKey> viewsToSave)
  419. {
  420. foreach (var viewKey in viewsToSave)
  421. {
  422. if (!rapidViews.Any(x => x.EntityType == viewKey.EntityType && x.ViewType == viewKey.ViewType))
  423. {
  424. rapidViews.Add(viewKey);
  425. }
  426. }
  427. }
  428. #endregion
  429. }
  430. }