/// <summary> /// Executes SaveChanges on the specified context and with specified options and verifies the results. /// </summary> /// <param name="verifier">The verifier to use for verification.</param> /// <param name="contextData">The data for the context.</param> /// <param name="context">The context to verify SaveChanges on.</param> /// <param name="options">The options for saving changes.</param> /// <returns>The response from SaveChanges</returns> public static DSClient.DataServiceResponse VerifySaveChanges(this ISaveChangesVerifier verifier, DataServiceContextData contextData, DSClient.DataServiceContext context, SaveChangesOptions?options) { ExceptionUtilities.CheckArgumentNotNull(verifier, "verifier"); ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData"); ExceptionUtilities.CheckArgumentNotNull(context, "context"); DSClient.DataServiceResponse response = null; SyncHelpers.ExecuteActionAndWait(c1 => verifier.VerifySaveChanges(c1, contextData, context, options, (c2, r) => { response = r; c2.Continue(); })); return(response); }
/// <summary> /// process the batch response /// </summary> /// <param name="batchReader">The batch reader to use for reading the batch response.</param> /// <returns>an instance of the DataServiceResponse, containing individual operation responses for this batch request.</returns> /// <remarks> /// The message reader for the entire batch response is stored in the this.batchMessageReader. /// The message reader is disposable, but this method should not dispose it itself. It will be either disposed by the caller (in case of exception) /// or the ownership will be passed to the returned response object (in case of success). /// In could also be diposed indirectly by this method when it enumerates through the responses. /// </remarks> private DataServiceResponse HandleBatchResponseInternal(ODataBatchReader batchReader) { Debug.Assert(this.batchMessageReader != null, "this.batchMessageReader != null"); Debug.Assert(batchReader != null, "batchReader != null"); DataServiceResponse response; HeaderCollection headers = new HeaderCollection(this.batchResponseMessage); IEnumerable <OperationResponse> responses = this.HandleBatchResponse(batchReader); if (this.Queries != null) { // ExecuteBatch, EndExecuteBatch response = new DataServiceResponse( headers, (int)this.batchResponseMessage.StatusCode, responses, true /*batchResponse*/); } else { List <OperationResponse> operationResponses = new List <OperationResponse>(); response = new DataServiceResponse(headers, (int)this.batchResponseMessage.StatusCode, operationResponses, true /*batchResponse*/); Exception exception = null; // SaveChanges, EndSaveChanges // enumerate the entire response foreach (ChangeOperationResponse changeOperationResponse in responses) { operationResponses.Add(changeOperationResponse); if (Util.IsBatchWithSingleChangeset(this.Options) && exception == null && changeOperationResponse.Error != null) { exception = changeOperationResponse.Error; } // Note that this will dispose the enumerator and this release the batch message reader which is owned // by the enumerable of responses by now. } // Note that if we encounter any error in a batch request with a single changeset, // we throw here since all change operations in the changeset are rolled back on the server. // If we encounter any error in a batch request with independent operations, we don't want to throw // since some of the operations might succeed. // Users need to inspect each OperationResponse to get the exception information from the failed operations. if (exception != null) { throw new DataServiceRequestException(Strings.DataServiceException_GeneralError, exception, response); } } return(response); }
/// <summary> /// This method creates a new reservation record for the customer, created by the CreateCustomer method. /// </summary> /// <param name="customer"></param> /// <returns></returns> private static FleetRental CreateReservation(FleetCustomer customer) { Random rnd = new Random(); DataServiceCollection <FleetRental> reservations = new DataServiceCollection <FleetRental>(context); FleetRental reservation = new FleetRental(); reservations.Add(reservation); //Create a new Reservation object with the details for new customer. reservation.CustomerFirstName = customer.FirstName; reservation.CustomerLastName = customer.LastName; reservation.CustomerDriverLicense = customer.DriverLicense; reservation.State = 0; //Limitation of OData, no client-side Enum support reservation.RentalId = "OData_" + rnd.Next(500).ToString(); reservation.Comments = "New customer"; reservation.VehicleId = "Adatum_Four_2"; reservation.VehicleVIN = "WAUXL58E15A104563"; reservation.EndDate = DateTime.Today.AddDays(5); reservation.StartDate = DateTime.Today; Microsoft.OData.Client.DataServiceResponse response = null; try { response = context.SaveChanges(SaveChangesOptions.PostOnlySetProperties); } catch (DataServiceRequestException e) { Console.WriteLine("Error occured while saving. Error Details: " + e.InnerException.Message); } //the following code retrieves the location header which represents the newly created entity Console.ForegroundColor = ConsoleColor.Cyan; foreach (ChangeOperationResponse r in response) { if (r.Headers.ContainsKey("Location")) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("New reservation created: "); Console.WriteLine(r.Headers["Location"]); } } Console.ForegroundColor = ConsoleColor.White; return(reservation); }
/// <summary> /// This method prompts the user for first name and last name and then uses that to create a new customer by calling the Customer OData entity /// </summary> /// <returns></returns> private static FleetCustomer CreateCustomer() { Console.WriteLine("Enter First Name, and then press Enter"); string firstName = Console.ReadLine(); Console.WriteLine("Enter Last Name, and then press Enter "); string lastName = Console.ReadLine(); Random rnd = new Random(); //Create the Customer object with the first and last names provided by the user FleetCustomer newCustomer = new FleetCustomer() { FirstName = firstName, LastName = lastName, DriverLicense = "B923-2381-" + rnd.Next(1000, 7000).ToString(), CellPhone = "0123456789", Email = "*****@*****.**", }; //Add the new customer object to the DataServiceContext object context.AddToFleetCustomers(newCustomer); //Save the DataServiceContext object and an internally a POST call is made to the OData Customer entity. //Record is created in Rainier Database Microsoft.OData.Client.DataServiceResponse response = null; try { response = context.SaveChanges(); } catch (DataServiceRequestException e) { Console.WriteLine("Error occured while saving. Error Details: " + e.InnerException.Message); } //the following code retrieves the location header which represents the newly created entity foreach (ChangeOperationResponse r in response) { if (r.Headers.ContainsKey("Location")) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("New customer created: "); Console.WriteLine(r.Headers["Location"]); } } Console.ForegroundColor = ConsoleColor.White; return(newCustomer); }
/// <summary> /// Executes SaveChanges on the specified context and with specified options and verifies the results. /// </summary> /// <param name="verifier">The verifier to use for verification.</param> /// <param name="contextData">The data for the context.</param> /// <param name="context">The context to verify SaveChanges on.</param> /// <param name="options">The options for saving changes.</param> /// <returns>The response from SaveChanges</returns> public static DSClient.DataServiceResponse VerifySaveChanges(this ISaveChangesVerifier verifier, DataServiceContextData contextData, DSClient.DataServiceContext context, SaveChangesOptions? options) { #if SILVERLIGHT throw new TaupoNotSupportedException("Not supported in Silverlight"); #else ExceptionUtilities.CheckArgumentNotNull(verifier, "verifier"); ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData"); ExceptionUtilities.CheckArgumentNotNull(context, "context"); DSClient.DataServiceResponse response = null; SyncHelpers.ExecuteActionAndWait(c1 => verifier.VerifySaveChanges(c1, contextData, context, options, (c2, r) => { response = r; c2.Continue(); })); return response; #endif }
/// <summary> /// Handle the response. /// </summary> /// <returns>an instance of the DataServiceResponse, containing individual responses for all the requests made during this SaveChanges call.</returns> protected override DataServiceResponse HandleResponse() { List <OperationResponse> responses = new List <OperationResponse>(this.cachedResponses != null ? this.cachedResponses.Count : 0); DataServiceResponse service = new DataServiceResponse(null, -1, responses, false /*isBatch*/); Exception ex = null; try { foreach (CachedResponse response in this.cachedResponses) { Descriptor descriptor = response.Descriptor; this.SaveResultProcessed(descriptor); OperationResponse operationResponse = new ChangeOperationResponse(response.Headers, descriptor); operationResponse.StatusCode = (int)response.StatusCode; if (response.Exception != null) { operationResponse.Error = response.Exception; if (ex == null) { ex = response.Exception; } } else { this.cachedResponse = response; #if DEBUG this.HandleOperationResponse(descriptor, response.Headers, response.StatusCode); #else this.HandleOperationResponse(descriptor, response.Headers); #endif } responses.Add(operationResponse); } } catch (InvalidOperationException e) { ex = e; } if (ex != null) { throw new DataServiceRequestException(Strings.DataServiceException_GeneralError, ex, service); } return(service); }
/// <summary> /// process the batch response /// </summary> /// <returns>an instance of the DataServiceResponse, containing individual operation responses for this batch request.</returns> private DataServiceResponse HandleBatchResponse() { bool batchMessageReaderOwned = true; try { if ((this.batchResponseMessage == null) || (this.batchResponseMessage.StatusCode == (int)HttpStatusCode.NoContent)) { // we always expect a response to our batch POST request throw Error.InvalidOperation(Strings.Batch_ExpectedResponse(1)); } Func <Stream> getResponseStream = () => this.ResponseStream; // We are not going to use the responseVersion returned from this call, as the $batch request itself doesn't apply versioning // of the responses on the root level. The responses are versioned on the part level. (Note that the version on the $batch level // is actually used to version the batch itself, but we for now we only recognize a single version so to keep it backward compatible // we don't check this here. Also note that the HandleResponse method will verify that we can support the version, that is it's // lower than the highest version we understand). Version responseVersion; BaseSaveResult.HandleResponse( this.RequestInfo, (HttpStatusCode)this.batchResponseMessage.StatusCode, // statusCode this.batchResponseMessage.GetHeader(XmlConstants.HttpODataVersion), // responseVersion getResponseStream, // getResponseStream true, // throwOnFailure out responseVersion); if (this.ResponseStream == null) { Error.ThrowBatchExpectedResponse(InternalError.NullResponseStream); } // Create the message and the message reader. this.batchResponseMessage = new HttpWebResponseMessage(new HeaderCollection(this.batchResponseMessage), this.batchResponseMessage.StatusCode, getResponseStream); ODataMessageReaderSettings messageReaderSettings = this.RequestInfo.GetDeserializationInfo(/*mergeOption*/ null).ReadHelper.CreateSettings(); // No need to pass in any model to the batch reader. this.batchMessageReader = new ODataMessageReader(this.batchResponseMessage, messageReaderSettings); ODataBatchReader batchReader; try { batchReader = this.batchMessageReader.CreateODataBatchReader(); } catch (ODataContentTypeException contentTypeException) { string mime; Encoding encoding; Exception inner = contentTypeException; ContentTypeUtil.ReadContentType(this.batchResponseMessage.GetHeader(XmlConstants.HttpContentType), out mime, out encoding); if (String.Equals(XmlConstants.MimeTextPlain, mime)) { inner = GetResponseText( this.batchResponseMessage.GetStream, (HttpStatusCode)this.batchResponseMessage.StatusCode); } throw Error.InvalidOperation(Strings.Batch_ExpectedContentType(this.batchResponseMessage.GetHeader(XmlConstants.HttpContentType)), inner); } DataServiceResponse response = this.HandleBatchResponseInternal(batchReader); // In case of successful processing of at least the beginning of the batch, the message reader is owned by the returned response // (or rather by the IEnumerable of operation responses inside it). // It will be disposed once the operation responses are enumerated (since the IEnumerator should be disposed once used). // In that case we must NOT dispose it here, since that enumeration can exist long after we return from this method. batchMessageReaderOwned = false; return(response); } catch (DataServiceRequestException) { throw; } catch (InvalidOperationException ex) { HeaderCollection headers = new HeaderCollection(this.batchResponseMessage); int statusCode = this.batchResponseMessage == null ? (int)HttpStatusCode.InternalServerError : (int)this.batchResponseMessage.StatusCode; DataServiceResponse response = new DataServiceResponse(headers, statusCode, new OperationResponse[0], this.IsBatchRequest); throw new DataServiceRequestException(Strings.DataServiceException_GeneralError, ex, response); } finally { if (batchMessageReaderOwned) { Util.Dispose(ref this.batchMessageReader); } } }
/// <summary> /// Construct a DataServiceSaveChangesEventArgs object. /// </summary> /// <param name="response">DataServiceContext SaveChanges response</param> public SaveChangesEventArgs(DataServiceResponse response) { this.response = response; }
/// <summary>Initializes a new instance of the <see cref="T:Microsoft.OData.Client.DataServiceRequestException" /> class. </summary> /// <param name="message">Error message text.</param> /// <param name="innerException">Exception object that contains the inner exception.</param> /// <param name="response"><see cref="T:Microsoft.OData.Client.DataServiceResponse" /> object.</param> public DataServiceRequestException(string message, Exception innerException, DataServiceResponse response) : base(message, innerException) { this.response = response; }
/// <summary> /// Tracks the SaveChanges method. /// </summary> /// <param name="data">The data service context data on which to apply state transition.</param> /// <param name="options">The options.</param> /// <param name="response">The response.</param> /// <param name="cachedOperationsFromResponse">The individual operation respones, pre-enumerated and cached.</param> /// <param name="tracker">The entity data change tracker to use</param> public static void TrackSaveChanges(this DataServiceContextData data, SaveChangesOptions options, DSClient.DataServiceResponse response, IEnumerable <DSClient.OperationResponse> cachedOperationsFromResponse, IEntityDescriptorDataChangeTracker tracker) { ExceptionUtilities.CheckArgumentNotNull(data, "data"); ExceptionUtilities.CheckArgumentNotNull(response, "response"); ExceptionUtilities.CheckArgumentNotNull(cachedOperationsFromResponse, "cachedOperationsFromResponse"); ExceptionUtilities.CheckArgumentNotNull(tracker, "tracker"); // Check options and response consistency if ((options & SaveChangesOptions.ContinueOnError) == 0) { ExceptionUtilities.Assert(response.Count(r => r.Error != null) == 0, "Check save changes options and response consistency: no errors in the response when ContinueOnError is off."); } // because some links will not have separate requests, we need to keep track of all link changes var allPendingLinkChanges = data.GetOrderedChanges().OfType <LinkDescriptorData>().ToList(); // go through the pending changes and update the states based on whether the request succeeded foreach (DSClient.ChangeOperationResponse changeResponse in cachedOperationsFromResponse) { DescriptorData descriptorData; LinkDescriptorData linkDescriptorData = null; StreamDescriptorData streamDescriptorData = null; var entityDescriptor = changeResponse.Descriptor as DSClient.EntityDescriptor; if (entityDescriptor != null) { descriptorData = data.GetEntityDescriptorData(entityDescriptor.Entity); } else { var linkDescriptor = changeResponse.Descriptor as DSClient.LinkDescriptor; if (linkDescriptor != null) { linkDescriptorData = data.GetLinkDescriptorData(linkDescriptor.Source, linkDescriptor.SourceProperty, linkDescriptor.Target); descriptorData = linkDescriptorData; allPendingLinkChanges.Remove(linkDescriptorData); } else { // for stream descriptors, we need to find the parent descriptor, then get the stream descriptor data from it var streamDescriptor = (DSClient.StreamDescriptor)changeResponse.Descriptor; entityDescriptor = streamDescriptor.EntityDescriptor; streamDescriptorData = data.GetStreamDescriptorData(entityDescriptor.Entity, streamDescriptor.StreamLink.Name); descriptorData = streamDescriptorData; } } // don't update states for responses that indicate failure if (changeResponse.Error != null) { continue; } // because the request succeeded, make the corresponding updates to the states if (descriptorData.State == EntityStates.Deleted || (linkDescriptorData != null && linkDescriptorData.State == EntityStates.Modified && linkDescriptorData.SourceDescriptor.State == EntityStates.Deleted)) { data.RemoveDescriptorData(descriptorData); } else { // for non-deleted descriptors, we need to update states based on the headers var entityDescriptorData = descriptorData as EntityDescriptorData; if (entityDescriptorData != null) { if (entityDescriptorData.IsMediaLinkEntry && entityDescriptorData.DefaultStreamState == EntityStates.Modified) { entityDescriptorData.DefaultStreamDescriptor.UpdateFromHeaders(changeResponse.Headers); entityDescriptorData.DefaultStreamState = EntityStates.Unchanged; } else { if (entityDescriptorData.IsMediaLinkEntry && entityDescriptorData.DefaultStreamState == EntityStates.Added) { entityDescriptorData.DefaultStreamState = EntityStates.Unchanged; } // because there might have been a reading-entity event for this entity, we need to apply the headers through the tracker tracker.TrackUpdateFromHeaders(entityDescriptorData, changeResponse.Headers); // ensure that all updates are applied before moving to the next response tracker.ApplyPendingUpdates(entityDescriptorData); entityDescriptorData.ParentForInsert = null; entityDescriptorData.ParentPropertyForInsert = null; entityDescriptorData.InsertLink = null; } } else if (streamDescriptorData != null) { streamDescriptorData.UpdateFromHeaders(changeResponse.Headers); } descriptorData.State = EntityStates.Unchanged; } } // go through each link change that did not have an assocatiated response and update its state foreach (var linkDescriptorData in allPendingLinkChanges.OfType <LinkDescriptorData>()) { if (linkDescriptorData.State == EntityStates.Added || linkDescriptorData.State == EntityStates.Modified) { linkDescriptorData.State = EntityStates.Unchanged; } } }
public DataServiceResponseWrapper(DataServiceResponse response) { this._DataServiceResponse = response; }