/// <summary> /// Executes SaveChanges on the specified context and with the default 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> /// <returns>The response from SaveChanges</returns> public static DSClient.DataServiceResponse VerifySaveChanges(this ISaveChangesVerifier verifier, DataServiceContextData contextData, DSClient.DataServiceContext context) { ExceptionUtilities.CheckArgumentNotNull(verifier, "verifier"); ExceptionUtilities.CheckArgumentNotNull(contextData, "contextData"); ExceptionUtilities.CheckArgumentNotNull(context, "context"); return verifier.VerifySaveChanges(contextData, context, null); }
public void FilterCollectionWithAnyAll() { DataServiceContext ctx = new DataServiceContext(new Uri("http://localhost"), ODataProtocolVersion.V4); var values = ctx.CreateQuery<EntityWithCollections>("Values"); var testCases = new[] { new{ q = from e in values where e.CollectionOfInt.Any() select e, url = "Values?$filter=CollectionOfInt/any()" }, new{ q = from e in values where e.CollectionOfInt.Any() && e.ID == 0 select e, url = "Values?$filter=CollectionOfInt/any() and ID eq 0" }, new{ q = from e in values where e.CollectionOfInt.Any(mv => mv == 2 ) select e, url = "Values?$filter=CollectionOfInt/any(mv:mv eq 2)" }, new{ q = from e in values where e.CollectionOfInt.Any(mv => mv > e.ID ) && e.ID <100 select e, url = "Values?$filter=CollectionOfInt/any(mv:mv gt $it/ID) and ID lt 100" }, new{ q = from e in values where e.CollectionOfComplexType.Any(mv => e.CollectionOfString.All(s => s.StartsWith(mv.Name)) || e.ID <100) && e.ID > 50 select e, url = "Values?$filter=CollectionOfComplexType/any(mv:$it/CollectionOfString/all(s:startswith(s,mv/Name)) or $it/ID lt 100) and ID gt 50" }, new{ q = from e in values where e.CollectionOfComplexType.All(mv => mv.Name.StartsWith("a") || e.ID <100) && e.ID > 50 select e, url = "Values?$filter=CollectionOfComplexType/all(mv:startswith(mv/Name,'a') or $it/ID lt 100) and ID gt 50" }, new{ q = from e in values where e.CollectionOfComplexType.All(mv => mv.Name.Contains("a") || mv.Numbers.All(n=>n % 2 == 0)) && e.ID/5 == 3 select e, url = "Values?$filter=CollectionOfComplexType/all(mv:contains(mv/Name,'a') or mv/Numbers/all(n:n mod 2 eq 0)) and ID div 5 eq 3" }, }; TestUtil.RunCombinations(testCases, (testCase) => { Assert.AreEqual(ctx.BaseUri.AbsoluteUri + testCase.url,testCase.q.ToString(), "url == q.ToString()"); }); }
/// <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> /// 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 { #if WINDOWS_PHONE throw new TaupoNotSupportedException("Stream Descriptors are not supported on Windows Phone"); #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; #endif } } // 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; } } }
/// <summary> /// Calculates the expected response from DataServiceContext.SaveChanges based on the context data before saving changes. /// Assumes there are no errors in the response. /// </summary> /// <param name="dataBeforeSaveChanges">The data before saving changes.</param> /// <param name="options">The options for saving changes.</param> /// <param name="context">The DataServiceContext instance which is calling SaveChanges.</param> /// <returns><see cref="DataServiceResponseData"/> that expresses expectations for the response.</returns> public DataServiceResponseData CalculateSaveChangesResponseData(DataServiceContextData dataBeforeSaveChanges, SaveChangesOptions options, DSClient.DataServiceContext context) { ExceptionUtilities.CheckArgumentNotNull(dataBeforeSaveChanges, "dataBeforeSaveChanges"); ExceptionUtilities.CheckArgumentNotNull(context, "context"); DataServiceResponseData responseData = new DataServiceResponseData(); responseData.IsBatchResponse = options == SaveChangesOptions.Batch; bool hasChanges = false; // Note: ordering is important as changes should be processed in the order specified by user. foreach (DescriptorData descriptorData in dataBeforeSaveChanges.GetOrderedChanges()) { int statusCode = (int)HttpStatusCode.NoContent; var entityDescriptorData = descriptorData as EntityDescriptorData; if (entityDescriptorData != null) { if (entityDescriptorData.IsMediaLinkEntry && entityDescriptorData.DefaultStreamState == EntityStates.Modified) { responseData.Add(new ChangeOperationResponseData(descriptorData) { StatusCode = statusCode }); } if (descriptorData.State == EntityStates.Added) { statusCode = GetStatusCodeForInsert(context); if (entityDescriptorData.IsMediaLinkEntry) { responseData.Add(new ChangeOperationResponseData(descriptorData) { StatusCode = statusCode }); statusCode = GetStatusCodeForUpdate(context); } } else if (descriptorData.State == EntityStates.Modified) { statusCode = GetStatusCodeForUpdate(context); } else if (descriptorData.State != EntityStates.Deleted) { continue; } } var linkDescriptorData = descriptorData as LinkDescriptorData; if (linkDescriptorData != null && (linkDescriptorData.State == EntityStates.Added || linkDescriptorData.State == EntityStates.Modified)) { if (!linkDescriptorData.WillTriggerSeparateRequest()) { continue; } } responseData.Add(new ChangeOperationResponseData(descriptorData) { StatusCode = statusCode }); hasChanges = true; } if (responseData.IsBatchResponse) { responseData.BatchStatusCode = hasChanges ? (int)HttpStatusCode.Accepted : 0; } else { responseData.BatchStatusCode = -1; } return responseData; }
private static int GetStatusCodeForUpdate(DSClient.DataServiceContext context) { DataServiceResponsePreference preference = DataServiceResponsePreference.Unspecified; #if !WINDOWS_PHONE preference = context.AddAndUpdateResponsePreference.ToTestEnum(); #endif if (preference == DataServiceResponsePreference.IncludeContent) { return (int)HttpStatusCode.OK; } else { return (int)HttpStatusCode.NoContent; } }
public void AnyAllClientVersionTests() { TestUtil.RunCombinations(ServiceVersion.DataServiceProtocolVersions, maxProtocolVersion => { DataServiceContext ctx = new DataServiceContext(new Uri("http://localhost"), maxProtocolVersion); var movies = ctx.CreateQuery<Movie>("Movies"); var testCases = new[] { new{ q = from m in movies where m.Awards.Any() select m, ErrorMessage = "Error translating Linq expression to URI: The method 'Any' is not supported when MaxProtocolVersion is less than '4.0'." }, new{ q = from m in movies where m.Awards.Any(a => a.ID == 2) select m, ErrorMessage = "Error translating Linq expression to URI: The method 'Any' is not supported when MaxProtocolVersion is less than '4.0'." }, new{ q = from m in movies where m.Titles.Any(t => t.Contains("Space")) select m, ErrorMessage = "Error translating Linq expression to URI: The method 'Any' is not supported when MaxProtocolVersion is less than '4.0'." }, new{ q = from m in movies where m.Actors.OfType<MegaStar>().Any() select m, ErrorMessage = "Error translating Linq expression to URI: The method 'OfType' is not supported when MaxProtocolVersion is less than '4.0'." }, new{ q = from m in movies where m.Director.Awards.All(a => a.Movie == m) select m, ErrorMessage = "Error translating Linq expression to URI: The method 'All' is not supported when MaxProtocolVersion is less than '4.0'." }, }; TestUtil.RunCombinations(testCases, testCase => { string qstr = testCase.q.ToString(); if (maxProtocolVersion < ODataProtocolVersion.V4) { Assert.AreEqual(testCase.ErrorMessage, qstr, "ErrorMessage == qstr"); } else { Assert.IsTrue(qstr.Contains("any(") || qstr.Contains("all("), "qstr should contain any(...) or all(...)"); } }); }); }
public void FilterNavigationWithAnyAll() { DataServiceContext ctx = new DataServiceContext(new Uri("http://localhost"), ODataProtocolVersion.V4); ctx.ResolveName = (type) => { return "NS." + type.Name; }; var movies = ctx.CreateQuery<Movie>("Movies"); var testCases = new[] { new{ q = from m in movies where m.Awards.Any() select m, url = "Movies?$filter=Awards/any()" }, new{ q = from m in movies where m.Awards.Any() && m.ID == 0 select m, url = "Movies?$filter=Awards/any() and ID eq 0" }, new{ q = from m in movies where m.Awards.Any(a => a.ID == 2) select m, url = "Movies?$filter=Awards/any(a:a/ID eq 2)" }, new{ q = from m in movies where m.Awards.Any(a => a.ID == m.ID) select m, url = "Movies?$filter=Awards/any(a:a/ID eq $it/ID)" }, new{ q = from m in movies where m.Director.Awards.All(a => a.Movie == m) select m, url = "Movies?$filter=Director/Awards/all(a:a/Movie eq $it)" }, new{ q = from m in movies where m.Actors.Any(a => a.DirectedMovies.All(dm => dm == m)) select m, url = "Movies?$filter=Actors/any(a:a/DirectedMovies/all(dm:dm eq $it))" }, new{ q = from m in movies where m.Actors.Any(a => a.DirectedMovies.All(dm => dm == m && m.Awards.All(aw=>aw.Movie.Director == dm.Director))) select m, url = "Movies?$filter=Actors/any(a:a/DirectedMovies/all(dm:dm eq $it and $it/Awards/all(aw:aw/Movie/Director eq dm/Director)))" }, new{ q = from m in movies where m.Actors.Any(a => a is MegaStar) select m, url = "Movies?$filter=Actors/any(a:isof(a, 'NS.MegaStar'))" }, new{ q = from m in movies where m.Awards.All(aw => aw.Movie.Director is MegaStar) select m, url = "Movies?$filter=Awards/all(aw:isof(aw/Movie/Director, 'NS.MegaStar'))" }, new{ q = from m in movies where m.Awards.All(aw => m.Director is MegaStar && !aw.Movie.Actors.Any(a=> a is MegaStar)) select m, url = "Movies?$filter=Awards/all(aw:isof($it/Director, 'NS.MegaStar') and not aw/Movie/Actors/any(a:isof(a, 'NS.MegaStar')))" }, new{ q = from m in movies where m.Awards.All(aw => m.Director.FirstName.StartsWith("Hus") && !aw.Movie.Actors.Any(a=> a is MegaStar)) select m, url = "Movies?$filter=Awards/all(aw:startswith($it/Director/FirstName,'Hus') and not aw/Movie/Actors/any(a:isof(a, 'NS.MegaStar')))" }, new{ q = from m in movies where m.Awards.All(aw => m.Director is MegaStar && ((MegaStar)m.Director).MegaStartProp.StartsWith("Hus") && aw.Recepient == m.Director) select m, url = "Movies?$filter=Awards/all(aw:isof($it/Director, 'NS.MegaStar') and startswith(cast($it/Director,'NS.MegaStar')/MegaStartProp,'Hus') and aw/Recepient eq $it/Director)" }, new{ q = from m in movies where m.Actors.OfType<MegaStar>().Any() select m, url = "Movies?$filter=Actors/NS.MegaStar/any()" }, new{ q = from m in movies where m.Actors.OfType<MegaStar>().Any( ms=> ms.Awards.Any()) select m, url = "Movies?$filter=Actors/NS.MegaStar/any(ms:ms/Awards/any())" }, new{ q = from m in movies where m.Actors.OfType<MegaStar>().All( ms=> ms.Awards.Any()) select m, url = "Movies?$filter=Actors/NS.MegaStar/all(ms:ms/Awards/any())" }, new{ q = from m in movies where m.Actors.All(a=> a is MegaStar && a.Awards.Any()) select m, url = "Movies?$filter=Actors/all(a:isof(a, 'NS.MegaStar') and a/Awards/any())" }, new{ q = from m in movies where m.Actors.OfType<MegaStar>().All( ms=> ms.Awards.Any(a=> a.AwardDate > new DateTime(0, DateTimeKind.Utc) && ms.DirectedMovies.Any() && m.Director == ms)) select m, url = "Movies?$filter=Actors/NS.MegaStar/all(ms:ms/Awards/any(a:a/AwardDate gt 0001-01-01T00:00:00Z and ms/DirectedMovies/any() and $it/Director eq ms))" }, new{ q = from m in movies where m.Actors.OfType<MegaStar>().All( ms=> ms.Awards.Any(a=> a.AwardDate > new DateTime(0, DateTimeKind.Utc) && ms.DirectedMovies.Any(dm=> dm.Awards.All(aw=> aw.Recepient.FirstName == ms.FirstName )) && m.Director == ms)) select m, url = "Movies?$filter=Actors/NS.MegaStar/all(ms:ms/Awards/any(a:a/AwardDate gt 0001-01-01T00:00:00Z and ms/DirectedMovies/any(dm:dm/Awards/all(aw:aw/Recepient/FirstName eq ms/FirstName)) and $it/Director eq ms))" }, new{ q = from m in movies where m.Awards.Any(aw => aw.Recepient is MegaStar && m.Actors.OfType<MegaStar>().All(a=>a.DateOfBirth > new DateTime(2010, 1, 1, 0, 0, 0, DateTimeKind.Utc))) select m, url = "Movies?$filter=Awards/any(aw:isof(aw/Recepient, 'NS.MegaStar') and $it/Actors/NS.MegaStar/all(a:a/DateOfBirth gt 2010-01-01T00:00:00Z))" }, }; TestUtil.RunCombinations(testCases, (testCase) => { Assert.AreEqual(ctx.BaseUri.AbsoluteUri + testCase.url, testCase.q.ToString(), "url == q.ToString()"); }); }
/// <summary> /// Creates the data service context data from data service context. /// </summary> /// <param name="context">The context.</param> /// <param name="maxProtocolVersion">The max protocol version.</param> /// <returns>DataServiceContextData based on context</returns> public static DataServiceContextData CreateDataServiceContextDataFromDataServiceContext(DSClient.DataServiceContext context, DataServiceProtocolVersion maxProtocolVersion) { var contextData = new DataServiceContextData(context.GetType(), maxProtocolVersion); // set all the properties on the context data to the values from the context foreach (var productProperty in typeof(DSClient.DataServiceContext).GetProperties(true, false)) { var testProperty = typeof(DataServiceContextData).GetProperty(productProperty.Name, true, false); if (testProperty != null && testProperty.PropertyType.IsAssignableFrom(productProperty.PropertyType)) { var value = productProperty.GetValue(context, null); testProperty.SetValue(contextData, value, null); } } return contextData; }