diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 18c1e7019cc..dc81a206c35 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -8,7 +8,7 @@ updates:
- dependency-name: Castle.Core
- dependency-name: Humanizer.Core
- dependency-name: IdentityServer4.EntityFramework
- - dependency-name: Microsoft.Azure.Cosmos
+ - dependency-name: Azure.Cosmos
- dependency-name: Microsoft.CSharp
- dependency-name: Microsoft.Data.SqlClient
- dependency-name: Microsoft.DotNet.PlatformAbstractions
diff --git a/src/EFCore.Cosmos/EFCore.Cosmos.csproj b/src/EFCore.Cosmos/EFCore.Cosmos.csproj
index 7b4d07c3489..7c65b686b16 100644
--- a/src/EFCore.Cosmos/EFCore.Cosmos.csproj
+++ b/src/EFCore.Cosmos/EFCore.Cosmos.csproj
@@ -21,7 +21,7 @@
-
+
diff --git a/src/EFCore.Cosmos/Extensions/CosmosDatabaseFacadeExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosDatabaseFacadeExtensions.cs
index dc9e8f08d73..f7f015d84a5 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosDatabaseFacadeExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosDatabaseFacadeExtensions.cs
@@ -2,8 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Azure.Cosmos;
using JetBrains.Annotations;
-using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
diff --git a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
index a11e0ab07ec..d84e03d2f59 100644
--- a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
+++ b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
@@ -4,8 +4,8 @@
using System;
using System.ComponentModel;
using System.Net;
+using Azure.Cosmos;
using JetBrains.Annotations;
-using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -59,14 +59,6 @@ public virtual CosmosDbContextOptionsBuilder Region([NotNull] string region)
public virtual CosmosDbContextOptionsBuilder LimitToEndpoint(bool enable = true)
=> WithOption(e => e.WithLimitToEndpoint(Check.NotNull(enable, nameof(enable))));
- ///
- /// Allows optimistic batching of requests to service. Setting this option might impact the latency of the operations.
- /// Hence this option is recommended for non-latency sensitive scenarios only.
- ///
- /// to allow optimistic batching of requests to service.
- public virtual CosmosDbContextOptionsBuilder AllowBulkExecution(bool enable = true)
- => WithOption(e => e.WithAllowBulkExecution(Check.NotNull(enable, nameof(enable))));
-
///
/// Configures the context to use the provided connection mode.
///
@@ -103,20 +95,6 @@ public virtual CosmosDbContextOptionsBuilder OpenTcpConnectionTimeout(TimeSpan t
public virtual CosmosDbContextOptionsBuilder IdleTcpConnectionTimeout(TimeSpan timeout)
=> WithOption(e => e.WithIdleTcpConnectionTimeout(Check.NotNull(timeout, nameof(timeout))));
- ///
- /// Configures the client port reuse policy used by the transport stack.
- ///
- /// The client port reuse policy used by the transport stack.
- public virtual CosmosDbContextOptionsBuilder PortReuseMode(PortReuseMode portReuseMode)
- => WithOption(e => e.WithPortReuseMode(Check.NotNull(portReuseMode, nameof(portReuseMode))));
-
- ///
- /// Configures the context to enable address cache refresh on TCP connection reset notification.
- ///
- /// to enable address cache refresh on TCP connection reset notification.
- public virtual CosmosDbContextOptionsBuilder EnableTcpConnectionEndpointRediscovery(bool enabled = true)
- => WithOption(e => e.WithTcpConnectionEndpointRediscovery(Check.NotNull(enabled, nameof(enabled))));
-
///
/// Configures the maximum number of concurrent connections allowed for the target service endpoint
/// in the Azure Cosmos DB service.
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
index 63f9aea3bf2..e21ab8c5d2b 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
@@ -6,8 +6,8 @@
using System.Globalization;
using System.Net;
using System.Text;
+using Azure.Cosmos;
using JetBrains.Annotations;
-using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
@@ -31,14 +31,11 @@ public class CosmosOptionsExtension : IDbContextOptionsExtension
private string _region;
private ConnectionMode? _connectionMode;
private bool? _limitToEndpoint;
- private bool? _allowBulkExecution;
private Func _executionStrategyFactory;
private IWebProxy _webProxy;
private TimeSpan? _requestTimeout;
private TimeSpan? _openTcpConnectionTimeout;
private TimeSpan? _idleTcpConnectionTimeout;
- private PortReuseMode? _portReuseMode;
- private bool? _enableTcpConnectionEndpointRediscovery;
private int? _gatewayModeMaxConnectionLimit;
private int? _maxTcpConnectionsPerEndpoint;
private int? _maxRequestsPerTcpConnection;
@@ -69,14 +66,11 @@ protected CosmosOptionsExtension([NotNull] CosmosOptionsExtension copyFrom)
_region = copyFrom._region;
_connectionMode = copyFrom._connectionMode;
_limitToEndpoint = copyFrom._limitToEndpoint;
- _allowBulkExecution = copyFrom._allowBulkExecution;
_executionStrategyFactory = copyFrom._executionStrategyFactory;
_webProxy = copyFrom._webProxy;
_requestTimeout = copyFrom._requestTimeout;
_openTcpConnectionTimeout = copyFrom._openTcpConnectionTimeout;
_idleTcpConnectionTimeout = copyFrom._idleTcpConnectionTimeout;
- _portReuseMode = copyFrom._portReuseMode;
- _enableTcpConnectionEndpointRediscovery = copyFrom._enableTcpConnectionEndpointRediscovery;
_gatewayModeMaxConnectionLimit = copyFrom._gatewayModeMaxConnectionLimit;
_maxTcpConnectionsPerEndpoint = copyFrom._maxTcpConnectionsPerEndpoint;
_maxRequestsPerTcpConnection = copyFrom._maxRequestsPerTcpConnection;
@@ -244,29 +238,6 @@ public virtual CosmosOptionsExtension WithLimitToEndpoint(bool enable)
return clone;
}
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual bool? AllowBulkExecution => _allowBulkExecution;
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual CosmosOptionsExtension WithAllowBulkExecution(bool enable)
- {
- var clone = Clone();
-
- clone._allowBulkExecution = enable;
-
- return clone;
- }
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -387,52 +358,6 @@ public virtual CosmosOptionsExtension WithIdleTcpConnectionTimeout(TimeSpan time
return clone;
}
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual PortReuseMode? PortReuseMode => _portReuseMode;
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual CosmosOptionsExtension WithPortReuseMode(PortReuseMode portReuseMode)
- {
- var clone = Clone();
-
- clone._portReuseMode = portReuseMode;
-
- return clone;
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual bool? TcpConnectionEndpointRediscoveryEnabled => _enableTcpConnectionEndpointRediscovery;
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual CosmosOptionsExtension WithTcpConnectionEndpointRediscovery(bool enable)
- {
- var clone = Clone();
-
- clone._enableTcpConnectionEndpointRediscovery = enable;
-
- return clone;
- }
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -584,13 +509,10 @@ public override long GetServiceProviderHashCode()
hashCode = (hashCode * 397) ^ (Extension._region?.GetHashCode() ?? 0);
hashCode = (hashCode * 3) ^ (Extension._connectionMode?.GetHashCode() ?? 0);
hashCode = (hashCode * 3) ^ (Extension._limitToEndpoint?.GetHashCode() ?? 0);
- hashCode = (hashCode * 3) ^ (Extension._allowBulkExecution?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (Extension._webProxy?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (Extension._requestTimeout?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (Extension._openTcpConnectionTimeout?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (Extension._idleTcpConnectionTimeout?.GetHashCode() ?? 0);
- hashCode = (hashCode * 3) ^ (Extension._portReuseMode?.GetHashCode() ?? 0);
- hashCode = (hashCode * 3) ^ (Extension._enableTcpConnectionEndpointRediscovery?.GetHashCode() ?? 0);
hashCode = (hashCode * 131) ^ (Extension._gatewayModeMaxConnectionLimit?.GetHashCode() ?? 0);
hashCode = (hashCode * 397) ^ (Extension._maxTcpConnectionsPerEndpoint?.GetHashCode() ?? 0);
hashCode = (hashCode * 131) ^ (Extension._maxRequestsPerTcpConnection?.GetHashCode() ?? 0);
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs
index 58222c95e34..39282b5b194 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs
@@ -3,6 +3,7 @@
using System;
using System.Net;
+using Azure.Cosmos;
using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -65,14 +66,6 @@ public class CosmosSingletonOptions : ICosmosSingletonOptions
///
public virtual bool? LimitToEndpoint { get; private set; }
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual bool? AllowBulkExecution { get; private set; }
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -113,22 +106,6 @@ public class CosmosSingletonOptions : ICosmosSingletonOptions
///
public virtual TimeSpan? IdleTcpConnectionTimeout { get; private set; }
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual PortReuseMode? PortReuseMode { get; private set; }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual bool? TcpConnectionEndpointRediscoveryEnabled { get; private set; }
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -169,14 +146,11 @@ public virtual void Initialize(IDbContextOptions options)
ConnectionString = cosmosOptions.ConnectionString;
Region = cosmosOptions.Region;
LimitToEndpoint = cosmosOptions.LimitToEndpoint;
- AllowBulkExecution = cosmosOptions.AllowBulkExecution;
ConnectionMode = cosmosOptions.ConnectionMode;
WebProxy = cosmosOptions.WebProxy;
RequestTimeout = cosmosOptions.RequestTimeout;
OpenTcpConnectionTimeout = cosmosOptions.OpenTcpConnectionTimeout;
IdleTcpConnectionTimeout = cosmosOptions.IdleTcpConnectionTimeout;
- PortReuseMode = cosmosOptions.PortReuseMode;
- TcpConnectionEndpointRediscoveryEnabled = cosmosOptions.TcpConnectionEndpointRediscoveryEnabled;
GatewayModeMaxConnectionLimit = cosmosOptions.GatewayModeMaxConnectionLimit;
MaxTcpConnectionsPerEndpoint = cosmosOptions.MaxTcpConnectionsPerEndpoint;
MaxRequestsPerTcpConnection = cosmosOptions.MaxRequestsPerTcpConnection;
@@ -199,14 +173,11 @@ public virtual void Validate(IDbContextOptions options)
|| ConnectionString != cosmosOptions.ConnectionString
|| Region != cosmosOptions.Region
|| LimitToEndpoint != cosmosOptions.LimitToEndpoint
- || AllowBulkExecution != cosmosOptions.AllowBulkExecution
|| ConnectionMode != cosmosOptions.ConnectionMode
|| WebProxy != cosmosOptions.WebProxy
|| RequestTimeout != cosmosOptions.RequestTimeout
|| OpenTcpConnectionTimeout != cosmosOptions.OpenTcpConnectionTimeout
|| IdleTcpConnectionTimeout != cosmosOptions.IdleTcpConnectionTimeout
- || PortReuseMode != cosmosOptions.PortReuseMode
- || TcpConnectionEndpointRediscoveryEnabled != cosmosOptions.TcpConnectionEndpointRediscoveryEnabled
|| GatewayModeMaxConnectionLimit != cosmosOptions.GatewayModeMaxConnectionLimit
|| MaxTcpConnectionsPerEndpoint != cosmosOptions.MaxTcpConnectionsPerEndpoint
|| MaxRequestsPerTcpConnection != cosmosOptions.MaxRequestsPerTcpConnection))
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs
index 50cb57c5db2..547c52aea77 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/ICosmosSingletonOptions.cs
@@ -3,7 +3,7 @@
using System;
using System.Net;
-using Microsoft.Azure.Cosmos;
+using Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
@@ -65,14 +65,6 @@ public interface ICosmosSingletonOptions : ISingletonOptions
///
bool? LimitToEndpoint { get; }
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- bool? AllowBulkExecution { get; }
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -113,22 +105,6 @@ public interface ICosmosSingletonOptions : ISingletonOptions
///
TimeSpan? IdleTcpConnectionTimeout { get; }
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- PortReuseMode? PortReuseMode { get; }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- bool? TcpConnectionEndpointRediscoveryEnabled { get; }
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs b/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs
index dd566eac06f..6ffc3ab7a77 100644
--- a/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs
@@ -6,6 +6,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Text;
+using System.Text.Json;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
@@ -363,6 +364,11 @@ protected override Expression VisitSqlConstant(SqlConstantExpression sqlConstant
private JToken GenerateJToken(object value, CoreTypeMapping typeMapping)
{
+ if (value == null)
+ {
+ return null;
+ }
+
value = ConvertUnderlyingEnumValueToEnum(value, typeMapping.ClrType);
var converter = typeMapping.Converter;
@@ -371,18 +377,9 @@ private JToken GenerateJToken(object value, CoreTypeMapping typeMapping)
value = converter.ConvertToProvider(value);
}
- if (value == null)
- {
- return null;
- }
-
return (value as JToken) ?? JToken.FromObject(value, CosmosClientWrapper.Serializer);
}
- // Enum when compared to constant will always have value of integral type
- // when enum would contain convert node. We remove the convert node but we also
- // need to convert the integral value to enum value.
- // This allows us to use converter on enum value or print enum value directly if supported by provider
private object ConvertUnderlyingEnumValueToEnum(object value, Type clrType)
=> value?.GetType().IsInteger() == true && clrType.UnwrapNullableType().IsEnum
? Enum.ToObject(clrType.UnwrapNullableType(), value)
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
index a92f3500aed..72de1c6bf3e 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
@@ -11,8 +11,9 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Azure;
+using Azure.Cosmos;
using JetBrains.Annotations;
-using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
@@ -50,7 +51,7 @@ public class CosmosClientWrapper
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public static readonly JsonSerializer Serializer = new JsonSerializer();
+ public static readonly JsonSerializer Serializer = JsonSerializer.Create();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -139,7 +140,7 @@ public virtual async Task CreateDatabaseIfNotExistsOnceAsync(
var response = await Client.CreateDatabaseIfNotExistsAsync(_databaseId, cancellationToken: cancellationToken)
.ConfigureAwait(false);
- return response.StatusCode == HttpStatusCode.Created;
+ return response.GetRawResponse().Status == (int)HttpStatusCode.Created;
}
///
@@ -186,13 +187,13 @@ public virtual async Task DeleteDatabaseOnceAsync(
{
using var response = await Client.GetDatabase(_databaseId).DeleteStreamAsync(cancellationToken: cancellationToken)
.ConfigureAwait(false);
- if (response.StatusCode == HttpStatusCode.NotFound)
+ if (response.Status == (int)HttpStatusCode.NotFound)
{
return false;
}
- response.EnsureSuccessStatusCode();
- return response.StatusCode == HttpStatusCode.NoContent;
+ EnsureSuccessStatusCode(response);
+ return response.Status == (int)HttpStatusCode.NoContent;
}
///
@@ -237,13 +238,13 @@ private async Task CreateContainerIfNotExistsOnceAsync(
},
cancellationToken: cancellationToken)
.ConfigureAwait(false);
- if (response.StatusCode == HttpStatusCode.Conflict)
+ if (response.Status == (int)HttpStatusCode.Conflict)
{
return false;
}
- response.EnsureSuccessStatusCode();
- return response.StatusCode == HttpStatusCode.Created;
+ EnsureSuccessStatusCode(response);
+ return response.Status == (int)HttpStatusCode.Created;
}
///
@@ -286,7 +287,7 @@ private async Task CreateItemOnceAsync(
await using var stream = new MemoryStream();
await using var writer = new StreamWriter(stream, new UTF8Encoding(), bufferSize: 1024, leaveOpen: false);
using var jsonWriter = new JsonTextWriter(writer);
- JsonSerializer.Create().Serialize(jsonWriter, parameters.Document);
+ Serializer.Serialize(jsonWriter, parameters.Document);
await jsonWriter.FlushAsync(cancellationToken).ConfigureAwait(false);
var entry = parameters.Entry;
@@ -298,7 +299,7 @@ private async Task CreateItemOnceAsync(
.ConfigureAwait(false);
ProcessResponse(response, entry);
- return response.StatusCode == HttpStatusCode.Created;
+ return response.Status == (int)HttpStatusCode.Created;
}
///
@@ -348,7 +349,7 @@ private async Task ReplaceItemOnceAsync(
using var stream = new MemoryStream();
using var writer = new StreamWriter(stream, new UTF8Encoding(), bufferSize: 1024, leaveOpen: false);
using var jsonWriter = new JsonTextWriter(writer);
- JsonSerializer.Create().Serialize(jsonWriter, parameters.Document);
+ Serializer.Serialize(jsonWriter, parameters.Document);
await jsonWriter.FlushAsync(cancellationToken).ConfigureAwait(false);
var entry = parameters.Entry;
@@ -361,7 +362,7 @@ private async Task ReplaceItemOnceAsync(
.ConfigureAwait(false);
ProcessResponse(response, entry);
- return response.StatusCode == HttpStatusCode.OK;
+ return response.Status == (int)HttpStatusCode.OK;
}
///
@@ -423,7 +424,7 @@ public virtual async Task DeleteItemOnceAsync(
.ConfigureAwait(false);
ProcessResponse(response, entry);
- return response.StatusCode == HttpStatusCode.NoContent;
+ return response.Status == (int)HttpStatusCode.NoContent;
}
private static ItemRequestOptions CreateItemRequestOptions(IUpdateEntry entry)
@@ -441,7 +442,7 @@ private static ItemRequestOptions CreateItemRequestOptions(IUpdateEntry entry)
etag = converter.ConvertToProvider(etag);
}
- return new ItemRequestOptions { IfMatchEtag = (string)etag };
+ return new ItemRequestOptions { IfMatch = new ETag((string)etag) };
}
private static PartitionKey CreatePartitionKey(IUpdateEntry entry)
@@ -463,25 +464,25 @@ private static PartitionKey CreatePartitionKey(IUpdateEntry entry)
return partitionKey == null ? PartitionKey.None : new PartitionKey((string)partitionKey);
}
- private static void ProcessResponse(ResponseMessage response, IUpdateEntry entry)
+ private static void ProcessResponse(Response response, IUpdateEntry entry)
{
- response.EnsureSuccessStatusCode();
+ EnsureSuccessStatusCode(response);
var etagProperty = entry.EntityType.GetETagProperty();
if (etagProperty != null && entry.EntityState != EntityState.Deleted)
{
- entry.SetStoreGeneratedValue(etagProperty, response.Headers.ETag);
+ entry.SetStoreGeneratedValue(etagProperty, response.Headers.ETag?.ToString());
}
var jObjectProperty = entry.EntityType.FindProperty(StoreKeyConvention.JObjectPropertyName);
if (jObjectProperty != null
&& jObjectProperty.ValueGenerated == ValueGenerated.OnAddOrUpdate
- && response.Content != null)
+ && response.ContentStream != null)
{
- using var responseStream = response.Content;
+ using var responseStream = response.ContentStream;
using var reader = new StreamReader(responseStream);
using var jsonReader = new JsonTextReader(reader);
- var createdDocument = new JsonSerializer().Deserialize(jsonReader);
+ var createdDocument = Serializer.Deserialize(jsonReader);
entry.SetStoreGeneratedValue(jObjectProperty, createdDocument);
}
@@ -559,20 +560,20 @@ internal virtual async Task ExecuteReadItemAsync(
return JObjectFromReadItemResponseMessage(responseMessage);
}
- private static JObject JObjectFromReadItemResponseMessage(ResponseMessage responseMessage)
+ private static JObject JObjectFromReadItemResponseMessage(Response response)
{
- responseMessage.EnsureSuccessStatusCode();
+ EnsureSuccessStatusCode(response);
- var responseStream = responseMessage.Content;
+ var responseStream = response.ContentStream;
var reader = new StreamReader(responseStream);
var jsonReader = new JsonTextReader(reader);
- var jObject = new JsonSerializer().Deserialize(jsonReader);
+ var jObject = Serializer.Deserialize(jsonReader);
return new JObject(new JProperty("c", jObject));
}
- private FeedIterator CreateQuery(
+ private IAsyncEnumerable CreateQuery(
string containerId,
string partitionKey,
CosmosSqlQuery query)
@@ -594,7 +595,7 @@ private FeedIterator CreateQuery(
return container.GetItemQueryStreamIterator(queryDefinition, requestOptions: queryRequestOptions);
}
- private async Task CreateSingleItemQuery(
+ private async Task CreateSingleItemQuery(
string containerId,
string partitionKey,
string resourceId,
@@ -641,13 +642,23 @@ private static bool TryReadJObject(JsonTextReader jsonReader, out JObject jObjec
{
if (jsonReader.TokenType == JsonToken.StartObject)
{
- jObject = new JsonSerializer().Deserialize(jsonReader);
+ jObject = Serializer.Deserialize(jsonReader);
return true;
}
}
+
return false;
}
+ private static void EnsureSuccessStatusCode(Response response)
+ {
+ var httpStatusCode = response.Status;
+ if (httpStatusCode < 200 || httpStatusCode > 299)
+ {
+ throw new HttpException(response);
+ }
+ }
+
private sealed class DocumentEnumerable : IEnumerable
{
private readonly CosmosClientWrapper _cosmosClient;
@@ -678,12 +689,12 @@ private sealed class Enumerator : IEnumerator
private readonly string _partitionKey;
private readonly CosmosSqlQuery _cosmosSqlQuery;
- private ResponseMessage _responseMessage;
+ private Response _response;
private Stream _responseStream;
private StreamReader _reader;
private JsonTextReader _jsonReader;
- private FeedIterator _query;
+ private IAsyncEnumerator _query;
public Enumerator(DocumentEnumerable documentEnumerable)
{
@@ -701,18 +712,18 @@ public bool MoveNext()
{
if (_jsonReader == null)
{
- _query ??= _cosmosClientWrapper.CreateQuery(_containerId, _partitionKey, _cosmosSqlQuery);
-
- if (!_query.HasMoreResults)
+ _query ??= _cosmosClientWrapper.CreateQuery(_containerId, _partitionKey, _cosmosSqlQuery).GetAsyncEnumerator();
+
+ if (!_query.MoveNextAsync().AsTask().GetAwaiter().GetResult())
{
Current = default;
return false;
}
- _responseMessage = _query.ReadNextAsync().GetAwaiter().GetResult();
- _responseMessage.EnsureSuccessStatusCode();
+ _response = _query.Current;
+ EnsureSuccessStatusCode(_response);
- _responseStream = _responseMessage.Content;
+ _responseStream = _response.ContentStream;
_reader = new StreamReader(_responseStream);
_jsonReader = CreateJsonReader(_reader);
}
@@ -742,8 +753,8 @@ public void Dispose()
{
ResetRead();
- _responseMessage?.Dispose();
- _responseMessage = null;
+ _response?.Dispose();
+ _response = null;
}
public void Reset() => throw new NotImplementedException();
@@ -780,12 +791,12 @@ private sealed class AsyncEnumerator : IAsyncEnumerator
private readonly CosmosSqlQuery _cosmosSqlQuery;
private readonly CancellationToken _cancellationToken;
- private ResponseMessage _responseMessage;
+ private Response _response;
private Stream _responseStream;
private StreamReader _reader;
private JsonTextReader _jsonReader;
- private FeedIterator _query;
+ private IAsyncEnumerator _query;
public JObject Current { get; private set; }
@@ -805,18 +816,18 @@ public async ValueTask MoveNextAsync()
if (_jsonReader == null)
{
- _query ??= _cosmosClientWrapper.CreateQuery(_containerId, _partitionKey, _cosmosSqlQuery);
+ _query ??= _cosmosClientWrapper.CreateQuery(_containerId, _partitionKey, _cosmosSqlQuery).GetAsyncEnumerator(_cancellationToken);
- if (!_query.HasMoreResults)
+ if (!await _query.MoveNextAsync().ConfigureAwait(false))
{
Current = default;
return false;
}
- _responseMessage = await _query.ReadNextAsync(_cancellationToken).ConfigureAwait(false);
- _responseMessage.EnsureSuccessStatusCode();
+ _response = _query.Current;
+ EnsureSuccessStatusCode(_response);
- _responseStream = _responseMessage.Content;
+ _responseStream = _response.ContentStream;
_reader = new StreamReader(_responseStream);
_jsonReader = CreateJsonReader(_reader);
}
@@ -846,8 +857,8 @@ public async ValueTask DisposeAsync()
{
await ResetReadAsync().ConfigureAwait(false);
- await _responseMessage.DisposeAsyncIfAvailable().ConfigureAwait(false);
- _responseMessage = null;
+ await _response.DisposeAsyncIfAvailable().ConfigureAwait(false);
+ _response = null;
}
}
}
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseWrapper.cs
index fb1a06f6ace..e9e85f4dc5f 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseWrapper.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseWrapper.cs
@@ -6,8 +6,8 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using Azure.Cosmos;
using JetBrains.Annotations;
-using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Metadata.Internal;
@@ -114,9 +114,13 @@ public override int SaveChanges(IList entries)
rowsAffected++;
}
}
- catch (CosmosException ex)
+ catch (CosmosException cosmosException)
{
- throw ThrowUpdateException(ex, entry);
+ throw ThrowUpdateException(cosmosException, entry);
+ }
+ catch (HttpException httpException)
+ {
+ throw ThrowUpdateException(httpException, entry);
}
}
@@ -177,9 +181,13 @@ public override async Task SaveChangesAsync(
rowsAffected++;
}
}
- catch (CosmosException ex)
+ catch (CosmosException cosmosException)
+ {
+ throw ThrowUpdateException(cosmosException, entry);
+ }
+ catch (HttpException httpException)
{
- throw ThrowUpdateException(ex, entry);
+ throw ThrowUpdateException(httpException, entry);
}
}
@@ -384,10 +392,26 @@ private Exception ThrowUpdateException(CosmosException exception, IUpdateEntry e
{
var documentSource = GetDocumentSource(entry.EntityType);
var id = documentSource.GetId(entry.SharedIdentityEntry ?? entry);
- throw exception.StatusCode switch
+ throw exception.Status switch
+ {
+ (int)HttpStatusCode.PreconditionFailed =>
+ new DbUpdateConcurrencyException(CosmosStrings.UpdateConflict(id), exception, new[] { entry }),
+ (int)HttpStatusCode.Conflict =>
+ new DbUpdateException(CosmosStrings.UpdateConflict(id), exception, new[] { entry }),
+ _ => Rethrow(exception),
+ };
+ }
+
+ private Exception ThrowUpdateException(HttpException exception, IUpdateEntry entry)
+ {
+ var documentSource = GetDocumentSource(entry.EntityType);
+ var id = documentSource.GetId(entry.SharedIdentityEntry ?? entry);
+ throw exception.Response.Status switch
{
- HttpStatusCode.PreconditionFailed => new DbUpdateConcurrencyException(CosmosStrings.UpdateConflict(id), exception, new[] { entry }),
- HttpStatusCode.Conflict => new DbUpdateException(CosmosStrings.UpdateConflict(id), exception, new[] { entry }),
+ (int)HttpStatusCode.PreconditionFailed =>
+ new DbUpdateConcurrencyException(CosmosStrings.UpdateConflict(id), exception, new[] { entry }),
+ (int)HttpStatusCode.Conflict =>
+ new DbUpdateException(CosmosStrings.UpdateConflict(id), exception, new[] { entry }),
_ => Rethrow(exception),
};
}
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosExecutionStrategy.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosExecutionStrategy.cs
index 325d7ec9903..ac58c510e2d 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosExecutionStrategy.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosExecutionStrategy.cs
@@ -5,8 +5,8 @@
using System.Globalization;
using System.Linq;
using System.Net;
+using Azure.Cosmos;
using JetBrains.Annotations;
-using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Storage;
namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal
@@ -101,24 +101,24 @@ protected override bool ShouldRetryOn(Exception exception)
{
if (exception is CosmosException cosmosException)
{
- return IsTransient(cosmosException.StatusCode);
+ return IsTransient(cosmosException.Status);
}
if (exception is HttpException httpException)
{
- return IsTransient(httpException.Response.StatusCode);
+ return IsTransient(httpException.Response.Status);
}
if (exception is WebException webException)
{
- return IsTransient(((HttpWebResponse)webException.Response).StatusCode);
+ return IsTransient((int)((HttpWebResponse)webException.Response).StatusCode);
}
return false;
- static bool IsTransient(HttpStatusCode statusCode)
- => statusCode == HttpStatusCode.ServiceUnavailable
- || statusCode == HttpStatusCode.TooManyRequests;
+ static bool IsTransient(int status)
+ => status == (int)HttpStatusCode.ServiceUnavailable
+ || status == (int)HttpStatusCode.TooManyRequests;
}
///
@@ -130,12 +130,9 @@ static bool IsTransient(HttpStatusCode statusCode)
protected override TimeSpan? GetNextDelay(Exception lastException)
{
var baseDelay = base.GetNextDelay(lastException);
- if (baseDelay == null)
- {
- return null;
- }
-
- return CallOnWrappedException(lastException, GetDelayFromException)
+ return baseDelay == null
+ ? null
+ : CallOnWrappedException(lastException, GetDelayFromException)
?? baseDelay;
}
@@ -143,54 +140,93 @@ static bool IsTransient(HttpStatusCode statusCode)
{
if (exception is CosmosException cosmosException)
{
- return cosmosException.RetryAfter;
+ if (cosmosException.TryGetHeader("x-ms-retry-after-ms", out var delayString)
+ && TryParseMsRetryAfter(delayString, out var delay))
+ {
+ return delay;
+ }
+
+ if (cosmosException.TryGetHeader("Retry-After", out delayString)
+ && TryParseRetryAfter(delayString, out delay))
+ {
+ return delay;
+ }
}
if (exception is HttpException httpException)
{
- if (httpException.Response.Headers.TryGetValues("x-ms-retry-after-ms", out var values))
+ if (httpException.Response.Headers.TryGetValues("x-ms-retry-after-ms", out var values)
+ && TryParseMsRetryAfter(values.FirstOrDefault(), out var delay))
{
- var delayString = values.Single();
- return TimeSpan.FromMilliseconds(int.Parse(delayString));
+ return delay;
}
- var retryDate = httpException.Response.Headers.RetryAfter.Date;
- if (retryDate != null)
+ if (httpException.Response.Headers.TryGetValues("Retry-After", out values)
+ && TryParseRetryAfter(values.FirstOrDefault(), out delay))
{
- var delay = retryDate.Value.Subtract(DateTime.Now);
- return delay <= TimeSpan.Zero ? TimeSpan.FromMilliseconds(1) : delay;
+ return delay;
}
-
- return httpException.Response.Headers.RetryAfter.Delta;
}
if (exception is WebException webException)
{
var response = (HttpWebResponse)webException.Response;
- var delayString = response.Headers.GetValues("x-ms-retry-after-ms")?.Single();
- if (delayString != null)
+ var delayString = response.Headers.GetValues("x-ms-retry-after-ms")?.FirstOrDefault();
+ if (TryParseMsRetryAfter(delayString, out var delay))
{
- return TimeSpan.FromMilliseconds(int.Parse(delayString));
+ return delay;
}
- delayString = response.Headers.GetValues("Retry-After")?.Single();
- if (delayString != null)
+ delayString = response.Headers.GetValues("Retry-After")?.FirstOrDefault();
+ if (TryParseRetryAfter(delayString, out delay))
{
- if (int.TryParse(delayString, out var intDelay))
- {
- return TimeSpan.FromSeconds(intDelay);
- }
-
- if (DateTime.TryParse(delayString, CultureInfo.InvariantCulture, DateTimeStyles.None, out var retryDate))
- {
- var delay = retryDate.Subtract(DateTime.Now);
- return delay <= TimeSpan.Zero ? TimeSpan.FromMilliseconds(1) : delay;
- }
+ return delay;
}
}
return null;
+
+ static bool TryParseMsRetryAfter(string delayString, out TimeSpan delay)
+ {
+ delay = default;
+ if (delayString == null)
+ {
+ return false;
+ }
+
+ if (int.TryParse(delayString, out var intDelay))
+ {
+ delay = TimeSpan.FromMilliseconds(intDelay);
+ return true;
+ }
+
+ return false;
+ }
+
+ static bool TryParseRetryAfter(string delayString, out TimeSpan delay)
+ {
+ delay = default;
+ if (delayString == null)
+ {
+ return false;
+ }
+
+ if (int.TryParse(delayString, out var intDelay))
+ {
+ delay = TimeSpan.FromSeconds(intDelay);
+ return true;
+ }
+
+ if (DateTimeOffset.TryParse(delayString, CultureInfo.InvariantCulture, DateTimeStyles.None, out var retryDate))
+ {
+ delay = retryDate.Subtract(DateTimeOffset.Now);
+ delay = delay <= TimeSpan.Zero ? TimeSpan.FromMilliseconds(1) : delay;
+ return true;
+ }
+
+ return false;
+ }
}
}
}
diff --git a/src/EFCore.Cosmos/Storage/Internal/HttpException.cs b/src/EFCore.Cosmos/Storage/Internal/HttpException.cs
index 3e4371a3525..282b8f6ccf0 100644
--- a/src/EFCore.Cosmos/Storage/Internal/HttpException.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/HttpException.cs
@@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Net.Http;
+using Azure;
using JetBrains.Annotations;
namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal
@@ -21,8 +21,8 @@ public class HttpException : Exception
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public HttpException([NotNull] HttpResponseMessage response)
- : base(response.StatusCode.ToString())
+ public HttpException([NotNull] Response response)
+ : base(response.ReasonPhrase)
{
// An error occurred while sending the request.
Response = response;
@@ -34,6 +34,6 @@ public HttpException([NotNull] HttpResponseMessage response)
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public virtual HttpResponseMessage Response { get; }
+ public virtual Response Response { get; }
}
}
diff --git a/src/EFCore.Cosmos/Storage/Internal/JsonCosmosSerializer.cs b/src/EFCore.Cosmos/Storage/Internal/JsonCosmosSerializer.cs
new file mode 100644
index 00000000000..984e594da29
--- /dev/null
+++ b/src/EFCore.Cosmos/Storage/Internal/JsonCosmosSerializer.cs
@@ -0,0 +1,56 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.IO;
+using System.Text;
+using Azure.Cosmos.Serialization;
+using Newtonsoft.Json;
+
+namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public class JsonCosmosSerializer : CosmosSerializer
+ {
+ private static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true);
+
+ ///
+ public override T FromStream(Stream stream)
+ {
+ using (stream)
+ {
+ if (typeof(Stream).IsAssignableFrom(typeof(T)))
+ {
+ return (T)(object)stream;
+ }
+
+ using var streamReader = new StreamReader(stream);
+ using var jsonTextReader = new JsonTextReader(streamReader);
+ return GetSerializer().Deserialize(jsonTextReader);
+ }
+ }
+
+ ///
+ public override Stream ToStream(T input)
+ {
+ var streamPayload = new MemoryStream();
+ using (var streamWriter = new StreamWriter(streamPayload, encoding: DefaultEncoding, bufferSize: 1024, leaveOpen: true))
+ {
+ using var jsonTextWriter = new JsonTextWriter(streamWriter);
+ jsonTextWriter.Formatting = Formatting.None;
+ GetSerializer().Serialize(jsonTextWriter, input);
+ jsonTextWriter.Flush();
+ streamWriter.Flush();
+ }
+
+ streamPayload.Position = 0;
+ return streamPayload;
+ }
+
+ private JsonSerializer GetSerializer() => CosmosClientWrapper.Serializer;
+ }
+}
diff --git a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs
index b766938fd5e..9a7a7689cb0 100644
--- a/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/SingletonCosmosClientWrapper.cs
@@ -2,8 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Azure.Cosmos;
using JetBrains.Annotations;
-using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
@@ -46,7 +46,8 @@ public SingletonCosmosClientWrapper([NotNull] ICosmosSingletonOptions options)
_connectionString = options.ConnectionString;
var configuration = new CosmosClientOptions
{
- ApplicationName = _userAgent
+ ApplicationName = _userAgent,
+ Serializer = new JsonCosmosSerializer()
};
if (options.Region != null)
@@ -59,11 +60,6 @@ public SingletonCosmosClientWrapper([NotNull] ICosmosSingletonOptions options)
configuration.LimitToEndpoint = options.LimitToEndpoint.Value;
}
- if (options.AllowBulkExecution != null)
- {
- configuration.AllowBulkExecution = options.AllowBulkExecution.Value;
- }
-
if (options.ConnectionMode != null)
{
configuration.ConnectionMode = options.ConnectionMode.Value;
@@ -89,16 +85,6 @@ public SingletonCosmosClientWrapper([NotNull] ICosmosSingletonOptions options)
configuration.IdleTcpConnectionTimeout = options.IdleTcpConnectionTimeout.Value;
}
- if (options.PortReuseMode != null)
- {
- configuration.PortReuseMode = options.PortReuseMode.Value;
- }
-
- if (options.TcpConnectionEndpointRediscoveryEnabled != null)
- {
- configuration.EnableTcpConnectionEndpointRediscovery = options.TcpConnectionEndpointRediscoveryEnabled.Value;
- }
-
if (options.GatewayModeMaxConnectionLimit != null)
{
configuration.GatewayModeMaxConnectionLimit = options.GatewayModeMaxConnectionLimit.Value;
diff --git a/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs b/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs
index 4d2ad67c164..92d747be4b1 100644
--- a/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs
+++ b/src/EFCore.Cosmos/Update/Internal/DocumentSource.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Collections;
using System.Linq;
using JetBrains.Annotations;
@@ -325,7 +326,10 @@ private static JToken ConvertPropertyValue(IProperty property, object value)
return null;
}
- var converter = property.GetTypeMapping().Converter;
+ var typeMapping = property.GetTypeMapping();
+ value = ConvertUnderlyingEnumValueToEnum(value, typeMapping.ClrType);
+
+ var converter = typeMapping.Converter;
if (converter != null)
{
value = converter.ConvertToProvider(value);
@@ -333,5 +337,10 @@ private static JToken ConvertPropertyValue(IProperty property, object value)
return (value as JToken) ?? JToken.FromObject(value, CosmosClientWrapper.Serializer);
}
+
+ private static object ConvertUnderlyingEnumValueToEnum(object value, Type clrType)
+ => value?.GetType().IsInteger() == true && clrType.UnwrapNullableType().IsEnum
+ ? Enum.ToObject(clrType.UnwrapNullableType(), value)
+ : value;
}
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs
index f0a8d16bcb9..9677cdeb951 100644
--- a/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/ConfigPatternsCosmosTest.cs
@@ -3,7 +3,7 @@
using System;
using System.Threading.Tasks;
-using Microsoft.Azure.Cosmos;
+using Azure.Cosmos;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;
diff --git a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs
index 80a7eb1285a..4802d2d8304 100644
--- a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs
@@ -6,6 +6,7 @@
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
+using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
@@ -1052,8 +1053,8 @@ public async Task Add_update_delete_query_throws_if_no_container()
context.Add(customer);
Assert.StartsWith(
- "Response status code does not indicate success: NotFound (404); Substatus: 0",
- (await Assert.ThrowsAsync(() => context.SaveChangesAsync())).Message);
+ @"Message: {""Errors"":[""Resource Not Found""]}",
+ (await Assert.ThrowsAsync(() => context.SaveChangesAsync())).Message);
}
using (var context = new CustomerContext(options))
@@ -1061,8 +1062,8 @@ public async Task Add_update_delete_query_throws_if_no_container()
context.Add(customer).State = EntityState.Modified;
Assert.StartsWith(
- "Response status code does not indicate success: NotFound (404); Substatus: 0",
- (await Assert.ThrowsAsync(() => context.SaveChangesAsync())).Message);
+ @"Message: {""Errors"":[""Resource Not Found""]}",
+ (await Assert.ThrowsAsync(() => context.SaveChangesAsync())).Message);
}
using (var context = new CustomerContext(options))
@@ -1070,15 +1071,15 @@ public async Task Add_update_delete_query_throws_if_no_container()
context.Add(customer).State = EntityState.Deleted;
Assert.StartsWith(
- "Response status code does not indicate success: NotFound (404); Substatus: 0",
- (await Assert.ThrowsAsync(() => context.SaveChangesAsync())).Message);
+ @"Message: {""Errors"":[""Resource Not Found""]}",
+ (await Assert.ThrowsAsync(() => context.SaveChangesAsync())).Message);
}
using (var context = new CustomerContext(options))
{
Assert.StartsWith(
- "Response status code does not indicate success: NotFound (404); Substatus: 0",
- (await Assert.ThrowsAsync(() => context.Set().SingleAsync())).Message);
+ @"Message: {""Errors"":[""Resource Not Found""]}",
+ (await Assert.ThrowsAsync(() => context.Set().SingleAsync())).Message);
}
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs
index 27039fcc1a4..54312c587b0 100644
--- a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs
@@ -5,7 +5,8 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
-using Microsoft.Azure.Cosmos;
+using System.Text.Json;
+using Azure.Cosmos;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -99,7 +100,7 @@ private async Task CreateFromFile(DbContext context)
if (await context.Database.EnsureCreatedAsync())
{
var cosmosClient = context.GetService();
- var serializer = new JsonSerializer();
+ var serializer = CosmosClientWrapper.Serializer;
using var fs = new FileStream(_dataFilePath, FileMode.Open, FileAccess.Read);
using var sr = new StreamReader(fs);
using var reader = new JsonTextReader(sr);
@@ -167,30 +168,24 @@ public override async Task CleanAsync(DbContext context)
var cosmosClient = context.Database.GetCosmosClient();
var database = cosmosClient.GetDatabase(Name);
var containerIterator = database.GetContainerQueryIterator();
- while (containerIterator.HasMoreResults)
+ await foreach (var containerProperties in containerIterator)
{
- foreach (var containerProperties in await containerIterator.ReadNextAsync())
- {
- var container = database.GetContainer(containerProperties.Id);
- var partitionKey = containerProperties.PartitionKeyPath[1..];
- var itemIterator = container.GetItemQueryIterator(
- new QueryDefinition("SELECT * FROM c"));
+ var container = database.GetContainer(containerProperties.Id);
+ var partitionKey = containerProperties.PartitionKeyPath[1..];
+ var itemIterator = container.GetItemQueryIterator(
+ new QueryDefinition("SELECT * FROM c"));
- var items = new List<(string Id, string PartitionKey)>();
- while (itemIterator.HasMoreResults)
- {
- foreach (var item in await itemIterator.ReadNextAsync())
- {
- items.Add((item["id"].ToString(), item[partitionKey]?.ToString()));
- }
- }
+ var items = new List<(string Id, string PartitionKey)>();
+ await foreach (var item in itemIterator)
+ {
+ items.Add((item["id"].ToString(), item[partitionKey]?.ToString()));
+ }
- foreach (var item in items)
- {
- await container.DeleteItemAsync