PageRenderTime 59ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/SFWK.DAO/SFWKEFRepository.cs

#
C# | 429 lines | 258 code | 51 blank | 120 comment | 59 complexity | b20dfe2ae1086cc79288b7cd22eddef3 MD5 | raw file
  1. //------------------------------------------------------------------------------
  2. //SFWK Framework
  3. //Copyright (C) 2011 SQLI
  4. //This program is free software: you can redistribute it and/or modify
  5. //it under the terms of the GNU General Public License as published by
  6. //the Free Software Foundation, either version 3 of the License, or
  7. //(at your option) any later version.
  8. //This program is distributed in the hope that it will be useful,
  9. //but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. //GNU General Public License for more details.
  12. //You should have received a copy of the GNU General Public License
  13. //along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. //------------------------------------------------------------------------------
  15. using System;
  16. using System.Linq;
  17. using System.Reflection;
  18. using System.Linq.Expressions;
  19. using System.Data.Linq;
  20. using System.Data.Linq.Mapping;
  21. using SFWK.Core;
  22. using System.Diagnostics;
  23. using System.Collections.Generic;
  24. using System.Data.Objects;
  25. using System.Data.Objects.DataClasses;
  26. using System.Data;
  27. using SFWK.Core.DTO;
  28. using SFWK.Core.ExpressionBuilder;
  29. using System.Linq.Dynamic;
  30. using SFWK.Core.Linq;
  31. namespace SFWK.DAO
  32. {
  33. /// <summary>
  34. /// Unic Access point to DAO objects
  35. /// Provides generic Submit, Attach, Detach, CRUD and Search methods
  36. /// Provides DataContext Access
  37. /// SubmitChanges method must be called to update data in DB!!!
  38. /// </summary>
  39. /// <typeparam name="TEntity"></typeparam>
  40. public class SFWKEFRepository<TDataContext>
  41. where TDataContext : ObjectContext, new()
  42. {
  43. #region Accessors
  44. /// <summary>
  45. /// Accessor to data context
  46. /// </summary>
  47. public TDataContext DataContext
  48. {
  49. get
  50. {
  51. return DataContextFactory.GetScopedDataContext<TDataContext>();// EntityFrameworkHelper<TDataContext>.Context;
  52. }
  53. }
  54. #endregion
  55. #region Save
  56. /// <summary>
  57. /// Saves an entity (Insert or Update)
  58. /// </summary>
  59. /// <typeparam name="TEntity"></typeparam>
  60. /// <param name="os"></param>
  61. /// <param name="entity"></param>
  62. /// <returns></returns>
  63. public TEntity Save<TEntity>(ObjectSet<TEntity> os, TEntity entity) where TEntity : class, IObjectWithChangeTracker, new()
  64. {
  65. TEntity workingEntity = SFWK.Core.EntityHelper.ValidateObject<TEntity>(entity); // For composition stuff, derived object can be passed -> transform derived to base object
  66. // LogHelper.Log(this, "SaveEntity", SFWK.Core.SerializeHelper.SerializeToXml(entity), ELogSeverity.Debug );
  67. // LogTracker("", entity);
  68. // LogObjectStateManager("Before apply Change", os.Context.ObjectStateManager);
  69. os.ApplyChanges<TEntity>(workingEntity);
  70. // LogObjectStateManager("After apply Change", os.Context.ObjectStateManager);
  71. os.Context.SaveChanges();
  72. // LogObjectStateManager("After SAVE CHANGES", os.Context.ObjectStateManager);
  73. if (!entity.GetType().Equals(typeof(TEntity)))
  74. SFWK.Core.EntityHelper.CopyObjectValues<TEntity>(workingEntity, entity); // For composition stuff, derived object can be passed -> transform base to derived object
  75. //entity.AcceptChanges();
  76. // IMPORTANT we must accept change on all entities commited to the db before returning result.
  77. AcceptChangesOnAllEntities<TEntity>(os);
  78. return entity;
  79. }
  80. #endregion
  81. #region Delete
  82. /// <summary>
  83. /// Delete an Entity
  84. /// </summary>
  85. /// <typeparam name="TEntity"></typeparam>
  86. /// <param name="os"></param>
  87. /// <param name="entity"></param>
  88. public void Delete<TEntity>(ObjectSet<TEntity> os, TEntity entity) where TEntity : class, IObjectWithChangeTracker, new()
  89. {
  90. entity = SFWK.Core.EntityHelper.ValidateObject<TEntity>(entity);// For composition stuff, derived object can be passed -> transform derived to base object
  91. os.ApplyChanges<TEntity>(entity);
  92. os.Context.DeleteObject(entity);
  93. os.Context.SaveChanges();
  94. entity.AcceptChanges();
  95. // AcceptChangesOnAllEntities<TEntity>(os);
  96. }
  97. #endregion
  98. private void LogObjectStateManager(String title, ObjectStateManager osm)
  99. {
  100. LogHelper.Log(osm, "DUMP STATE", title, ELogSeverity.Error);
  101. foreach(var x in osm.GetObjectStateEntries( EntityState.Added )) {
  102. if (x.Entity != null) LogHelper.Log(osm, "Added", SerializeHelper.SerializeXml( x.EntityKey) , ELogSeverity.Error);
  103. else LogHelper.Log(osm, "Added", x.EntitySet + "-" + x.EntityKey , ELogSeverity.Error);
  104. }
  105. foreach(var x in osm.GetObjectStateEntries( EntityState.Deleted )) {
  106. if (x.Entity != null) LogHelper.Log(osm, "Deleted", SerializeHelper.SerializeXml(x.EntityKey), ELogSeverity.Error);
  107. else LogHelper.Log(osm, "Deleted", x.EntitySet + "-" + x.EntityKey, ELogSeverity.Error);
  108. }
  109. foreach(var x in osm.GetObjectStateEntries( EntityState.Modified )) {
  110. if (x.Entity != null) LogHelper.Log(osm, "Modified", SerializeHelper.SerializeXml(x.EntityKey), ELogSeverity.Error);
  111. else LogHelper.Log(osm, "Modified", x.EntitySet + "-" + x.EntityKey, ELogSeverity.Error);
  112. }
  113. foreach (var x in osm.GetObjectStateEntries(EntityState.Unchanged))
  114. {
  115. if (x.Entity != null) LogHelper.Log(osm, "Unchanged", SerializeHelper.SerializeXml(x.EntityKey), ELogSeverity.Error);
  116. else LogHelper.Log(osm, "Unchanged", x.EntitySet + "-" + x.EntityKey, ELogSeverity.Error);
  117. }
  118. }
  119. private void LogTracker(string tab, IObjectWithChangeTracker ioc)
  120. {
  121. LogHelper.Log(ioc, "Traker",tab+ ioc.ChangeTracker.State.ToString()+ ":" + ioc.ToString(), ELogSeverity.Error);
  122. foreach (var x in ioc.ChangeTracker.ObjectsAddedToCollectionProperties)
  123. {
  124. foreach( var y in x.Value) {
  125. IObjectWithChangeTracker z = y as IObjectWithChangeTracker;
  126. if (z != null) LogTracker(tab + " ADDED " + x.Key + " ", z);
  127. }
  128. }
  129. foreach (var x in ioc.ChangeTracker.ObjectsRemovedFromCollectionProperties)
  130. {
  131. foreach (var y in x.Value)
  132. {
  133. IObjectWithChangeTracker z = y as IObjectWithChangeTracker;
  134. if (z != null) LogTracker(tab + " DELET " + x.Key + " ", z);
  135. }
  136. }
  137. foreach (var x in ioc.ChangeTracker.OriginalValues)
  138. {
  139. LogHelper.Log(ioc, "Traker", tab + x.Key + "=" + x.Value.ToString(), ELogSeverity.Error);
  140. }
  141. }
  142. private static void AcceptChangesOnAllEntities<TEntity>(ObjectSet<TEntity> os) where TEntity : class, IObjectWithChangeTracker, new()
  143. {
  144. foreach (var entry in os.Context.ObjectStateManager.GetObjectStateEntries(EntityState.Unchanged))
  145. {
  146. IObjectWithChangeTracker ioc = entry.Entity as IObjectWithChangeTracker;
  147. if (ioc != null) ioc.AcceptChanges();
  148. }
  149. }
  150. #region Query builder methods
  151. /// <summary>
  152. /// Adds Where Clause to ObjectQuery from string filter
  153. /// </summary>
  154. /// <typeparam name="T"></typeparam>
  155. /// <param name="objectQuery"></param>
  156. /// <param name="filter"></param>
  157. /// <returns></returns>
  158. //protected IQueryable<T> setWhereClause<T>(IQueryable<T> queryable, string filter) where T : class
  159. //{
  160. // return queryable.Where(!string.IsNullOrEmpty(filter) ? ExpressionSerializerHelper<T>.Deserialize(CompressionHelper.Decompress(filter)) : x => true);
  161. //}
  162. /// <summary>
  163. /// Adds Order by Clause to ObjectQuery from string filter
  164. /// </summary>
  165. /// <typeparam name="T"></typeparam>
  166. /// <param name="queryable"></param>
  167. /// <param name="orderBy"></param>
  168. /// <param name="defaultOrderBy"></param>
  169. /// <returns></returns>
  170. //protected IQueryable<T> setOrderByClause<T>(IQueryable<T> queryable, string orderBy, Expression<Func<T, object>> defaultOrderBy) where T : class
  171. //{
  172. // return queryable.OrderBy(!string.IsNullOrEmpty(orderBy) ? ExpressionSerializerHelper<T>.DeserializeOrderBy(orderBy) : defaultOrderBy);
  173. //}
  174. /// <summary>
  175. /// Adds Include graph to query
  176. /// </summary>
  177. /// <typeparam name="T"></typeparam>
  178. /// <param name="objectQuery"></param>
  179. /// <param name="graphs"></param>
  180. /// <returns></returns>
  181. protected ObjectQuery<T> setInclude<T>(ObjectQuery<T> objectQuery, ControlledGraph<T> graph)
  182. {
  183. if (graph.Graph != null && graph.Graph.Count > 0)
  184. foreach (string val in graph.Graph)
  185. if (val != null)
  186. objectQuery = objectQuery.Include(val);
  187. return objectQuery;
  188. }
  189. /// <summary>
  190. /// Build query with include graph, where clause, and orderby clause
  191. /// </summary>
  192. /// <typeparam name="T"></typeparam>
  193. /// <param name="objectQuery"></param>
  194. /// <param name="graphs"></param>
  195. /// <param name="filter"></param>
  196. /// <param name="orderBy"></param>
  197. /// <param name="defaultOrderBy"></param>
  198. /// <returns></returns>
  199. protected IQueryable<T> BuildQuery<T>(ControlledGraph<T> graph, string filter, string orderBy)
  200. where T : class
  201. {
  202. IQueryable<T> res = setInclude<T>(this.DataContext.CreateObjectSet<T>(typeof(T).Name), graph).Where(!string.IsNullOrEmpty(filter) ? ExpressionSerializerHelper<T>.Deserialize(CompressionHelper.Decompress(filter)) : x => true);
  203. if (!string.IsNullOrEmpty(orderBy))
  204. {
  205. Expression<Func<IQueryable<T>, IOrderedQueryable>> ex = ExpressionSerializerHelper<T>.DeserializeOrderBy(orderBy);
  206. if (ex != null)
  207. res = ex.Compile().Invoke(res) as IQueryable<T>;
  208. }
  209. return res;
  210. }
  211. protected IQueryable<T> BuildQuery<T>(ControlledGraph<T> graph, string filter, Expression<Func<IQueryable<T>, IOrderedQueryable>> orderBy)
  212. where T : class
  213. {
  214. IQueryable<T> res = setInclude<T>(this.DataContext.CreateObjectSet<T>(typeof(T).Name), graph).Where(!string.IsNullOrEmpty(filter) ? ExpressionSerializerHelper<T>.Deserialize(CompressionHelper.Decompress(filter)) : x => true);
  215. if (orderBy != null)
  216. {
  217. res = orderBy.Compile().Invoke(res) as IQueryable<T>;
  218. }
  219. return res;
  220. }
  221. protected IQueryable<T> BuildQuery<T>(string filter, string orderBy)
  222. where T : class
  223. {
  224. return BuildQuery<T>(null, filter, orderBy);
  225. }
  226. protected IQueryable<T> BuildQuery<T>(string filter, Expression<Func<IQueryable<T>, IOrderedQueryable>> orderBy)
  227. where T : class
  228. {
  229. return BuildQuery<T>(null, filter, orderBy);
  230. }
  231. #endregion
  232. #region SearchGraph
  233. /// <summary>
  234. /// Search for a list of Entities, and retrieve them with the requested linked object's graph list
  235. /// </summary>
  236. /// <typeparam name="T">Type of searched entities</typeparam>
  237. /// <param name="filter">serialized Where clause lambda expression</param>
  238. /// <param name="from">Paging: start index</param>
  239. /// <param name="range">Paging: number of record to get</param>
  240. /// <param name="orderBy">serialized Sort lambda expresion</param>
  241. /// <param name="graphs">List of linked object graphs to be retrieved</param>
  242. /// <param name="defaultOrderBy">If order by null, set a default order by</param>
  243. /// <returns>List of found records with the number of record available</returns>
  244. public SearchResultList<T> SearchGraph<T>(string filter, int? from, int? range, string orderBy, ControlledGraph<T> graph)
  245. where T : class
  246. {
  247. IQueryable<T> result = BuildQuery<T>(graph, filter, orderBy);
  248. int count = result.Count();
  249. if (from != null) result = result.Skip((int)from);
  250. if (range != null) result = result.Take((int)range);
  251. return new SearchResultList<T>(result.ToList(), count);
  252. }
  253. public SearchResultList<T> SearchGraph<T>(string filter, int? from, int? range, Expression<Func<IQueryable<T>, IOrderedQueryable>> orderBy, ControlledGraph<T> graph)
  254. where T : class
  255. {
  256. IQueryable<T> result = BuildQuery<T>(graph, filter, orderBy);
  257. int count = result.Count();
  258. if (from != null) result = result.Skip((int)from);
  259. if (range != null) result = result.Take((int)range);
  260. return new SearchResultList<T>(result.ToList(), count);
  261. }
  262. public SearchResultList<T> SearchGraph<T>(ControlledGraph<T> graph, Expression<Func<T, bool>> filter)
  263. where T : class
  264. {
  265. IQueryable<T> result = setInclude<T>(this.DataContext.CreateObjectSet<T>(typeof(T).Name), graph).Where(filter);
  266. int count = result.Count();
  267. return new SearchResultList<T>(result.ToList(), count);
  268. }
  269. #endregion SearchGraph
  270. #region Search
  271. /// <summary>
  272. /// Search for a list of Entities
  273. /// </summary>
  274. /// <typeparam name="T">Type of searched entities</typeparam>
  275. /// <param name="filter">serialized Where clause lambda expression</param>
  276. /// <param name="from">Paging: start index</param>
  277. /// <param name="range">Paging: number of record to get</param>
  278. /// <param name="orderBy">serialized Sort lambda expresion</param>
  279. /// <param name="defaultOrderBy">If order by null, set a default order by</param>
  280. /// <returns>List of found records with the number of record available</returns>
  281. private SearchResultList<T> _search<T>(string filter, int? from, int? range, object orderBy)
  282. where T : class
  283. {
  284. IQueryable<T> result = null;
  285. if (!string.IsNullOrEmpty(filter))
  286. result = this.DataContext.CreateObjectSet<T>(typeof(T).Name).Where(ExpressionSerializerHelper<T>.Deserialize(CompressionHelper.Decompress(filter)));
  287. else
  288. result = this.DataContext.CreateObjectSet<T>(typeof(T).Name);
  289. if (orderBy != null)
  290. {
  291. if (orderBy is string && orderBy != string.Empty)
  292. {
  293. Expression<Func<IQueryable<T>, IOrderedQueryable>> ex = ExpressionSerializerHelper<T>.DeserializeOrderBy((string)orderBy);
  294. if (ex != null)
  295. result = ex.Compile().Invoke(result) as IQueryable<T>;
  296. }
  297. else if (orderBy is Expression<Func<IQueryable<T>, IOrderedQueryable>>)
  298. {
  299. result = ((Expression<Func<IQueryable<T>, IOrderedQueryable>>)orderBy).Compile().Invoke(result) as IQueryable<T>;
  300. }
  301. }
  302. // - 03.09.2010 - Only compute count if FROM value equals 0
  303. int? count = null;
  304. if (from.HasValue && from.Value == 0)
  305. {
  306. count = result.Count();
  307. }
  308. if (from != null) result = result.Skip((int)from);
  309. if (range != null) result = result.Take((int)range);
  310. var res = result.ToList();
  311. if (!from.HasValue && !range.HasValue)
  312. {
  313. count = res.Count;
  314. }
  315. return new SearchResultList<T>(res, count);
  316. //
  317. }
  318. public SearchResultList<T> Search<T>(string filter, int? from, int? range, string orderBy)
  319. where T : class
  320. { return _search<T>( filter, from, range, orderBy);}
  321. public SearchResultList<T> Search<T>(string filter, int? from, int? range, Expression<Func<IQueryable<T>, IOrderedQueryable>> orderBy)
  322. where T : class
  323. { return _search<T>(filter, from, range, orderBy); }
  324. public SearchResultList<T> Search<T>(string filter)
  325. where T : class
  326. {
  327. IQueryable<T> result = this.DataContext.CreateObjectSet<T>(typeof(T).Name).Where(ExpressionSerializerHelper<T>.Deserialize(CompressionHelper.Decompress(filter)));
  328. // - 03.09.2010 - second database call for count has no utility
  329. var res = result.ToList();
  330. return new SearchResultList<T>(res, res.Count);
  331. //
  332. }
  333. public SearchResultList<T> Search<T>(Expression<Func<T, bool>> filter) where T : class
  334. {
  335. IQueryable<T> result = null;
  336. if (filter != null)
  337. result = this.DataContext.CreateObjectSet<T>(typeof(T).Name).Where(filter);
  338. else
  339. result = this.DataContext.CreateObjectSet<T>(typeof(T).Name);
  340. // - 03.09.2010 - second database call has no utility
  341. var res = result.ToList();
  342. return new SearchResultList<T>(res, res.Count);
  343. //
  344. }
  345. #endregion Search
  346. #region SearchIds
  347. public SearchResultList<TResult> SearchIds<T, TResult>(string filter, Expression<Func<T, TResult>> selectClause)
  348. where T : class
  349. {
  350. IQueryable<T> result = null;
  351. if (!string.IsNullOrEmpty(filter))
  352. result = this.DataContext.CreateObjectSet<T>(typeof(T).Name).Where(ExpressionSerializerHelper<T>.Deserialize(CompressionHelper.Decompress(filter)));
  353. else
  354. result = this.DataContext.CreateObjectSet<T>(typeof(T).Name);
  355. // - 03.09.2010 - Count from database has no utility --> directly count result list
  356. var res = result.Select(selectClause).ToList();
  357. return new SearchResultList<TResult>(res, res.Count);
  358. //
  359. }
  360. #endregion
  361. #region Get
  362. /// <summary>
  363. /// Gets an entity depending on the filter passed
  364. /// </summary>
  365. /// <typeparam name="T"></typeparam>
  366. /// <param name="filter"></param>
  367. /// <returns></returns>
  368. public T GetByClause<T>(Expression<Func<T, bool>> filter) where T : class
  369. {
  370. return this.DataContext.CreateObjectSet<T>(typeof(T).Name).Where(filter).FirstOrDefault();
  371. }
  372. public T GetGraphByClause<T>(Expression<Func<T, bool>> filter, ControlledGraph<T> graph) where T : class
  373. {
  374. return setInclude<T>(this.DataContext.CreateObjectSet<T>(typeof(T).Name), graph).Where(filter).FirstOrDefault();
  375. }
  376. #endregion
  377. }
  378. }