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

/Raven.Client.Lightweight/Document/Async/AsyncDocumentSession.cs

http://github.com/ravendb/ravendb
C# | 1084 lines | 724 code | 151 blank | 209 comment | 74 complexity | ba0daa31ddbcd515aec59c99413d4c7a MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. //-----------------------------------------------------------------------
  2. // <copyright file="AsyncDocumentSession.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Linq.Expressions;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. using Raven.Abstractions.Data;
  13. using Raven.Abstractions.Extensions;
  14. using Raven.Abstractions.Util;
  15. using Raven.Client.Connection;
  16. using Raven.Client.Connection.Async;
  17. using Raven.Client.Document.SessionOperations;
  18. using Raven.Client.Linq;
  19. using Raven.Client.Indexes;
  20. using Raven.Json.Linq;
  21. using Raven.Client.Document.Batches;
  22. using System.Diagnostics;
  23. using System.Dynamic;
  24. namespace Raven.Client.Document.Async
  25. {
  26. /// <summary>
  27. /// Implementation for async document session
  28. /// </summary>
  29. public class AsyncDocumentSession : InMemoryDocumentSessionOperations, IAsyncDocumentSessionImpl, IAsyncAdvancedSessionOperations, IDocumentQueryGenerator, ITransactionalDocumentSession
  30. {
  31. private readonly AsyncDocumentKeyGeneration asyncDocumentKeyGeneration;
  32. /// <summary>
  33. /// Initializes a new instance of the <see cref="AsyncDocumentSession"/> class.
  34. /// </summary>
  35. public AsyncDocumentSession(string dbName, DocumentStore documentStore,
  36. IAsyncDatabaseCommands asyncDatabaseCommands,
  37. DocumentSessionListeners listeners,
  38. Guid id)
  39. : base(dbName, documentStore, listeners, id)
  40. {
  41. AsyncDatabaseCommands = asyncDatabaseCommands;
  42. GenerateDocumentKeysOnStore = false;
  43. asyncDocumentKeyGeneration = new AsyncDocumentKeyGeneration(this, entitiesAndMetadata.TryGetValue, (key, entity, metadata) => key);
  44. }
  45. /// <summary>
  46. /// Gets the async database commands.
  47. /// </summary>
  48. /// <value>The async database commands.</value>
  49. public IAsyncDatabaseCommands AsyncDatabaseCommands { get; private set; }
  50. /// <summary>
  51. /// Access the lazy operations
  52. /// </summary>
  53. public IAsyncLazySessionOperations Lazily
  54. {
  55. get { return this; }
  56. }
  57. /// <summary>
  58. /// Access the eager operations
  59. /// </summary>
  60. public IAsyncEagerSessionOperations Eagerly
  61. {
  62. get { return this; }
  63. }
  64. /// <summary>
  65. /// Begin a load while including the specified path
  66. /// </summary>
  67. /// <param name="path">The path.</param>
  68. IAsyncLazyLoaderWithInclude<object> IAsyncLazySessionOperations.Include(string path)
  69. {
  70. return new AsyncLazyMultiLoaderWithInclude<object>(this).Include(path);
  71. }
  72. /// <summary>
  73. /// Begin a load while including the specified path
  74. /// </summary>
  75. /// <param name="path">The path.</param>
  76. IAsyncLazyLoaderWithInclude<T> IAsyncLazySessionOperations.Include<T>(Expression<Func<T, object>> path)
  77. {
  78. return new AsyncLazyMultiLoaderWithInclude<T>(this).Include(path);
  79. }
  80. /// <summary>
  81. /// Loads the specified ids.
  82. /// </summary>
  83. /// <param name="token">The cancellation token.</param>
  84. /// <param name="ids">The ids of the documents to load.</param>
  85. Lazy<Task<T[]>> IAsyncLazySessionOperations.LoadAsync<T>(IEnumerable<string> ids, CancellationToken token)
  86. {
  87. return Lazily.LoadAsync<T>(ids, null, token);
  88. }
  89. /// <summary>
  90. /// Loads the specified id.
  91. /// </summary>
  92. /// <typeparam name="T"></typeparam>
  93. /// <param name="id">The id.</param>
  94. /// <param name="token">The cancellation token.</param>
  95. /// <returns></returns>
  96. Lazy<Task<T>> IAsyncLazySessionOperations.LoadAsync<T>(string id, CancellationToken token)
  97. {
  98. return Lazily.LoadAsync(id, (Action<T>)null, token);
  99. }
  100. /// <summary>
  101. /// Loads the specified ids and a function to call when it is evaluated
  102. /// </summary>
  103. public Lazy<Task<T[]>> LoadAsync<T>(IEnumerable<string> ids, Action<T[]> onEval, CancellationToken token = default (CancellationToken))
  104. {
  105. return LazyLoadInternal(ids.ToArray(), new KeyValuePair<string, Type>[0], onEval, token);
  106. }
  107. /// <summary>
  108. /// Loads the specified id and a function to call when it is evaluated
  109. /// </summary>
  110. public Lazy<Task<T>> LoadAsync<T>(string id, Action<T> onEval, CancellationToken token = default (CancellationToken))
  111. {
  112. if (IsLoaded(id))
  113. return new Lazy<Task<T>>(() => LoadAsync<T>(id, token));
  114. var lazyLoadOperation = new LazyLoadOperation<T>(id, new LoadOperation(this, AsyncDatabaseCommands.DisableAllCaching, id), handleInternalMetadata: HandleInternalMetadata);
  115. return AddLazyOperation(lazyLoadOperation, onEval, token);
  116. }
  117. /// <summary>
  118. /// Loads the specified entities with the specified id after applying
  119. /// conventions on the provided id to get the real document id.
  120. /// </summary>
  121. /// <remarks>
  122. /// This method allows you to call:
  123. /// Load{Post}(1)
  124. /// And that call will internally be translated to
  125. /// Load{Post}("posts/1");
  126. ///
  127. /// Or whatever your conventions specify.
  128. /// </remarks>
  129. Lazy<Task<T>> IAsyncLazySessionOperations.LoadAsync<T>(ValueType id, Action<T> onEval, CancellationToken token)
  130. {
  131. var documentKey = Conventions.FindFullDocumentKeyFromNonStringIdentifier(id, typeof(T), false);
  132. return Lazily.LoadAsync(documentKey, onEval, token);
  133. }
  134. Lazy<Task<T[]>> IAsyncLazySessionOperations.LoadAsync<T>(CancellationToken token,params ValueType[] ids)
  135. {
  136. var documentKeys = ids.Select(id => Conventions.FindFullDocumentKeyFromNonStringIdentifier(id, typeof(T), false));
  137. return Lazily.LoadAsync<T>(documentKeys, null, token);
  138. }
  139. Lazy<Task<T[]>> IAsyncLazySessionOperations.LoadAsync<T>(IEnumerable<ValueType> ids, CancellationToken token)
  140. {
  141. var documentKeys = ids.Select(id => Conventions.FindFullDocumentKeyFromNonStringIdentifier(id, typeof(T), false));
  142. return Lazily.LoadAsync<T>(documentKeys, null, token);
  143. }
  144. Lazy<Task<T[]>> IAsyncLazySessionOperations.LoadAsync<T>(IEnumerable<ValueType> ids, Action<T[]> onEval, CancellationToken token)
  145. {
  146. var documentKeys = ids.Select(id => Conventions.FindFullDocumentKeyFromNonStringIdentifier(id, typeof(T), false));
  147. return LazyLoadInternal(documentKeys.ToArray(), new KeyValuePair<string, Type>[0], onEval, token);
  148. }
  149. Lazy<Task<TResult>> IAsyncLazySessionOperations.LoadAsync<TTransformer, TResult>(string id, Action<ILoadConfiguration> configure, Action<TResult> onEval, CancellationToken token)
  150. {
  151. return Lazily.LoadAsync(id, typeof(TTransformer), configure, onEval, token);
  152. }
  153. Lazy<Task<TResult>> IAsyncLazySessionOperations.LoadAsync<TResult>(string id, Type transformerType, Action<ILoadConfiguration> configure, Action<TResult> onEval, CancellationToken token)
  154. {
  155. var transformer = ((AbstractTransformerCreationTask)Activator.CreateInstance(transformerType)).TransformerName;
  156. var ids = new[] { id };
  157. var configuration = new RavenLoadConfiguration();
  158. if (configure != null)
  159. configure(configuration);
  160. var lazyLoadOperation = new LazyTransformerLoadOperation<TResult>(
  161. ids,
  162. transformer,
  163. configuration.TransformerParameters,
  164. new LoadTransformerOperation(this, transformer, ids),
  165. singleResult: true);
  166. return AddLazyOperation(lazyLoadOperation, onEval, token);
  167. }
  168. public Lazy<Task<TResult[]>> MoreLikeThisAsync<TResult>(MoreLikeThisQuery query, CancellationToken token = default (CancellationToken))
  169. {
  170. var multiLoadOperation = new MultiLoadOperation(this, AsyncDatabaseCommands.DisableAllCaching, null, null);
  171. var lazyOp = new LazyMoreLikeThisOperation<TResult>(multiLoadOperation, query);
  172. return AddLazyOperation<TResult[]>(lazyOp, null, token);
  173. }
  174. public Task<FacetResults[]> MultiFacetedSearchAsync(params FacetQuery[] queries)
  175. {
  176. IncrementRequestCount();
  177. return AsyncDatabaseCommands.GetMultiFacetsAsync(queries);
  178. }
  179. public string GetDocumentUrl(object entity)
  180. {
  181. DocumentMetadata value;
  182. if (entitiesAndMetadata.TryGetValue(entity, out value) == false)
  183. throw new InvalidOperationException("Could not figure out identifier for transient instance");
  184. return AsyncDatabaseCommands.UrlFor(value.Key);
  185. }
  186. Lazy<Task<T[]>> IAsyncLazySessionOperations.LoadStartingWithAsync<T>(string keyPrefix, string matches, int start, int pageSize, string exclude, RavenPagingInformation pagingInformation, string skipAfter, CancellationToken token)
  187. {
  188. var operation = new LazyStartsWithOperation<T>(keyPrefix, matches, exclude, start, pageSize, this, pagingInformation, skipAfter);
  189. return AddLazyOperation<T[]>(operation, null, token);
  190. }
  191. /// <summary>
  192. /// Loads the specified entities with the specified id after applying
  193. /// conventions on the provided id to get the real document id.
  194. /// </summary>
  195. /// <remarks>
  196. /// This method allows you to call:
  197. /// Load{Post}(1)
  198. /// And that call will internally be translated to
  199. /// Load{Post}("posts/1");
  200. ///
  201. /// Or whatever your conventions specify.
  202. /// </remarks>
  203. Lazy<Task<T>> IAsyncLazySessionOperations.LoadAsync<T>(ValueType id, CancellationToken token)
  204. {
  205. return Lazily.LoadAsync(id, (Action<T>)null, token);
  206. }
  207. internal Lazy<Task<T>> AddLazyOperation<T>(ILazyOperation operation, Action<T> onEval, CancellationToken token = default (CancellationToken))
  208. {
  209. pendingLazyOperations.Add(operation);
  210. var lazyValue = new Lazy<Task<T>>(() =>
  211. ExecuteAllPendingLazyOperationsAsync(token)
  212. .ContinueWith(t =>
  213. {
  214. if(t.Exception != null)
  215. throw new InvalidOperationException("Could not perform add lazy operation", t.Exception);
  216. return (T)operation.Result;
  217. }, token));
  218. if (onEval != null)
  219. onEvaluateLazy[operation] = theResult => onEval((T)theResult);
  220. return lazyValue;
  221. }
  222. internal Lazy<Task<int>> AddLazyCountOperation(ILazyOperation operation, CancellationToken token = default (CancellationToken))
  223. {
  224. pendingLazyOperations.Add(operation);
  225. var lazyValue = new Lazy<Task<int>>(() => ExecuteAllPendingLazyOperationsAsync(token)
  226. .ContinueWith(t =>
  227. {
  228. if(t.Exception != null)
  229. throw new InvalidOperationException("Could not perform lazy count", t.Exception);
  230. return operation.QueryResult.TotalResults;
  231. }));
  232. return lazyValue;
  233. }
  234. public async Task<ResponseTimeInformation> ExecuteAllPendingLazyOperationsAsync(CancellationToken token = default (CancellationToken))
  235. {
  236. if (pendingLazyOperations.Count == 0)
  237. return new ResponseTimeInformation();
  238. try
  239. {
  240. var sw = Stopwatch.StartNew();
  241. IncrementRequestCount();
  242. var responseTimeDuration = new ResponseTimeInformation();
  243. while (await ExecuteLazyOperationsSingleStep(responseTimeDuration).WithCancellation(token))
  244. {
  245. await Task.Delay(100).WithCancellation(token);
  246. }
  247. responseTimeDuration.ComputeServerTotal();
  248. foreach (var pendingLazyOperation in pendingLazyOperations)
  249. {
  250. Action<object> value;
  251. if (onEvaluateLazy.TryGetValue(pendingLazyOperation, out value))
  252. value(pendingLazyOperation.Result);
  253. }
  254. responseTimeDuration.TotalClientDuration = sw.Elapsed;
  255. return responseTimeDuration;
  256. }
  257. finally
  258. {
  259. pendingLazyOperations.Clear();
  260. }
  261. }
  262. private async Task<bool> ExecuteLazyOperationsSingleStep(ResponseTimeInformation responseTimeInformation)
  263. {
  264. var disposables = pendingLazyOperations.Select(x => x.EnterContext()).Where(x => x != null).ToList();
  265. try
  266. {
  267. var requests = pendingLazyOperations.Select(x => x.CreateRequest()).ToArray();
  268. var responses = await AsyncDatabaseCommands.MultiGetAsync(requests);
  269. for (int i = 0; i < pendingLazyOperations.Count; i++)
  270. {
  271. long totalTime;
  272. long.TryParse(responses[i].Headers["Temp-Request-Time"], out totalTime);
  273. responseTimeInformation.DurationBreakdown.Add(new ResponseTimeItem
  274. {
  275. Url = requests[i].UrlAndQuery,
  276. Duration = TimeSpan.FromMilliseconds(totalTime)
  277. });
  278. if (responses[i].RequestHasErrors())
  279. {
  280. throw new InvalidOperationException("Got an error from server, status code: " + responses[i].Status +
  281. Environment.NewLine + responses[i].Result);
  282. }
  283. pendingLazyOperations[i].HandleResponse(responses[i]);
  284. if (pendingLazyOperations[i].RequiresRetry)
  285. {
  286. return true;
  287. }
  288. }
  289. return false;
  290. }
  291. finally
  292. {
  293. foreach (var disposable in disposables)
  294. {
  295. disposable.Dispose();
  296. }
  297. }
  298. }
  299. /// <summary>
  300. /// Register to lazily load documents and include
  301. /// </summary>
  302. public Lazy<Task<T[]>> LazyLoadInternal<T>(string[] ids, KeyValuePair<string, Type>[] includes, Action<T[]> onEval, CancellationToken token = default (CancellationToken))
  303. {
  304. var multiLoadOperation = new MultiLoadOperation(this, AsyncDatabaseCommands.DisableAllCaching, ids, includes);
  305. var lazyOp = new LazyMultiLoadOperation<T>(multiLoadOperation, ids, includes);
  306. return AddLazyOperation(lazyOp, onEval,token);
  307. }
  308. /// <summary>
  309. /// Load documents with the specified key prefix
  310. /// </summary>
  311. public Task<IEnumerable<T>> LoadStartingWithAsync<T>(string keyPrefix, string matches = null, int start = 0, int pageSize = 25, string exclude = null, RavenPagingInformation pagingInformation = null, string skipAfter = null, CancellationToken token = default (CancellationToken))
  312. {
  313. return AsyncDatabaseCommands.StartsWithAsync(keyPrefix, matches, start, pageSize, exclude: exclude, pagingInformation: pagingInformation, skipAfter: skipAfter, token: token)
  314. .ContinueWith(task => (IEnumerable<T>)task.Result.Select(TrackEntity<T>).ToList(), token);
  315. }
  316. public Task<IEnumerable<TResult>> LoadStartingWithAsync<TTransformer, TResult>(string keyPrefix, string matches = null, int start = 0, int pageSize = 25,
  317. string exclude = null, RavenPagingInformation pagingInformation = null,
  318. Action<ILoadConfiguration> configure = null,
  319. string skipAfter = null, CancellationToken token = default (CancellationToken)) where TTransformer : AbstractTransformerCreationTask, new()
  320. {
  321. var transformer = new TTransformer().TransformerName;
  322. var configuration = new RavenLoadConfiguration();
  323. if (configure != null)
  324. {
  325. configure(configuration);
  326. }
  327. var queryOperation = new QueryOperation(this, "Load/StartingWith", null, null, false, TimeSpan.Zero, null, null, false);
  328. return AsyncDatabaseCommands.StartsWithAsync(keyPrefix, matches, start, pageSize, exclude: exclude,
  329. pagingInformation: pagingInformation, transformer: transformer,
  330. transformerParameters: configuration.TransformerParameters,
  331. skipAfter: skipAfter, token: token)
  332. .ContinueWith(
  333. task => (IEnumerable<TResult>) task.Result.Select(x=> queryOperation.Deserialize<TResult>(x.ToJson())).ToList(), token);
  334. }
  335. public Task<IAsyncEnumerator<StreamResult<T>>> StreamAsync<T>(IAsyncDocumentQuery<T> query, CancellationToken token = default (CancellationToken))
  336. {
  337. return StreamAsync(query, new Reference<QueryHeaderInformation>(), token);
  338. }
  339. public Task<IAsyncEnumerator<StreamResult<T>>> StreamAsync<T>(IQueryable<T> query, CancellationToken token = default (CancellationToken))
  340. {
  341. return StreamAsync(query, new Reference<QueryHeaderInformation>(), token);
  342. }
  343. public async Task<IAsyncEnumerator<StreamResult<T>>> StreamAsync<T>(IQueryable<T> query, Reference<QueryHeaderInformation> queryHeaderInformation, CancellationToken token = default (CancellationToken))
  344. {
  345. var queryInspector = (IRavenQueryProvider)query.Provider;
  346. var indexQuery = queryInspector.ToAsyncDocumentQuery<T>(query.Expression);
  347. return await StreamAsync(indexQuery, queryHeaderInformation, token).ConfigureAwait(false);
  348. }
  349. public async Task<IAsyncEnumerator<StreamResult<T>>> StreamAsync<T>(IAsyncDocumentQuery<T> query, Reference<QueryHeaderInformation> queryHeaderInformation, CancellationToken token = default (CancellationToken))
  350. {
  351. var ravenQueryInspector = ((IRavenQueryInspector)query);
  352. var indexQuery = ravenQueryInspector.GetIndexQuery(true);
  353. bool waitForNonStaleResultsWasSetGloably = this.Advanced.DocumentStore.Conventions.DefaultQueryingConsistency == ConsistencyOptions.AlwaysWaitForNonStaleResultsAsOfLastWrite;
  354. if (!waitForNonStaleResultsWasSetGloably && (indexQuery.WaitForNonStaleResults || indexQuery.WaitForNonStaleResultsAsOfNow))
  355. throw new NotSupportedException(
  356. "Since Stream() does not wait for indexing (by design), streaming query with WaitForNonStaleResults is not supported.");
  357. var enumerator = await AsyncDatabaseCommands.StreamQueryAsync(ravenQueryInspector.AsyncIndexQueried, indexQuery, queryHeaderInformation, token).ConfigureAwait(false);
  358. var queryOperation = ((AsyncDocumentQuery<T>)query).InitializeQueryOperation();
  359. queryOperation.DisableEntitiesTracking = true;
  360. return new QueryYieldStream<T>(this, enumerator, queryOperation,query, token);
  361. }
  362. public Task<IAsyncEnumerator<StreamResult<T>>> StreamAsync<T>(Etag fromEtag, int start = 0,
  363. int pageSize = Int32.MaxValue, RavenPagingInformation pagingInformation = null, CancellationToken token = default (CancellationToken))
  364. {
  365. return StreamAsync<T>(fromEtag: fromEtag, startsWith: null, matches: null, start: start, pageSize: pageSize, pagingInformation: pagingInformation, token: token);
  366. }
  367. public Task<IAsyncEnumerator<StreamResult<T>>> StreamAsync<T>(string startsWith, string matches = null, int start = 0,
  368. int pageSize = Int32.MaxValue, RavenPagingInformation pagingInformation = null, string skipAfter = null, CancellationToken token = default (CancellationToken))
  369. {
  370. return StreamAsync<T>(fromEtag: null, startsWith: startsWith, matches: matches, start: start, pageSize: pageSize, pagingInformation: pagingInformation, skipAfter: skipAfter, token: token);
  371. }
  372. private async Task<IAsyncEnumerator<StreamResult<T>>> StreamAsync<T>(Etag fromEtag, string startsWith, string matches, int start, int pageSize, RavenPagingInformation pagingInformation = null, string skipAfter = null, CancellationToken token = default (CancellationToken))
  373. {
  374. var enumerator = await AsyncDatabaseCommands.StreamDocsAsync(fromEtag, startsWith, matches, start, pageSize, pagingInformation: pagingInformation, skipAfter: skipAfter, token: token).ConfigureAwait(false);
  375. return new DocsYieldStream<T>(this, enumerator, token);
  376. }
  377. public abstract class YieldStream<T> : IAsyncEnumerator<StreamResult<T>>
  378. {
  379. protected readonly AsyncDocumentSession parent;
  380. protected readonly IAsyncEnumerator<RavenJObject> enumerator;
  381. protected CancellationToken token;
  382. protected YieldStream(AsyncDocumentSession parent, IAsyncEnumerator<RavenJObject> enumerator, CancellationToken token)
  383. {
  384. this.parent = parent;
  385. this.enumerator = enumerator;
  386. }
  387. public void Dispose()
  388. {
  389. enumerator.Dispose();
  390. }
  391. public async Task<bool> MoveNextAsync()
  392. {
  393. if (await enumerator.MoveNextAsync().WithCancellation(token).ConfigureAwait(false) == false)
  394. {
  395. this.Dispose();
  396. return false;
  397. }
  398. SetCurrent();
  399. return true;
  400. }
  401. protected abstract void SetCurrent();
  402. public StreamResult<T> Current { get; protected set; }
  403. }
  404. public class QueryYieldStream<T> : YieldStream<T>
  405. {
  406. private readonly QueryOperation queryOperation;
  407. private IAsyncDocumentQuery<T> query;
  408. public QueryYieldStream(AsyncDocumentSession parent, IAsyncEnumerator<RavenJObject> enumerator, QueryOperation queryOperation, IAsyncDocumentQuery<T> query, CancellationToken token = default (CancellationToken))
  409. : base(parent, enumerator, token)
  410. {
  411. this.queryOperation = queryOperation;
  412. this.query = query;
  413. }
  414. protected override void SetCurrent()
  415. {
  416. var ravenJObject = enumerator.Current;
  417. query.InvokeAfterStreamExecuted(ref ravenJObject);
  418. var meta = ravenJObject.Value<RavenJObject>(Constants.Metadata);
  419. string key = null;
  420. Etag etag = null;
  421. if (meta != null)
  422. {
  423. key = meta.Value<string>("@id") ??
  424. meta.Value<string>(Constants.DocumentIdFieldName) ??
  425. ravenJObject.Value<string>(Constants.DocumentIdFieldName);
  426. var value = meta.Value<string>("@etag");
  427. if (value != null)
  428. etag = Etag.Parse(value);
  429. }
  430. Current = new StreamResult<T>
  431. {
  432. Document = queryOperation.Deserialize<T>(ravenJObject),
  433. Etag = etag,
  434. Key = key,
  435. Metadata = meta
  436. };
  437. }
  438. }
  439. public class DocsYieldStream<T> : YieldStream<T>
  440. {
  441. public DocsYieldStream(AsyncDocumentSession parent, IAsyncEnumerator<RavenJObject> enumerator, CancellationToken token)
  442. : base(parent, enumerator, token)
  443. {
  444. }
  445. protected override void SetCurrent()
  446. {
  447. var document = SerializationHelper.RavenJObjectToJsonDocument(enumerator.Current);
  448. Current = new StreamResult<T>
  449. {
  450. Document = (T)parent.ConvertToEntity(typeof(T),
  451. document.Key,
  452. document.DataAsJson,
  453. document.Metadata, true),
  454. Etag = document.Etag,
  455. Key = document.Key,
  456. Metadata = document.Metadata
  457. };
  458. }
  459. }
  460. /// <summary>
  461. /// Queries the index specified by <typeparamref name="TIndexCreator"/> using lucene syntax.
  462. /// </summary>
  463. /// <typeparam name="T">The result of the query</typeparam>
  464. /// <typeparam name="TIndexCreator">The type of the index creator.</typeparam>
  465. /// <returns></returns>
  466. [Obsolete("Use AsyncDocumentQuery instead")]
  467. public IAsyncDocumentQuery<T> AsyncLuceneQuery<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
  468. {
  469. return AsyncDocumentQuery<T, TIndexCreator>();
  470. }
  471. /// <summary>
  472. /// Queries the index specified by <typeparamref name="TIndexCreator"/> using lucene syntax.
  473. /// </summary>
  474. /// <typeparam name="T">The result of the query</typeparam>
  475. /// <typeparam name="TIndexCreator">The type of the index creator.</typeparam>
  476. /// <returns></returns>
  477. public IAsyncDocumentQuery<T> AsyncDocumentQuery<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
  478. {
  479. var index = new TIndexCreator();
  480. return AsyncDocumentQuery<T>(index.IndexName, index.IsMapReduce);
  481. }
  482. /// <summary>
  483. /// Query the specified index using Lucene syntax
  484. /// </summary>
  485. [Obsolete("Use AsyncDocumentQuery instead.")]
  486. public IAsyncDocumentQuery<T> AsyncLuceneQuery<T>(string index, bool isMapReduce)
  487. {
  488. return AsyncDocumentQuery<T>(index, isMapReduce);
  489. }
  490. /// <summary>
  491. /// Query the specified index using Lucene syntax
  492. /// </summary>
  493. public IAsyncDocumentQuery<T> AsyncDocumentQuery<T>(string index, bool isMapReduce)
  494. {
  495. return new AsyncDocumentQuery<T>(this,null,AsyncDatabaseCommands, index, new string[0], new string[0], theListeners.QueryListeners, isMapReduce);
  496. }
  497. /// <summary>
  498. /// Dynamically query RavenDB using Lucene syntax
  499. /// </summary>
  500. [Obsolete("Use AsyncDocumentQuery instead.")]
  501. public IAsyncDocumentQuery<T> AsyncLuceneQuery<T>()
  502. {
  503. return AsyncDocumentQuery<T>();
  504. }
  505. /// <summary>
  506. /// Dynamically query RavenDB using Lucene syntax
  507. /// </summary>
  508. public IAsyncDocumentQuery<T> AsyncDocumentQuery<T>()
  509. {
  510. var indexName = CreateDynamicIndexName<T>();
  511. return new AsyncDocumentQuery<T>(this, null, AsyncDatabaseCommands, indexName, new string[0], new string[0], theListeners.QueryListeners, false);
  512. }
  513. /// <summary>
  514. /// Get the accessor for advanced operations
  515. /// </summary>
  516. /// <remarks>
  517. /// Those operations are rarely needed, and have been moved to a separate
  518. /// property to avoid cluttering the API
  519. /// </remarks>
  520. public IAsyncAdvancedSessionOperations Advanced
  521. {
  522. get { return this; }
  523. }
  524. /// <summary>
  525. /// Begin a load while including the specified path
  526. /// </summary>
  527. /// <param name="path">The path.</param>
  528. public IAsyncLoaderWithInclude<object> Include(string path)
  529. {
  530. return new AsyncMultiLoaderWithInclude<object>(this).Include(path);
  531. }
  532. /// <summary>
  533. /// Begin a load while including the specified path
  534. /// </summary>
  535. /// <param name="path">The path.</param>
  536. public IAsyncLoaderWithInclude<T> Include<T>(Expression<Func<T, object>> path)
  537. {
  538. return new AsyncMultiLoaderWithInclude<T>(this).Include(path);
  539. }
  540. /// <summary>
  541. /// Begin a load while including the specified path
  542. /// </summary>
  543. /// <param name="path">The path.</param>
  544. public IAsyncLoaderWithInclude<T> Include<T, TInclude>(Expression<Func<T, object>> path)
  545. {
  546. return new AsyncMultiLoaderWithInclude<T>(this).Include<TInclude>(path);
  547. }
  548. /// <summary>
  549. /// Begins the async load operation, with the specified id after applying
  550. /// conventions on the provided id to get the real document id.
  551. /// </summary>
  552. /// <remarks>
  553. /// This method allows you to call:
  554. /// LoadAsync{Post}(1)
  555. /// And that call will internally be translated to
  556. /// LoadAsync{Post}("posts/1");
  557. ///
  558. /// Or whatever your conventions specify.
  559. /// </remarks>
  560. public Task<T> LoadAsync<T>(ValueType id, CancellationToken token = default (CancellationToken))
  561. {
  562. var documentKey = Conventions.FindFullDocumentKeyFromNonStringIdentifier(id, typeof(T), false);
  563. return LoadAsync<T>(documentKey, token);
  564. }
  565. /// <summary>
  566. /// Begins the async multi-load operation, with the specified ids after applying
  567. /// conventions on the provided ids to get the real document ids.
  568. /// </summary>
  569. /// <remarks>
  570. /// This method allows you to call:
  571. /// LoadAsync{Post}(1,2,3)
  572. /// And that call will internally be translated to
  573. /// LoadAsync{Post}("posts/1","posts/2","posts/3");
  574. ///
  575. /// Or whatever your conventions specify.
  576. /// </remarks>
  577. public Task<T[]> LoadAsync<T>(CancellationToken token = default (CancellationToken),params ValueType[] ids)
  578. {
  579. var documentKeys = ids.Select(id => Conventions.FindFullDocumentKeyFromNonStringIdentifier(id, typeof(T), false));
  580. return LoadAsync<T>(documentKeys, token);
  581. }
  582. /// <summary>
  583. /// Begins the async multi-load operation, with the specified ids after applying
  584. /// conventions on the provided ids to get the real document ids.
  585. /// </summary>
  586. /// <remarks>
  587. /// This method allows you to call:
  588. /// LoadAsync{Post}(new List&lt;int&gt;(){1,2,3})
  589. /// And that call will internally be translated to
  590. /// LoadAsync{Post}("posts/1","posts/2","posts/3");
  591. ///
  592. /// Or whatever your conventions specify.
  593. /// </remarks>
  594. public Task<T[]> LoadAsync<T>(IEnumerable<ValueType> ids)
  595. {
  596. return LoadAsync<T>(ids, new CancellationToken());
  597. }
  598. /// <summary>
  599. /// Begins the async multi-load operation, with the specified ids after applying
  600. /// conventions on the provided ids to get the real document ids.
  601. /// </summary>
  602. /// <remarks>
  603. /// This method allows you to call:
  604. /// LoadAsync{Post}(new List&lt;int&gt;(){1,2,3})
  605. /// And that call will internally be translated to
  606. /// LoadAsync{Post}("posts/1","posts/2","posts/3");
  607. ///
  608. /// Or whatever your conventions specify.
  609. /// </remarks>
  610. public Task<T[]> LoadAsync<T>(IEnumerable<ValueType> ids, CancellationToken token = default (CancellationToken))
  611. {
  612. var documentKeys = ids.Select(id => Conventions.FindFullDocumentKeyFromNonStringIdentifier(id, typeof(T), false));
  613. return LoadAsync<T>(documentKeys, token);
  614. }
  615. /// <summary>
  616. /// Begins the async load operation
  617. /// </summary>
  618. /// <param name="id">The id.</param>
  619. /// <param name="token">The canecllation token.</param>
  620. /// <returns></returns>
  621. public async Task<T> LoadAsync<T>(string id, CancellationToken token = default (CancellationToken))
  622. {
  623. if (id == null)
  624. throw new ArgumentNullException("id", "The document id cannot be null");
  625. object entity;
  626. if (entitiesByKey.TryGetValue(id, out entity))
  627. {
  628. return (T) entity;
  629. }
  630. JsonDocument value;
  631. if (includedDocumentsByKey.TryGetValue(id, out value))
  632. {
  633. includedDocumentsByKey.Remove(id);
  634. return TrackEntity<T>(value);
  635. }
  636. if (IsDeleted(id))
  637. return default(T);
  638. IncrementRequestCount();
  639. var loadOperation = new LoadOperation(this, AsyncDatabaseCommands.DisableAllCaching, id);
  640. return await CompleteLoadAsync<T>(id, loadOperation, token);
  641. }
  642. private async Task<T> CompleteLoadAsync<T>(string id, LoadOperation loadOperation, CancellationToken token = default (CancellationToken))
  643. {
  644. loadOperation.LogOperation();
  645. using (loadOperation.EnterLoadContext())
  646. {
  647. var result = await AsyncDatabaseCommands.GetAsync(id, token);
  648. if (loadOperation.SetResult(result) == false)
  649. return loadOperation.Complete<T>();
  650. return await CompleteLoadAsync<T>(id, loadOperation, token).WithCancellation(token);
  651. }
  652. }
  653. public Task<T[]> LoadAsync<T>(IEnumerable<string> ids, CancellationToken token = default (CancellationToken))
  654. {
  655. return LoadAsyncInternal<T>(ids.ToArray(), token);
  656. }
  657. public async Task<T> LoadAsync<TTransformer, T>(string id, Action<ILoadConfiguration> configure = null, CancellationToken token = default (CancellationToken)) where TTransformer : AbstractTransformerCreationTask, new()
  658. {
  659. var result = await LoadAsync<TTransformer, T>(new[] { id }.AsEnumerable(), configure, token).ConfigureAwait(false);
  660. return result.FirstOrDefault();
  661. }
  662. public async Task<TResult[]> LoadAsync<TTransformer, TResult>(IEnumerable<string> ids, Action<ILoadConfiguration> configure = null, CancellationToken token = default (CancellationToken)) where TTransformer : AbstractTransformerCreationTask, new()
  663. {
  664. var transformer = new TTransformer();
  665. var configuration = new RavenLoadConfiguration();
  666. if (configure != null)
  667. configure(configuration);
  668. var result = await LoadUsingTransformerInternalAsync<TResult>(ids.ToArray(), null, transformer.TransformerName, configuration.TransformerParameters, token).ConfigureAwait(false);
  669. return result;
  670. }
  671. public async Task<TResult> LoadAsync<TResult>(string id, string transformer, Action<ILoadConfiguration> configure = null, CancellationToken token = default (CancellationToken))
  672. {
  673. var configuration = new RavenLoadConfiguration();
  674. if (configure != null)
  675. configure(configuration);
  676. var result = await LoadUsingTransformerInternalAsync<TResult>(new[] { id }, null, transformer, configuration.TransformerParameters, token).ConfigureAwait(false);
  677. return result.FirstOrDefault();
  678. }
  679. public async Task<TResult[]> LoadAsync<TResult>(IEnumerable<string> ids, string transformer, Action<ILoadConfiguration> configure = null, CancellationToken token = default (CancellationToken))
  680. {
  681. var configuration = new RavenLoadConfiguration();
  682. if (configure != null)
  683. configure(configuration);
  684. return await LoadUsingTransformerInternalAsync<TResult>(ids.ToArray(), null, transformer, configuration.TransformerParameters, token).ConfigureAwait(false);
  685. }
  686. public async Task<TResult> LoadAsync<TResult>(string id, Type transformerType, Action<ILoadConfiguration> configure = null, CancellationToken token = default (CancellationToken))
  687. {
  688. var configuration = new RavenLoadConfiguration();
  689. if (configure != null)
  690. configure(configuration);
  691. var transformer = ((AbstractTransformerCreationTask)Activator.CreateInstance(transformerType)).TransformerName;
  692. var result = await LoadUsingTransformerInternalAsync<TResult>(new[] { id }, null, transformer, configuration.TransformerParameters, token).ConfigureAwait(false);
  693. return result.FirstOrDefault();
  694. }
  695. public async Task<TResult[]> LoadAsync<TResult>(IEnumerable<string> ids, Type transformerType, Action<ILoadConfiguration> configure = null, CancellationToken token = default (CancellationToken))
  696. {
  697. var configuration = new RavenLoadConfiguration();
  698. if (configure != null)
  699. configure(configuration);
  700. var transformer = ((AbstractTransformerCreationTask)Activator.CreateInstance(transformerType)).TransformerName;
  701. return await LoadUsingTransformerInternalAsync<TResult>(ids.ToArray(), null, transformer, configuration.TransformerParameters, token).ConfigureAwait(false);
  702. }
  703. public async Task<T[]> LoadUsingTransformerInternalAsync<T>(string[] ids, KeyValuePair<string, Type>[] includes, string transformer, Dictionary<string, RavenJToken> transformerParameters = null, CancellationToken token = default (CancellationToken))
  704. {
  705. if (transformer == null)
  706. throw new ArgumentNullException("transformer");
  707. if (ids.Length == 0)
  708. return new T[0];
  709. IncrementRequestCount();
  710. var includeNames = includes != null ? includes.Select(x=>x.Key).ToArray() : new string[0];
  711. var multiLoadResult = await AsyncDatabaseCommands.GetAsync(ids, includeNames, transformer, transformerParameters, token: token);
  712. return new LoadTransformerOperation(this, transformer, ids).Complete<T>(multiLoadResult);
  713. }
  714. public Lazy<Task<T[]>> LazyAsyncLoadInternal<T>(string[] ids, KeyValuePair<string, Type>[] includes, Action<T[]> onEval, CancellationToken token = default (CancellationToken))
  715. {
  716. if (CheckIfIdAlreadyIncluded(ids, includes))
  717. {
  718. return new Lazy<Task<T[]>>(async () => await Task.WhenAll(ids.Select(id => LoadAsync<T>(id,token)).ToArray()).WithCancellation(token));
  719. }
  720. var multiLoadOperation = new MultiLoadOperation(this, AsyncDatabaseCommands.DisableAllCaching, ids, includes);
  721. var lazyOp = new LazyMultiLoadOperation<T>(multiLoadOperation, ids, includes);
  722. return AddLazyOperation(lazyOp, onEval);
  723. }
  724. /// <summary>
  725. /// Begins the async multi load operation
  726. /// </summary>
  727. public async Task<T[]> LoadAsyncInternal<T>(string[] ids, KeyValuePair<string, Type>[] includes,CancellationToken token = default (CancellationToken))
  728. {
  729. if (CheckIfIdAlreadyIncluded(ids, includes))
  730. {
  731. var loadTasks = ids.Select(id => LoadAsync<T>(id,token)).ToArray();
  732. var loadedData = await Task.WhenAll(loadTasks).WithCancellation(token);
  733. return loadedData;
  734. }
  735. IncrementRequestCount();
  736. var multiLoadOperation = new MultiLoadOperation(this, AsyncDatabaseCommands.DisableAllCaching, ids, includes);
  737. multiLoadOperation.LogOperation();
  738. var includePaths = includes != null ? includes.Select(x => x.Key).ToArray() : null;
  739. MultiLoadResult result;
  740. do
  741. {
  742. multiLoadOperation.LogOperation();
  743. using (multiLoadOperation.EnterMultiLoadContext())
  744. {
  745. result = await AsyncDatabaseCommands.GetAsync(ids, includePaths, token: token).ConfigureAwait(false);
  746. }
  747. } while (multiLoadOperation.SetResult(result));
  748. return multiLoadOperation.Complete<T>();
  749. }
  750. /// <summary>
  751. /// Begins the async multi load operation
  752. /// </summary>
  753. public async Task<T[]> LoadAsyncInternal<T>(string[] ids, CancellationToken token = default(CancellationToken))
  754. {
  755. if (ids.Length == 0)
  756. return new T[0];
  757. // only load documents that aren't already cached
  758. var idsOfNotExistingObjects = ids.Where(id => IsLoaded(id) == false && IsDeleted(id) == false)
  759. .Distinct(StringComparer.OrdinalIgnoreCase)
  760. .ToArray();
  761. if (idsOfNotExistingObjects.Length > 0)
  762. {
  763. IncrementRequestCount();
  764. var multiLoadOperation = new MultiLoadOperation(this, AsyncDatabaseCommands.DisableAllCaching, idsOfNotExistingObjects, null);
  765. MultiLoadResult multiLoadResult;
  766. do
  767. {
  768. multiLoadOperation.LogOperation();
  769. using (multiLoadOperation.EnterMultiLoadContext())
  770. {
  771. multiLoadResult = await AsyncDatabaseCommands.GetAsync(idsOfNotExistingObjects, null);
  772. }
  773. } while (multiLoadOperation.SetResult(multiLoadResult));
  774. multiLoadOperation.Complete<T>();
  775. }
  776. var loadTasks = ids.Select(async id => await LoadAsync<T>(id, token)).ToArray();
  777. var loadedData = await Task.WhenAll(loadTasks).WithCancellation(token);
  778. return loadedData;
  779. }
  780. /// <summary>
  781. /// Begins the async save changes operation
  782. /// </summary>
  783. /// <returns></returns>
  784. public async Task SaveChangesAsync(CancellationToken token = default (CancellationToken))
  785. {
  786. await asyncDocumentKeyGeneration.GenerateDocumentKeysForSaveChanges().WithCancellation(token);
  787. using (EntityToJson.EntitiesToJsonCachingScope())
  788. {
  789. var data = PrepareForSaveChanges();
  790. if (data.Commands.Count == 0)
  791. return;
  792. IncrementRequestCount();
  793. var result = await AsyncDatabaseCommands.BatchAsync(data.Commands.ToArray(), token);
  794. UpdateBatchResults(result, data);
  795. }
  796. }
  797. /// <summary>
  798. /// Get the json document by key from the store
  799. /// </summary>
  800. protected override JsonDocument GetJsonDocument(string documentKey)
  801. {
  802. throw new NotSupportedException("Cannot get a document in a synchronous manner using async document session");
  803. }
  804. #if !DNXCORE50
  805. /// <summary>
  806. /// Commits the specified tx id.
  807. /// </summary>
  808. /// <param name="txId">The tx id.</param>
  809. public override async Task Commit(string txId)
  810. {
  811. await AsyncDatabaseCommands.CommitAsync(txId).ConfigureAwait(false);
  812. ClearEnlistment();
  813. }
  814. /// <summary>
  815. /// Rollbacks the specified tx id.
  816. /// </summary>
  817. /// <param name="txId">The tx id.</param>
  818. public override async Task Rollback(string txId)
  819. {
  820. await AsyncDatabaseCommands.RollbackAsync(txId).ConfigureAwait(false);
  821. ClearEnlistment();
  822. }
  823. public async Task PrepareTransaction(string txId, Guid? resourceManagerId = null, byte[] recoveryInformation = null)
  824. {
  825. await AsyncDatabaseCommands.PrepareTransactionAsync(txId, resourceManagerId, recoveryInformation).ConfigureAwait(false);
  826. ClearEnlistment();
  827. }
  828. #endif
  829. /// <summary>
  830. /// Dynamically queries RavenDB using LINQ
  831. /// </summary>
  832. /// <typeparam name="T">The result of the query</typeparam>
  833. public IRavenQueryable<T> Query<T>()
  834. {
  835. string indexName = CreateDynamicIndexName<T>();
  836. return Query<T>(indexName);
  837. }
  838. public IRavenQueryable<T> Query<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
  839. {
  840. var indexCreator = new TIndexCreator();
  841. return Query<T>(indexCreator.IndexName, indexCreator.IsMapReduce);
  842. }
  843. public IRavenQueryable<T> Query<T>(string indexName, bool isMapReduce = false)
  844. {
  845. var ravenQueryStatistics = new RavenQueryStatistics();
  846. var highlightings = new RavenQueryHighlightings();
  847. var ravenQueryInspector = new RavenQueryInspector<T>();
  848. var ravenQueryProvider = new RavenQueryProvider<T>(this, indexName, ravenQueryStatistics, highlightings, null, AsyncDatabaseCommands, isMapReduce);
  849. ravenQueryInspector.Init(ravenQueryProvider,
  850. ravenQueryStatistics,
  851. highlightings,
  852. indexName,
  853. null,
  854. this, null, AsyncDatabaseCommands, isMapReduce);
  855. return ravenQueryInspector;
  856. }
  857. /// <summary>
  858. /// Create a new query for <typeparam name="T"/>
  859. /// </summary>
  860. IDocumentQuery<T> IDocumentQueryGenerator.Query<T>(string indexName, bool isMapReduce)
  861. {
  862. throw new NotSupportedException("You can't get a sync query from async session");
  863. }
  864. /// <summary>
  865. /// Create a new query for <typeparam name="T"/>
  866. /// </summary>
  867. public IAsyncDocumentQuery<T> AsyncQuery<T>(string indexName, bool isMapReduce = false)
  868. {
  869. return AsyncDocumentQuery<T>(indexName, isMapReduce);
  870. }
  871. public RavenQueryInspector<S> CreateRavenQueryInspector<S>()
  872. {
  873. return new RavenQueryInspector<S>();
  874. }
  875. protected override string GenerateKey(object entity)
  876. {
  877. throw new NotSupportedException("Async session cannot generate keys synchronously");
  878. }
  879. protected override void RememberEntityForDocumentKeyGeneration(object entity)
  880. {
  881. asyncDocumentKeyGeneration.Add(entity);
  882. }
  883. protected override Task<string> GenerateKeyAsync(object entity)
  884. {
  885. return Conventions.GenerateDocumentKeyAsync(dbName, AsyncDatabaseCommands, entity);
  886. }
  887. public async Task RefreshAsync<T>(T entity, CancellationToken token = default (CancellationToken))
  888. {
  889. DocumentMetadata value;
  890. if (entitiesAndMetadata.TryGetValue(entity, out value) == false)
  891. throw new InvalidOperationException("Cannot refresh a transient instance");
  892. IncrementRequestCount();
  893. var jsonDocument = await AsyncDatabaseCommands.GetAsync(value.Key, token);
  894. RefreshInternal(entity, jsonDocument, value);
  895. }
  896. public async Task<RavenJObject> GetMetadataForAsync<T>(T instance)
  897. {
  898. var metadata = await GetDocumentMetadataAsync(instance);
  899. return metadata.Metadata;
  900. }
  901. private async Task<DocumentMetadata> GetDocumentMetadataAsync<T>(T instance)
  902. {
  903. DocumentMetadata value;
  904. if (entitiesAndMetadata.TryGetValue(instance, out value) == false)
  905. {
  906. string id;
  907. if (GenerateEntityIdOnTheClient.TryGetIdFromInstance(instance, out id)
  908. || (instance is IDynamicMetaObjectProvider &&
  909. GenerateEntityIdOnTheClient.TryGetIdFromDynamic(instance, out id)))
  910. {
  911. AssertNoNonUniqueInstance(instance, id);
  912. var jsonDocument = await GetJsonDocumentAsync(id);
  913. value = GetDocumentMetadataValue(instance, id, jsonDocument);
  914. }
  915. else
  916. {
  917. throw new InvalidOperationException("Could not find the document key for " + instance);
  918. }
  919. }
  920. return value;
  921. }
  922. /// <summary>
  923. /// Get the json document by key from the store
  924. /// </summary>
  925. priv

Large files files are truncated, but you can click here to view the full file