PageRenderTime 56ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/System.Data.Services.Client/Client/System/Data/Services/Client/DataServiceContext.cs

https://bitbucket.org/danipen/mono
C# | 4908 lines | 4024 code | 873 blank | 11 comment | 935 complexity | 4e5d044121c43696f18b2edfc18d037a MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0

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

  1. //Copyright 2010 Microsoft Corporation
  2. //
  3. //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
  4. //You may obtain a copy of the License at
  5. //
  6. //http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. //Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
  9. //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. //See the License for the specific language governing permissions and limitations under the License.
  11. namespace System.Data.Services.Client
  12. {
  13. #region Namespaces.
  14. using System;
  15. using System.Collections;
  16. using System.Collections.Generic;
  17. using System.Collections.ObjectModel;
  18. using System.Diagnostics;
  19. using System.Globalization;
  20. using System.IO;
  21. using System.Linq;
  22. using System.Linq.Expressions;
  23. using System.Data.Services.Common;
  24. #if !ASTORIA_LIGHT
  25. using System.Net;
  26. #else
  27. using System.Data.Services.Http;
  28. #endif
  29. using System.Text;
  30. using System.Xml;
  31. using System.Xml.Linq;
  32. #endregion Namespaces.
  33. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506", Justification = "Central class of the API, likely to have many cross-references")]
  34. public class DataServiceContext
  35. {
  36. #if !TESTUNIXNEWLINE
  37. private static readonly string NewLine = System.Environment.NewLine;
  38. #else
  39. private const string NewLine = "\n";
  40. #endif
  41. private readonly System.Uri baseUri;
  42. private readonly System.Uri baseUriWithSlash;
  43. #if !ASTORIA_LIGHT
  44. private System.Net.ICredentials credentials;
  45. #endif
  46. private string dataNamespace;
  47. private Func<Type, string> resolveName;
  48. private Func<string, Type> resolveType;
  49. #if !ASTORIA_LIGHT
  50. private int timeout;
  51. #endif
  52. private bool postTunneling;
  53. private bool ignoreMissingProperties;
  54. private MergeOption mergeOption;
  55. private SaveChangesOptions saveChangesDefaultOptions;
  56. private Uri typeScheme;
  57. private bool ignoreResourceNotFoundException;
  58. #if ASTORIA_LIGHT
  59. private HttpStack httpStack;
  60. #endif
  61. #region Resource state management
  62. private uint nextChange;
  63. private Dictionary<object, EntityDescriptor> entityDescriptors = new Dictionary<object, EntityDescriptor>(EqualityComparer<object>.Default);
  64. private Dictionary<String, EntityDescriptor> identityToDescriptor;
  65. private Dictionary<LinkDescriptor, LinkDescriptor> bindings = new Dictionary<LinkDescriptor, LinkDescriptor>(LinkDescriptor.EquivalenceComparer);
  66. private bool applyingChanges;
  67. #endregion
  68. #region ctor
  69. public DataServiceContext(Uri serviceRoot)
  70. {
  71. Util.CheckArgumentNull(serviceRoot, "serviceRoot");
  72. #if ASTORIA_LIGHT
  73. if (!serviceRoot.IsAbsoluteUri)
  74. {
  75. if (XHRHttpWebRequest.IsAvailable())
  76. {
  77. serviceRoot = new Uri(System.Windows.Browser.HtmlPage.Document.DocumentUri, serviceRoot);
  78. }
  79. else
  80. {
  81. System.Net.WebClient webClient = new System.Net.WebClient();
  82. serviceRoot = new Uri(new Uri(webClient.BaseAddress), serviceRoot);
  83. }
  84. }
  85. #endif
  86. if (!serviceRoot.IsAbsoluteUri ||
  87. !Uri.IsWellFormedUriString(serviceRoot.OriginalString, UriKind.Absolute) ||
  88. !String.IsNullOrEmpty(serviceRoot.Query) ||
  89. !string.IsNullOrEmpty(serviceRoot.Fragment) ||
  90. ((serviceRoot.Scheme != "http") && (serviceRoot.Scheme != "https")))
  91. {
  92. throw Error.Argument(Strings.Context_BaseUri, "serviceRoot");
  93. }
  94. this.baseUri = serviceRoot;
  95. this.baseUriWithSlash = serviceRoot;
  96. if (!serviceRoot.OriginalString.EndsWith("/", StringComparison.Ordinal))
  97. {
  98. this.baseUriWithSlash = Util.CreateUri(serviceRoot.OriginalString + "/", UriKind.Absolute);
  99. }
  100. this.mergeOption = MergeOption.AppendOnly;
  101. this.DataNamespace = XmlConstants.DataWebNamespace;
  102. #if ASTORIA_LIGHT
  103. this.UsePostTunneling = true;
  104. #else
  105. this.UsePostTunneling = false;
  106. #endif
  107. this.typeScheme = new Uri(XmlConstants.DataWebSchemeNamespace);
  108. #if ASTORIA_LIGHT
  109. this.httpStack = HttpStack.Auto;
  110. #endif
  111. }
  112. #endregion
  113. public event EventHandler<SendingRequestEventArgs> SendingRequest;
  114. public event EventHandler<ReadingWritingEntityEventArgs> ReadingEntity;
  115. public event EventHandler<ReadingWritingEntityEventArgs> WritingEntity;
  116. internal event EventHandler<SaveChangesEventArgs> ChangesSaved;
  117. #region BaseUri, Credentials, MergeOption, Timeout, Links, Entities
  118. public Uri BaseUri
  119. {
  120. get { return this.baseUri; }
  121. }
  122. #if !ASTORIA_LIGHT
  123. public System.Net.ICredentials Credentials
  124. {
  125. get { return this.credentials; }
  126. set { this.credentials = value; }
  127. }
  128. #endif
  129. public MergeOption MergeOption
  130. {
  131. get { return this.mergeOption; }
  132. set { this.mergeOption = Util.CheckEnumerationValue(value, "MergeOption"); }
  133. }
  134. public bool ApplyingChanges
  135. {
  136. get { return this.applyingChanges; }
  137. internal set { this.applyingChanges = value; }
  138. }
  139. public bool IgnoreMissingProperties
  140. {
  141. get { return this.ignoreMissingProperties; }
  142. set { this.ignoreMissingProperties = value; }
  143. }
  144. public string DataNamespace
  145. {
  146. get
  147. {
  148. return this.dataNamespace;
  149. }
  150. set
  151. {
  152. Util.CheckArgumentNull(value, "value");
  153. this.dataNamespace = value;
  154. }
  155. }
  156. public Func<Type, string> ResolveName
  157. {
  158. get { return this.resolveName; }
  159. set { this.resolveName = value; }
  160. }
  161. public Func<string, Type> ResolveType
  162. {
  163. get { return this.resolveType; }
  164. set { this.resolveType = value; }
  165. }
  166. #if !ASTORIA_LIGHT
  167. public int Timeout
  168. {
  169. get
  170. {
  171. return this.timeout;
  172. }
  173. set
  174. {
  175. if (value < 0)
  176. {
  177. throw Error.ArgumentOutOfRange("Timeout");
  178. }
  179. this.timeout = value;
  180. }
  181. }
  182. #endif
  183. public Uri TypeScheme
  184. {
  185. get
  186. {
  187. return this.typeScheme;
  188. }
  189. set
  190. {
  191. Util.CheckArgumentNull(value, "value");
  192. this.typeScheme = value;
  193. }
  194. }
  195. public bool UsePostTunneling
  196. {
  197. get { return this.postTunneling; }
  198. set { this.postTunneling = value; }
  199. }
  200. public ReadOnlyCollection<LinkDescriptor> Links
  201. {
  202. get
  203. {
  204. return this.bindings.Values.OrderBy(l => l.ChangeOrder).ToList().AsReadOnly();
  205. }
  206. }
  207. public ReadOnlyCollection<EntityDescriptor> Entities
  208. {
  209. get
  210. {
  211. return this.entityDescriptors.Values.OrderBy(d => d.ChangeOrder).ToList().AsReadOnly();
  212. }
  213. }
  214. public SaveChangesOptions SaveChangesDefaultOptions
  215. {
  216. get
  217. {
  218. return this.saveChangesDefaultOptions;
  219. }
  220. set
  221. {
  222. ValidateSaveChangesOptions(value);
  223. this.saveChangesDefaultOptions = value;
  224. }
  225. }
  226. #endregion
  227. public bool IgnoreResourceNotFoundException
  228. {
  229. get { return this.ignoreResourceNotFoundException; }
  230. set { this.ignoreResourceNotFoundException = value; }
  231. }
  232. #if ASTORIA_LIGHT
  233. public HttpStack HttpStack
  234. {
  235. get { return this.httpStack; }
  236. set { this.httpStack = Util.CheckEnumerationValue(value, "HttpStack"); }
  237. }
  238. #endif
  239. internal Uri BaseUriWithSlash
  240. {
  241. get { return this.baseUriWithSlash; }
  242. }
  243. internal bool HasReadingEntityHandlers
  244. {
  245. [DebuggerStepThrough]
  246. get { return this.ReadingEntity != null; }
  247. }
  248. #region Entity and Link Tracking
  249. public EntityDescriptor GetEntityDescriptor(object entity)
  250. {
  251. Util.CheckArgumentNull(entity, "entity");
  252. EntityDescriptor descriptor;
  253. if (this.entityDescriptors.TryGetValue(entity, out descriptor))
  254. {
  255. return descriptor;
  256. }
  257. else
  258. {
  259. return null;
  260. }
  261. }
  262. public LinkDescriptor GetLinkDescriptor(object source, string sourceProperty, object target)
  263. {
  264. Util.CheckArgumentNull(source, "source");
  265. Util.CheckArgumentNotEmpty(sourceProperty, "sourceProperty");
  266. Util.CheckArgumentNull(target, "target");
  267. LinkDescriptor link;
  268. if (this.bindings.TryGetValue(new LinkDescriptor(source, sourceProperty, target), out link))
  269. {
  270. return link;
  271. }
  272. else
  273. {
  274. return null;
  275. }
  276. }
  277. #endregion
  278. #region CancelRequest
  279. public void CancelRequest(IAsyncResult asyncResult)
  280. {
  281. Util.CheckArgumentNull(asyncResult, "asyncResult");
  282. BaseAsyncResult result = asyncResult as BaseAsyncResult;
  283. if ((null == result) || (this != result.Source))
  284. {
  285. object context = null;
  286. DataServiceQuery query = null;
  287. if (null != result)
  288. {
  289. query = result.Source as DataServiceQuery;
  290. if (null != query)
  291. {
  292. DataServiceQueryProvider provider = query.Provider as DataServiceQueryProvider;
  293. if (null != provider)
  294. {
  295. context = provider.Context;
  296. }
  297. }
  298. }
  299. if (this != context)
  300. {
  301. throw Error.Argument(Strings.Context_DidNotOriginateAsync, "asyncResult");
  302. }
  303. }
  304. if (!result.IsCompletedInternally)
  305. {
  306. result.SetAborted();
  307. WebRequest request = result.Abortable;
  308. if (null != request)
  309. {
  310. request.Abort();
  311. }
  312. }
  313. }
  314. #endregion
  315. #region CreateQuery
  316. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "required for this feature")]
  317. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "required for this feature")]
  318. public DataServiceQuery<T> CreateQuery<T>(string entitySetName)
  319. {
  320. Util.CheckArgumentNotEmpty(entitySetName, "entitySetName");
  321. this.ValidateEntitySetName(ref entitySetName);
  322. ResourceSetExpression rse = new ResourceSetExpression(typeof(IOrderedQueryable<T>), null, Expression.Constant(entitySetName), typeof(T), null, CountOption.None, null, null);
  323. return new DataServiceQuery<T>.DataServiceOrderedQuery(rse, new DataServiceQueryProvider(this));
  324. }
  325. #endregion
  326. #region GetMetadataUri
  327. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "required for this feature")]
  328. public Uri GetMetadataUri()
  329. {
  330. Uri metadataUri = Util.CreateUri(this.baseUriWithSlash.OriginalString + XmlConstants.UriMetadataSegment, UriKind.Absolute);
  331. return metadataUri;
  332. }
  333. #endregion
  334. #region LoadProperty
  335. public IAsyncResult BeginLoadProperty(object entity, string propertyName, AsyncCallback callback, object state)
  336. {
  337. return this.BeginLoadProperty(entity, propertyName, (Uri)null, callback, state);
  338. }
  339. public IAsyncResult BeginLoadProperty(object entity, string propertyName, Uri nextLinkUri, AsyncCallback callback, object state)
  340. {
  341. LoadPropertyResult result = this.CreateLoadPropertyRequest(entity, propertyName, callback, state, nextLinkUri, null);
  342. result.BeginExecute();
  343. return result;
  344. }
  345. public IAsyncResult BeginLoadProperty(object entity, string propertyName, DataServiceQueryContinuation continuation, AsyncCallback callback, object state)
  346. {
  347. Util.CheckArgumentNull(continuation, "continuation");
  348. LoadPropertyResult result = this.CreateLoadPropertyRequest(entity, propertyName, callback, state, null, continuation);
  349. result.BeginExecute();
  350. return result;
  351. }
  352. public QueryOperationResponse EndLoadProperty(IAsyncResult asyncResult)
  353. {
  354. LoadPropertyResult response = QueryResult.EndExecute<LoadPropertyResult>(this, "LoadProperty", asyncResult);
  355. return response.LoadProperty();
  356. }
  357. #if !ASTORIA_LIGHT
  358. public QueryOperationResponse LoadProperty(object entity, string propertyName)
  359. {
  360. return this.LoadProperty(entity, propertyName, (Uri)null);
  361. }
  362. public QueryOperationResponse LoadProperty(object entity, string propertyName, Uri nextLinkUri)
  363. {
  364. LoadPropertyResult result = this.CreateLoadPropertyRequest(entity, propertyName, null, null, nextLinkUri, null);
  365. result.Execute();
  366. return result.LoadProperty();
  367. }
  368. public QueryOperationResponse LoadProperty(object entity, string propertyName, DataServiceQueryContinuation continuation)
  369. {
  370. LoadPropertyResult result = this.CreateLoadPropertyRequest(entity, propertyName, null, null, null, continuation);
  371. result.Execute();
  372. return result.LoadProperty();
  373. }
  374. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011", Justification = "allows compiler to infer 'T'")]
  375. public QueryOperationResponse<T> LoadProperty<T>(object entity, string propertyName, DataServiceQueryContinuation<T> continuation)
  376. {
  377. LoadPropertyResult result = this.CreateLoadPropertyRequest(entity, propertyName, null, null, null, continuation);
  378. result.Execute();
  379. return (QueryOperationResponse<T>)result.LoadProperty();
  380. }
  381. #endif
  382. #endregion
  383. #region GetReadStreamUri
  384. public Uri GetReadStreamUri(object entity)
  385. {
  386. EntityDescriptor box = this.EnsureContained(entity, "entity");
  387. return box.GetMediaResourceUri(this.baseUriWithSlash);
  388. }
  389. #endregion
  390. #region GetReadStream, BeginGetReadStream, EndGetReadStream
  391. public IAsyncResult BeginGetReadStream(object entity, DataServiceRequestArgs args, AsyncCallback callback, object state)
  392. {
  393. GetReadStreamResult result;
  394. result = this.CreateGetReadStreamResult(entity, args, callback, state);
  395. result.Begin();
  396. return result;
  397. }
  398. public DataServiceStreamResponse EndGetReadStream(IAsyncResult asyncResult)
  399. {
  400. GetReadStreamResult result = BaseAsyncResult.EndExecute<GetReadStreamResult>(this, "GetReadStream", asyncResult);
  401. return result.End();
  402. }
  403. #if !ASTORIA_LIGHT
  404. public DataServiceStreamResponse GetReadStream(object entity)
  405. {
  406. DataServiceRequestArgs args = new DataServiceRequestArgs();
  407. return this.GetReadStream(entity, args);
  408. }
  409. public DataServiceStreamResponse GetReadStream(object entity, string acceptContentType)
  410. {
  411. Util.CheckArgumentNotEmpty(acceptContentType, "acceptContentType");
  412. DataServiceRequestArgs args = new DataServiceRequestArgs();
  413. args.AcceptContentType = acceptContentType;
  414. return this.GetReadStream(entity, args);
  415. }
  416. public DataServiceStreamResponse GetReadStream(object entity, DataServiceRequestArgs args)
  417. {
  418. GetReadStreamResult result = this.CreateGetReadStreamResult(entity, args, null, null);
  419. return result.Execute();
  420. }
  421. #endif
  422. #endregion
  423. #region SetSaveStream
  424. public void SetSaveStream(object entity, Stream stream, bool closeStream, string contentType, string slug)
  425. {
  426. Util.CheckArgumentNull(contentType, "contentType");
  427. Util.CheckArgumentNull(slug, "slug");
  428. DataServiceRequestArgs args = new DataServiceRequestArgs();
  429. args.ContentType = contentType;
  430. args.Slug = slug;
  431. this.SetSaveStream(entity, stream, closeStream, args);
  432. }
  433. public void SetSaveStream(object entity, Stream stream, bool closeStream, DataServiceRequestArgs args)
  434. {
  435. EntityDescriptor box = this.EnsureContained(entity, "entity");
  436. Util.CheckArgumentNull(stream, "stream");
  437. Util.CheckArgumentNull(args, "args");
  438. ClientType clientType = ClientType.Create(entity.GetType());
  439. if (clientType.MediaDataMember != null)
  440. {
  441. throw new ArgumentException(
  442. Strings.Context_SetSaveStreamOnMediaEntryProperty(clientType.ElementTypeName),
  443. "entity");
  444. }
  445. box.SaveStream = new DataServiceSaveStream(stream, closeStream, args);
  446. Debug.Assert(box.State != EntityStates.Detached, "We should never have a detached entity in the entityDescriptor dictionary.");
  447. switch (box.State)
  448. {
  449. case EntityStates.Added:
  450. box.StreamState = StreamStates.Added;
  451. break;
  452. case EntityStates.Modified:
  453. case EntityStates.Unchanged:
  454. box.StreamState = StreamStates.Modified;
  455. break;
  456. case EntityStates.Deleted:
  457. default:
  458. throw new DataServiceClientException(Strings.DataServiceException_GeneralError);
  459. }
  460. }
  461. #endregion
  462. #region ExecuteBatch, BeginExecuteBatch, EndExecuteBatch
  463. public IAsyncResult BeginExecuteBatch(AsyncCallback callback, object state, params DataServiceRequest[] queries)
  464. {
  465. Util.CheckArgumentNotEmpty(queries, "queries");
  466. SaveResult result = new SaveResult(this, "ExecuteBatch", queries, SaveChangesOptions.Batch, callback, state, true);
  467. result.BatchBeginRequest(false);
  468. return result;
  469. }
  470. public DataServiceResponse EndExecuteBatch(IAsyncResult asyncResult)
  471. {
  472. SaveResult result = BaseAsyncResult.EndExecute<SaveResult>(this, "ExecuteBatch", asyncResult);
  473. return result.EndRequest();
  474. }
  475. #if !ASTORIA_LIGHT
  476. public DataServiceResponse ExecuteBatch(params DataServiceRequest[] queries)
  477. {
  478. Util.CheckArgumentNotEmpty(queries, "queries");
  479. SaveResult result = new SaveResult(this, "ExecuteBatch", queries, SaveChangesOptions.Batch, null, null, false);
  480. result.BatchRequest(false );
  481. return result.EndRequest();
  482. }
  483. #endif
  484. #endregion
  485. #region Execute(Uri), BeginExecute(Uri), EndExecute(Uri)
  486. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Type is used to infer result")]
  487. public IAsyncResult BeginExecute<TElement>(Uri requestUri, AsyncCallback callback, object state)
  488. {
  489. requestUri = Util.CreateUri(this.baseUriWithSlash, requestUri);
  490. QueryComponents qc = new QueryComponents(requestUri, Util.DataServiceVersionEmpty, typeof(TElement), null, null);
  491. return (new DataServiceRequest<TElement>(qc, null)).BeginExecute(this, this, callback, state);
  492. }
  493. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Type is used to infer result")]
  494. public IAsyncResult BeginExecute<T>(DataServiceQueryContinuation<T> continuation, AsyncCallback callback, object state)
  495. {
  496. Util.CheckArgumentNull(continuation, "continuation");
  497. QueryComponents qc = continuation.CreateQueryComponents();
  498. return (new DataServiceRequest<T>(qc, continuation.Plan)).BeginExecute(this, this, callback, state);
  499. }
  500. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Type is used to infer result")]
  501. public IEnumerable<TElement> EndExecute<TElement>(IAsyncResult asyncResult)
  502. {
  503. Util.CheckArgumentNull(asyncResult, "asyncResult");
  504. return DataServiceRequest.EndExecute<TElement>(this, this, asyncResult);
  505. }
  506. #if !ASTORIA_LIGHT
  507. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Type is used to infer result")]
  508. public IEnumerable<TElement> Execute<TElement>(Uri requestUri)
  509. {
  510. requestUri = Util.CreateUri(this.baseUriWithSlash, requestUri);
  511. QueryComponents qc = new QueryComponents(requestUri, Util.DataServiceVersionEmpty, typeof(TElement), null, null);
  512. DataServiceRequest request = new DataServiceRequest<TElement>(qc, null);
  513. return request.Execute<TElement>(this, qc);
  514. }
  515. public QueryOperationResponse<T> Execute<T>(DataServiceQueryContinuation<T> continuation)
  516. {
  517. Util.CheckArgumentNull(continuation, "continuation");
  518. QueryComponents qc = continuation.CreateQueryComponents();
  519. DataServiceRequest request = new DataServiceRequest<T>(qc, continuation.Plan);
  520. return request.Execute<T>(this, qc);
  521. }
  522. #endif
  523. #endregion
  524. #region SaveChanges, BeginSaveChanges, EndSaveChanges
  525. public IAsyncResult BeginSaveChanges(AsyncCallback callback, object state)
  526. {
  527. return this.BeginSaveChanges(this.SaveChangesDefaultOptions, callback, state);
  528. }
  529. public IAsyncResult BeginSaveChanges(SaveChangesOptions options, AsyncCallback callback, object state)
  530. {
  531. ValidateSaveChangesOptions(options);
  532. SaveResult result = new SaveResult(this, "SaveChanges", null, options, callback, state, true);
  533. bool replaceOnUpdate = IsFlagSet(options, SaveChangesOptions.ReplaceOnUpdate);
  534. if (IsFlagSet(options, SaveChangesOptions.Batch))
  535. {
  536. result.BatchBeginRequest(replaceOnUpdate);
  537. }
  538. else
  539. {
  540. result.BeginNextChange(replaceOnUpdate);
  541. }
  542. return result;
  543. }
  544. public DataServiceResponse EndSaveChanges(IAsyncResult asyncResult)
  545. {
  546. SaveResult result = BaseAsyncResult.EndExecute<SaveResult>(this, "SaveChanges", asyncResult);
  547. DataServiceResponse errors = result.EndRequest();
  548. if (this.ChangesSaved != null)
  549. {
  550. this.ChangesSaved(this, new SaveChangesEventArgs(errors));
  551. }
  552. return errors;
  553. }
  554. #if !ASTORIA_LIGHT
  555. public DataServiceResponse SaveChanges()
  556. {
  557. return this.SaveChanges(this.SaveChangesDefaultOptions);
  558. }
  559. public DataServiceResponse SaveChanges(SaveChangesOptions options)
  560. {
  561. DataServiceResponse errors = null;
  562. ValidateSaveChangesOptions(options);
  563. SaveResult result = new SaveResult(this, "SaveChanges", null, options, null, null, false);
  564. bool replaceOnUpdate = IsFlagSet(options, SaveChangesOptions.ReplaceOnUpdate);
  565. if (IsFlagSet(options, SaveChangesOptions.Batch))
  566. {
  567. result.BatchRequest(replaceOnUpdate);
  568. }
  569. else
  570. {
  571. result.BeginNextChange(replaceOnUpdate);
  572. }
  573. errors = result.EndRequest();
  574. Debug.Assert(null != errors, "null errors");
  575. if (this.ChangesSaved != null)
  576. {
  577. this.ChangesSaved(this, new SaveChangesEventArgs(errors));
  578. }
  579. return errors;
  580. }
  581. #endif
  582. #endregion
  583. #region Add, Attach, Delete, Detach, Update, TryGetEntity, TryGetUri
  584. public void AddLink(object source, string sourceProperty, object target)
  585. {
  586. this.EnsureRelatable(source, sourceProperty, target, EntityStates.Added);
  587. LinkDescriptor relation = new LinkDescriptor(source, sourceProperty, target);
  588. if (this.bindings.ContainsKey(relation))
  589. {
  590. throw Error.InvalidOperation(Strings.Context_RelationAlreadyContained);
  591. }
  592. relation.State = EntityStates.Added;
  593. this.bindings.Add(relation, relation);
  594. this.IncrementChange(relation);
  595. }
  596. public void AttachLink(object source, string sourceProperty, object target)
  597. {
  598. this.AttachLink(source, sourceProperty, target, MergeOption.NoTracking);
  599. }
  600. public bool DetachLink(object source, string sourceProperty, object target)
  601. {
  602. Util.CheckArgumentNull(source, "source");
  603. Util.CheckArgumentNotEmpty(sourceProperty, "sourceProperty");
  604. LinkDescriptor existing;
  605. LinkDescriptor relation = new LinkDescriptor(source, sourceProperty, target);
  606. if (!this.bindings.TryGetValue(relation, out existing))
  607. {
  608. return false;
  609. }
  610. this.DetachExistingLink(existing, false);
  611. return true;
  612. }
  613. public void DeleteLink(object source, string sourceProperty, object target)
  614. {
  615. bool delay = this.EnsureRelatable(source, sourceProperty, target, EntityStates.Deleted);
  616. LinkDescriptor existing = null;
  617. LinkDescriptor relation = new LinkDescriptor(source, sourceProperty, target);
  618. if (this.bindings.TryGetValue(relation, out existing) && (EntityStates.Added == existing.State))
  619. {
  620. this.DetachExistingLink(existing, false);
  621. }
  622. else
  623. {
  624. if (delay)
  625. {
  626. throw Error.InvalidOperation(Strings.Context_NoRelationWithInsertEnd);
  627. }
  628. if (null == existing)
  629. {
  630. this.bindings.Add(relation, relation);
  631. existing = relation;
  632. }
  633. if (EntityStates.Deleted != existing.State)
  634. {
  635. existing.State = EntityStates.Deleted;
  636. this.IncrementChange(existing);
  637. }
  638. }
  639. }
  640. public void SetLink(object source, string sourceProperty, object target)
  641. {
  642. this.EnsureRelatable(source, sourceProperty, target, EntityStates.Modified);
  643. LinkDescriptor relation = this.DetachReferenceLink(source, sourceProperty, target, MergeOption.NoTracking);
  644. if (null == relation)
  645. {
  646. relation = new LinkDescriptor(source, sourceProperty, target);
  647. this.bindings.Add(relation, relation);
  648. }
  649. Debug.Assert(
  650. 0 == relation.State ||
  651. IncludeLinkState(relation.State),
  652. "set link entity state");
  653. if (EntityStates.Modified != relation.State)
  654. {
  655. relation.State = EntityStates.Modified;
  656. this.IncrementChange(relation);
  657. }
  658. }
  659. #endregion
  660. #region AddObject, AttachTo, DeleteObject, Detach, TryGetEntity, TryGetUri
  661. public void AddObject(string entitySetName, object entity)
  662. {
  663. this.ValidateEntitySetName(ref entitySetName);
  664. ValidateEntityType(entity);
  665. EntityDescriptor resource = new EntityDescriptor(null, null , null , entity, null, null, entitySetName, null, EntityStates.Added);
  666. try
  667. {
  668. this.entityDescriptors.Add(entity, resource);
  669. }
  670. catch (ArgumentException)
  671. {
  672. throw Error.InvalidOperation(Strings.Context_EntityAlreadyContained);
  673. }
  674. this.IncrementChange(resource);
  675. }
  676. public void AddRelatedObject(object source, string sourceProperty, object target)
  677. {
  678. Util.CheckArgumentNull(source, "source");
  679. Util.CheckArgumentNotEmpty(sourceProperty, "propertyName");
  680. Util.CheckArgumentNull(target, "target");
  681. ValidateEntityType(source);
  682. EntityDescriptor sourceResource = this.EnsureContained(source, "source");
  683. if (sourceResource.State == EntityStates.Deleted)
  684. {
  685. throw Error.InvalidOperation(Strings.Context_AddRelatedObjectSourceDeleted);
  686. }
  687. ClientType parentType = ClientType.Create(source.GetType());
  688. ClientType.ClientProperty property = parentType.GetProperty(sourceProperty, false);
  689. if (property.IsKnownType || property.CollectionType == null)
  690. {
  691. throw Error.InvalidOperation(Strings.Context_AddRelatedObjectCollectionOnly);
  692. }
  693. ClientType childType = ClientType.Create(target.GetType());
  694. ValidateEntityType(target);
  695. ClientType propertyElementType = ClientType.Create(property.CollectionType);
  696. if (!propertyElementType.ElementType.IsAssignableFrom(childType.ElementType))
  697. {
  698. throw Error.Argument(Strings.Context_RelationNotRefOrCollection, "target");
  699. }
  700. EntityDescriptor targetResource = new EntityDescriptor(null, null, null, target, sourceResource, sourceProperty, null , null, EntityStates.Added);
  701. try
  702. {
  703. this.entityDescriptors.Add(target, targetResource);
  704. }
  705. catch (ArgumentException)
  706. {
  707. throw Error.InvalidOperation(Strings.Context_EntityAlreadyContained);
  708. }
  709. LinkDescriptor end = targetResource.GetRelatedEnd();
  710. end.State = EntityStates.Added;
  711. this.bindings.Add(end, end);
  712. this.IncrementChange(targetResource);
  713. }
  714. public void AttachTo(string entitySetName, object entity)
  715. {
  716. this.AttachTo(entitySetName, entity, null);
  717. }
  718. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704", MessageId = "etag", Justification = "represents ETag in request")]
  719. public void AttachTo(string entitySetName, object entity, string etag)
  720. {
  721. this.ValidateEntitySetName(ref entitySetName);
  722. Uri editLink = GenerateEditLinkUri(this.baseUriWithSlash, entitySetName, entity);
  723. String identity = Util.ReferenceIdentity(editLink.ToString());
  724. EntityDescriptor descriptor = new EntityDescriptor(identity, null , editLink, entity, null , null , null , etag, EntityStates.Unchanged);
  725. this.InternalAttachEntityDescriptor(descriptor, true);
  726. }
  727. public void DeleteObject(object entity)
  728. {
  729. Util.CheckArgumentNull(entity, "entity");
  730. EntityDescriptor resource = null;
  731. if (!this.entityDescriptors.TryGetValue(entity, out resource))
  732. {
  733. throw Error.InvalidOperation(Strings.Context_EntityNotContained);
  734. }
  735. EntityStates state = resource.State;
  736. if (EntityStates.Added == state)
  737. {
  738. this.DetachResource(resource);
  739. }
  740. else if (EntityStates.Deleted != state)
  741. {
  742. Debug.Assert(
  743. IncludeLinkState(state),
  744. "bad state transition to deleted");
  745. resource.State = EntityStates.Deleted;
  746. this.IncrementChange(resource);
  747. }
  748. }
  749. public bool Detach(object entity)
  750. {
  751. Util.CheckArgumentNull(entity, "entity");
  752. EntityDescriptor resource = null;
  753. if (this.entityDescriptors.TryGetValue(entity, out resource))
  754. {
  755. return this.DetachResource(resource);
  756. }
  757. return false;
  758. }
  759. public void UpdateObject(object entity)
  760. {
  761. Util.CheckArgumentNull(entity, "entity");
  762. EntityDescriptor resource = null;
  763. if (!this.entityDescriptors.TryGetValue(entity, out resource))
  764. {
  765. throw Error.Argument(Strings.Context_EntityNotContained, "entity");
  766. }
  767. if (EntityStates.Unchanged == resource.State)
  768. {
  769. resource.State = EntityStates.Modified;
  770. this.IncrementChange(resource);
  771. }
  772. }
  773. public bool TryGetEntity<TEntity>(Uri identity, out TEntity entity) where TEntity : class
  774. {
  775. entity = null;
  776. Util.CheckArgumentNull(identity, "relativeUri");
  777. EntityStates state;
  778. entity = (TEntity)this.TryGetEntity(Util.ReferenceIdentity(identity.ToString()), null, MergeOption.AppendOnly, out state);
  779. return (null != entity);
  780. }
  781. public bool TryGetUri(object entity, out Uri identity)
  782. {
  783. identity = null;
  784. Util.CheckArgumentNull(entity, "entity");
  785. EntityDescriptor resource = null;
  786. if ((null != this.identityToDescriptor) &&
  787. this.entityDescriptors.TryGetValue(entity, out resource) &&
  788. (null != resource.Identity) &&
  789. Object.ReferenceEquals(resource, this.identityToDescriptor[resource.Identity]))
  790. {
  791. string identityUri = Util.DereferenceIdentity(resource.Identity);
  792. identity = Util.CreateUri(identityUri, UriKind.Absolute);
  793. }
  794. return (null != identity);
  795. }
  796. internal static Exception HandleResponse(
  797. HttpStatusCode statusCode,
  798. string responseVersion,
  799. Func<Stream> getResponseStream,
  800. bool throwOnFailure)
  801. {
  802. InvalidOperationException failure = null;
  803. if (!CanHandleResponseVersion(responseVersion))
  804. {
  805. string description = Strings.Context_VersionNotSupported(
  806. responseVersion,
  807. SerializeSupportedVersions());
  808. failure = Error.InvalidOperation(description);
  809. }
  810. if (failure == null && !WebUtil.SuccessStatusCode(statusCode))
  811. {
  812. failure = GetResponseText(getResponseStream, statusCode);
  813. }
  814. if (failure != null && throwOnFailure)
  815. {
  816. throw failure;
  817. }
  818. return failure;
  819. }
  820. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031", Justification = "Cache exception so user can examine it later")]
  821. internal static DataServiceClientException GetResponseText(Func<Stream> getResponseStream, HttpStatusCode statusCode)
  822. {
  823. string message = null;
  824. using (System.IO.Stream stream = getResponseStream())
  825. {
  826. if ((null != stream) && stream.CanRead)
  827. {
  828. message = new StreamReader(stream).ReadToEnd();
  829. }
  830. }
  831. if (String.IsNullOrEmpty(message))
  832. {
  833. message = statusCode.ToString();
  834. }
  835. return new DataServiceClientException(message, (int)statusCode);
  836. }
  837. internal void AttachIdentity(String identity, Uri selfLink, Uri editLink, object entity, string etag)
  838. {
  839. Debug.Assert(null != identity, "must have identity");
  840. this.EnsureIdentityToResource();
  841. EntityDescriptor resource = this.entityDescriptors[entity];
  842. this.DetachResourceIdentity(resource);
  843. if (resource.IsDeepInsert)
  844. {
  845. LinkDescriptor end = this.bindings[resource.GetRelatedEnd()];
  846. end.State = EntityStates.Unchanged;
  847. }
  848. resource.ETag = etag;
  849. resource.Identity = identity;
  850. resource.SelfLink = selfLink;
  851. resource.EditLink = editLink;
  852. resource.State = EntityStates.Unchanged;
  853. this.identityToDescriptor[identity] = resource;
  854. }
  855. internal void AttachLocation(object entity, string location)
  856. {
  857. Debug.Assert(null != entity, "null != entity");
  858. Uri editLink = new Uri(location, UriKind.Absolute);
  859. String identity = Util.ReferenceIdentity(editLink.ToString());
  860. this.EnsureIdentityToResource();
  861. EntityDescriptor resource = this.entityDescriptors[entity];
  862. this.DetachResourceIdentity(resource);
  863. if (resource.IsDeepInsert)
  864. {
  865. LinkDescriptor end = this.bindings[resource.GetRelatedEnd()];
  866. end.State = EntityStates.Unchanged;
  867. }
  868. resource.Identity = identity;
  869. resource.EditLink = editLink;
  870. this.identityToDescriptor[identity] = resource;
  871. }
  872. internal void AttachLink(object source, string sourceProperty, object target, MergeOption linkMerge)
  873. {
  874. this.EnsureRelatable(source, sourceProperty, target, EntityStates.Unchanged);
  875. LinkDescriptor existing = null;
  876. LinkDescriptor relation = new LinkDescriptor(source, sourceProperty, target);
  877. if (this.bindings.TryGetValue(relation, out existing))
  878. {
  879. switch (linkMerge)
  880. {
  881. case MergeOption.AppendOnly:
  882. break;
  883. case MergeOption.OverwriteChanges:
  884. relation = existing;
  885. break;
  886. case MergeOption.PreserveChanges:
  887. if ((EntityStates.Added == existing.State) ||
  888. (EntityStates.Unchanged == existing.State) ||
  889. (EntityStates.Modified == existing.State && null != existing.Target))
  890. {
  891. relation = existing;
  892. }
  893. break;
  894. case MergeOption.NoTracking:
  895. throw Error.InvalidOperation(Strings.Context_RelationAlreadyContained);
  896. }
  897. }
  898. else
  899. {
  900. bool collectionProperty = (null != ClientType.Create(source.GetType()).GetProperty(sourceProperty, false).CollectionType);
  901. if (collectionProperty || (null == (existing = this.DetachReferenceLink(source, sourceProperty, target, linkMerge))))
  902. {
  903. this.bindings.Add(relation, relation);
  904. this.IncrementChange(relation);
  905. }
  906. else if (!((MergeOption.AppendOnly == linkMerge) ||
  907. (MergeOption.PreserveChanges == linkMerge && EntityStates.Modified == existing.State)))
  908. {
  909. relation = existing;
  910. }
  911. }
  912. relation.State = EntityStates.Unchanged;
  913. }
  914. internal EntityDescriptor InternalAttachEntityDescriptor(EntityDescriptor descriptor, bool failIfDuplicated)
  915. {
  916. Debug.Assert((null != descriptor.Identity), "must have identity");
  917. Debug.Assert(null != descriptor.Entity && ClientType.Create(descriptor.Entity.GetType()).IsEntityType, "must be entity type to attach");
  918. this.EnsureIdentityToResource();
  919. EntityDescriptor resource;
  920. this.entityDescriptors.TryGetValue(descriptor.Entity, out resource);
  921. EntityDescriptor existing;
  922. this.identityToDescriptor.TryGetValue(descriptor.Identity, out existing);
  923. if (failIfDuplicated && (null != resource))
  924. {
  925. throw Error.InvalidOperation(Strings.Context_EntityAlreadyContained);
  926. }
  927. else if (resource != existing)
  928. {
  929. throw Error.InvalidOperation(Strings.Context_DifferentEntityAlreadyContained);
  930. }
  931. else if (null == resource)
  932. {
  933. resource = descriptor;
  934. this.IncrementChange(descriptor);
  935. this.entityDescriptors.Add(descriptor.Entity, descriptor);
  936. this.identityToDescriptor.Add(descriptor.Identity, descriptor);
  937. }
  938. return resource;
  939. }
  940. #endregion
  941. #if ASTORIA_LIGHT
  942. internal HttpWebRequest CreateRequest(Uri requestUri, string method, bool allowAnyType, string contentType, Version requestVersion, bool sendChunked)
  943. {
  944. return CreateRequest(requestUri, method, allowAnyType, contentType, requestVersion, sendChunked, HttpStack.Auto);
  945. }
  946. #endif
  947. #if !ASTORIA_LIGHT
  948. internal HttpWebRequest CreateRequest(Uri requestUri, string method, bool allowAnyType, string contentType, Version requestVersion, bool sendChunked)
  949. #else
  950. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "sendChunked", Justification = "common parameter not used in silverlight")]
  951. internal HttpWebRequest CreateRequest(Uri requestUri, string method, bool allowAnyType, string contentType, Version requestVersion, bool sendChunked, HttpStack httpStackArg)
  952. #endif
  953. {
  954. Debug.Assert(null != requestUri, "request uri is null");
  955. Debug.Assert(requestUri.IsAbsoluteUri, "request uri is not absolute uri");
  956. Debug.Assert(
  957. requestUri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase) ||
  958. requestUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase),
  959. "request uri is not for HTTP");
  960. Debug.Assert(
  961. Object.ReferenceEquals(XmlConstants.HttpMethodDelete, method) ||
  962. Object.ReferenceEquals(XmlConstants.HttpMethodGet, method) ||
  963. Object.ReferenceEquals(XmlConstants.HttpMethodPost, method) ||
  964. Object.ReferenceEquals(XmlConstants.HttpMethodPut, method) ||
  965. Object.ReferenceEquals(XmlConstants.HttpMethodMerge, method),
  966. "unexpected http method string reference");
  967. #if !ASTORIA_LIGHT
  968. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
  969. #else
  970. if (httpStackArg == HttpStack.Auto)
  971. {
  972. httpStackArg = this.httpStack;
  973. }
  974. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri, httpStackArg);
  975. #endif
  976. #if !ASTORIA_LIGHT
  977. if (null != this.Credentials)
  978. {
  979. request.Credentials = this.Credentials;
  980. }
  981. #endif
  982. #if !ASTORIA_LIGHT
  983. if (0 != this.timeout)
  984. {
  985. request.Timeout = (int)Math.Min(Int32.MaxValue, new TimeSpan(0, 0, this.timeout).TotalMilliseconds);
  986. }
  987. #endif
  988. #if !ASTORIA_LIGHT
  989. request.KeepAlive = true;
  990. #endif
  991. #if !ASTORIA_LIGHT
  992. request.UserAgent = "Microsoft ADO.NET Data Services";
  993. #endif
  994. if (this.UsePostTunneling &&
  995. (!Object.ReferenceEquals(XmlConstants.HttpMethodPost, method)) &&
  996. (!Object.ReferenceEquals(XmlConstants.HttpMethodGet, method)))
  997. {
  998. request.Headers[XmlConstants.HttpXMethod] = method;
  999. request.Method = XmlConstants.HttpMethodPost;
  1000. }
  1001. else
  1002. {
  1003. request.Method = method;
  1004. }
  1005. if (requestVersion != null && requestVersion.Major > 0)
  1006. {
  1007. request.Headers[XmlConstants.HttpDataServiceVersion] = requestVersion.ToString() + Util.VersionSuffix;
  1008. }
  1009. request.Headers[XmlConstants.HttpMaxDataServiceVersion] = Util.MaxResponseVersion.ToString() + Util.VersionSuffix;
  1010. #if !ASTORIA_LIGHT
  1011. if (sendChunked)
  1012. {
  1013. request.SendChunked = true;
  1014. }
  1015. #endif
  1016. if (this.SendingRequest != null)
  1017. {
  1018. System.Net.WebHeaderCollection requestHeaders;
  1019. #if !ASTORIA_LIGHT
  1020. requestHeaders = request.Headers;
  1021. SendingRequestEventArgs args = new SendingRequestEventArgs(request, requestHeaders);
  1022. #else
  1023. requestHeaders = request.CreateEmptyWebHeaderCollection();
  1024. SendingRequestEventArgs args = new SendingRequestEventArgs(null, requestHeaders);
  1025. #endif
  1026. this.SendingRequest(this, args);
  1027. #if !ASTORIA_LIGHT
  1028. if (!Object.ReferenceEquals(args.Request, request))
  1029. {
  1030. request = (System.Net.HttpWebRequest)args.Request;
  1031. }
  1032. #else
  1033. foreach (string key in requestHeaders.AllKeys)
  1034. {
  1035. request.Headers[key] = requestHeaders[key];
  1036. }
  1037. #endif
  1038. }
  1039. request.Accept = allowAnyType ?
  1040. XmlConstants.MimeAny :
  1041. (XmlConstants.MimeApplicationAtom + "," + XmlConstants.MimeApplicationXml);
  1042. request.Headers[HttpRequestHeader.AcceptCharset] = XmlConstants.Utf8Encoding;
  1043. #if !ASTORIA_LIGHT
  1044. bool allowStreamBuffering = false;
  1045. bool removeXMethod = true;
  1046. #endif
  1047. if (!Object.ReferenceEquals(XmlConstants.HttpMethodGet, method))
  1048. {
  1049. Debug.Assert(!String.IsNullOrEmpty(contentType), "Content-Type must be specified for non get operation");
  1050. request.ContentType = contentType;
  1051. if (Object.ReferenceEquals(XmlConstants.HttpMethodDelete, method))
  1052. {
  1053. request.ContentLength = 0;
  1054. }
  1055. #if !ASTORIA_LIGHT
  1056. // else
  1057. {
  1058. allowStreamBuffering = true;
  1059. }
  1060. #endif
  1061. if (this.UsePostTunneling && (!Object.ReferenceEquals(XmlConstants.HttpMethodPost, method)))
  1062. {
  1063. request.Headers[XmlConstants.HttpXMethod] = method;
  1064. method = XmlConstants.HttpMethodPost;
  1065. #if !ASTORIA_LIGHT
  1066. removeXMethod = false;
  1067. #endif
  1068. }
  1069. }
  1070. else
  1071. {
  1072. Debug.Assert(contentType == null, "Content-Type for get methods should be null");
  1073. }
  1074. #if !ASTORIA_LIGHT
  1075. request.AllowWriteStreamBuffering = allowStreamBuffering;
  1076. #endif
  1077. ICollection<string> headers;
  1078. headers = request.Headers.AllKeys;
  1079. #if !ASTORIA_LIGHT
  1080. if (headers.Contains(XmlConstants.HttpRequestIfMatch))
  1081. {
  1082. request.Headers.Remove(HttpRequestHeader.IfMatch);
  1083. }
  1084. #endif
  1085. #if !ASTORIA_LIGHT
  1086. if (removeXMethod && headers.Contains(XmlConstants.HttpXMethod))
  1087. {
  1088. request.Head

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