Skip to content

Commit

Permalink
Make CosmosClient singleton and add a public way to access it.
Browse files Browse the repository at this point in the history
Part of #12086
  • Loading branch information
AndriySvyryd committed Jun 12, 2019
1 parent 90be0d5 commit bae4df4
Show file tree
Hide file tree
Showing 15 changed files with 490 additions and 111 deletions.
43 changes: 43 additions & 0 deletions src/EFCore.Cosmos/Extensions/CosmosDatabaseFacadeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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 JetBrains.Annotations;
using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore
{
/// <summary>
/// Extension methods for the <see cref="DatabaseFacade" /> returned from <see cref="DbContext.Database" />
/// that can be used only with the Cosmos provider.
/// </summary>
public static class CosmosDatabaseFacadeExtensions
{
/// <summary>
/// Gets the underlying <see cref="CosmosClient" /> for this <see cref="DbContext" />.
/// </summary>
/// <param name="databaseFacade"> The <see cref="DatabaseFacade" /> for the context. </param>
/// <returns> The <see cref="CosmosClient" /> </returns>
public static CosmosClient GetCosmosClient([NotNull] this DatabaseFacade databaseFacade)
=> GetService<SingletonCosmosClientWrapper>(databaseFacade).Client;

private static TService GetService<TService>(IInfrastructure<IServiceProvider> databaseFacade)
{
Check.NotNull(databaseFacade, nameof(databaseFacade));

var service = databaseFacade.Instance.GetService<TService>();
if (service == null)
{
throw new InvalidOperationException(CosmosStrings.CosmosNotInUse);
}

return service;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ public static IServiceCollection AddEntityFrameworkCosmos([NotNull] this IServic
.TryAdd<IQueryableMethodTranslatingExpressionVisitorFactory, CosmosQueryableMethodTranslatingExpressionVisitorFactory>()
.TryAdd<IShapedQueryCompilingExpressionVisitorFactory, CosmosShapedQueryCompilingExpressionVisitorFactory>()

.TryAdd<ISingletonOptions, ICosmosSingletonOptions>(p => p.GetService<ICosmosSingletonOptions>())
.TryAddProviderSpecificServices(
b => b
.TryAddSingleton<ICosmosSingletonOptions, CosmosSingletonOptions>()
.TryAddSingleton<SingletonCosmosClientWrapper, SingletonCosmosClientWrapper>()
.TryAddScoped<CosmosClientWrapper, CosmosClientWrapper>()
.TryAddScoped<ISqlGeneratorFactory, CosmosSqlGeneratorFactory>()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal
Expand All @@ -15,10 +17,11 @@ public class CosmosDbOptionsExtension : IDbContextOptionsExtension
{
private string _serviceEndPoint;
private string _authKeyOrResourceToken;
private string _region;
private string _databaseName;
private Func<ExecutionStrategyDependencies, IExecutionStrategy> _executionStrategyFactory;
private string _logFragment;
private string _region;
private long? _serviceProviderHash;

public CosmosDbOptionsExtension()
{
Expand Down Expand Up @@ -108,18 +111,43 @@ public bool ApplyServices(IServiceCollection services)
return true;
}

public long GetServiceProviderHashCode()
/// <summary>
/// Returns a hash code created from any options that would cause a new <see cref="IServiceProvider" />
/// to be needed.
/// </summary>
/// <returns> A hash over options that require a new service provider when changed. </returns>
public virtual long GetServiceProviderHashCode()
{
return 0;
if (_serviceProviderHash == null)
{
var hashCode = _serviceEndPoint.GetHashCode();
hashCode = (hashCode * 397) ^ _authKeyOrResourceToken.GetHashCode();
hashCode = (hashCode * 397) ^ (_region?.GetHashCode() ?? 0);

_serviceProviderHash = hashCode;
}

return _serviceProviderHash.Value;
}

public void Validate(IDbContextOptions options)
/// <summary>
/// Populates a dictionary of information that may change between uses of the
/// extension such that it can be compared to a previous configuration for
/// this option and differences can be logged. The dictionary key prefix
/// <c>"Cosmos:"</c> is used.
/// </summary>
/// <param name="debugInfo"> The dictionary to populate. </param>
public virtual void PopulateDebugInfo(IDictionary<string, string> debugInfo)
{
Check.NotNull(debugInfo, nameof(debugInfo));

debugInfo["Cosmos:" + nameof(ServiceEndPoint)] = _serviceEndPoint.GetHashCode().ToString(CultureInfo.InvariantCulture);
debugInfo["Cosmos:" + nameof(AuthKeyOrResourceToken)] = _authKeyOrResourceToken.GetHashCode().ToString(CultureInfo.InvariantCulture);
debugInfo["Cosmos:" + nameof(CosmosDbContextOptionsBuilder.Region)] = (_region?.GetHashCode() ?? 0).ToString(CultureInfo.InvariantCulture);
}

public virtual void PopulateDebugInfo(IDictionary<string, string> debugInfo)
public void Validate(IDbContextOptions options)
{
debugInfo["Cosmos"] = "1";
}

public string LogFragment
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// 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 Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal
{
/// <summary>
/// <para>
/// 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.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Singleton"/>. This means a single instance
/// is used by many <see cref="DbContext"/> instances. The implementation must be thread-safe.
/// This service cannot depend on services registered as <see cref="ServiceLifetime.Scoped"/>.
/// </para>
/// </summary>
public class CosmosSingletonOptions : ICosmosSingletonOptions
{
/// <summary>
/// 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.
/// </summary>
public string ServiceEndPoint { get; private set; }

/// <summary>
/// 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.
/// </summary>
public string AuthKeyOrResourceToken { get; private set; }

/// <summary>
/// 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.
/// </summary>
public string Region { get; private set; }

/// <summary>
/// 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.
/// </summary>
public virtual void Initialize(IDbContextOptions options)
{
var cosmosOptions = options.FindExtension<CosmosDbOptionsExtension>();
if (cosmosOptions != null)
{
ServiceEndPoint = cosmosOptions.ServiceEndPoint;
AuthKeyOrResourceToken = cosmosOptions.AuthKeyOrResourceToken;
Region = cosmosOptions.Region;
}
}

/// <summary>
/// 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.
/// </summary>
public virtual void Validate(IDbContextOptions options)
{
var inMemoryOptions = options.FindExtension<CosmosDbOptionsExtension>();

if (inMemoryOptions != null
&& (ServiceEndPoint != inMemoryOptions.ServiceEndPoint
|| AuthKeyOrResourceToken != inMemoryOptions.AuthKeyOrResourceToken
|| Region != inMemoryOptions.Region))
{
throw new InvalidOperationException(
CoreStrings.SingletonOptionChanged(
nameof(CosmosDbContextOptionsExtensions.UseCosmos),
nameof(DbContextOptionsBuilder.UseInternalServiceProvider)));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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 Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal
{
/// <summary>
/// <para>
/// 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.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Singleton"/> and multiple registrations
/// are allowed. This means a single instance of each service is used by many <see cref="DbContext"/>
/// instances. The implementation must be thread-safe.
/// This service cannot depend on services registered as <see cref="ServiceLifetime.Scoped"/>.
/// </para>
/// </summary>
public interface ICosmosSingletonOptions : ISingletonOptions
{
/// <summary>
/// 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.
/// </summary>
string ServiceEndPoint { get; }

/// <summary>
/// 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.
/// </summary>
string AuthKeyOrResourceToken { get; }

/// <summary>
/// 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.
/// </summary>
string Region { get; }
}
}
6 changes: 6 additions & 0 deletions src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EFCore.Cosmos/Properties/CosmosStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CosmosNotInUse" xml:space="preserve">
<value>Cosmos-specific methods can only be used when the context is using the Cosmos provider.</value>
</data>
<data name="OrphanedNestedDocument" xml:space="preserve">
<value>The entity of type '{entityType}' is mapped as a part of the document mapped to '{missingEntityType}', but there is no tracked entity of this type with the corresponding key value. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the key values.</value>
</data>
Expand Down
Loading

0 comments on commit bae4df4

Please sign in to comment.