Skip to content

Conversation

@sebastienros
Copy link
Member

@sebastienros sebastienros commented Feb 7, 2024

Fixes #1403

Microsoft Reviewers: Open in CodeFlow

@ghost ghost added the area-codeflow for labeling automated codeflow. intentionally a different color! label Feb 7, 2024
# Conflicts:
#	src/Components/Aspire.Npgsql.EntityFrameworkCore.PostgreSQL/AspireEFPostgreSqlExtensions.cs
"type": "string",
"description": "Gets or sets the connection string of the PostgreSQL database to connect to."
},
"DbContextPooling": {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pooling is enabled by default in AddNpgsql. Use Enrich otherwise.

"description": "Gets or sets a boolean value that indicates whether the OpenTelemetry metrics are enabled or not.",
"default": true
},
"Retry": {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bool instead of int to simplify the usage. Use a custom call in the options builder to define a custom value.

namespace Aspire.Components.Common.Tests;

public class TestDbContext : DbContext
public interface ITestDbContext
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To test we can register and Enrich a DbContext with distinct service and implementation.

configureSettings?.Invoke(settings);

if (settings.DbContextPooling)
builder.Services.AddNpgsqlDataSource(settings.ConnectionString ?? string.Empty, builder =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are overwriting my changes in ae11632. Can you revert this so it doesn't add back NpgsqlDataSource into DI?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// We don't provide the connection string, it's going to use the pre-registered DataSource.
// We don't register logger factory, because there is no need to: https://learn.microsoft.com/dotnet/api/microsoft.entityframeworkcore.dbcontextoptionsbuilder.useloggerfactory?view=efcore-7.0#remarks
dbContextOptionsBuilder.UseNpgsql(builder =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you always need to call UseNpgsql whether settings.Retry is true or false.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, mistake when I refactored the code to reuse the same method for both Enrich and AddNpgsql. And with your changes I might have to split them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also means we might be missing a test that should have caught that for AddNpgsql.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also means we might be missing a test that should have caught that for AddNpgsql.

Good call. #2136

}

[Fact]
public void EnrichCanConfigureDbContextOptions()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) Why are these tests here, and not in EnrichNpgsqlTests?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class was named AspireEFPostgreSqlExtensionsTests.cs which is where they are. Didn't think we would do a test class for a specific method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't realize you meant the one that just got created, moved them.


protected override void SetupConnectionInformationIsDelayValidated()
{
throw new SkipTestException("Need to skip this test until https://github.com/npgsql/efcore.pg/issues/2891 is fixed.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to remove this now that we have builder.EnableServiceProviderCaching(false); in ConfigureDbContextOptionsBuilderForTesting.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works, but I had to add a similar thing for Enrich instead since it doesn't care about the connection string.

@davidfowl
Copy link
Member

Add a playground sample or modify one with the new apai

#pragma warning restore EF1001 // Internal EF Core API usage.
}

public class WorkaroundToReadProtectedField : NpgsqlRetryingExecutionStrategy
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) maybe we can extract this to a common class to share it.


builder.EnrichNpgsqlDbContext<TestDbContext>();

// The service descriptor should not be affected with MaxRetryCount set to 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does with MaxRetryCount set to 0 mean here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to change the text, will fix. The idea is that if the setting is false (0 previously) then we should not have replaced the service descriptor.

@sebastienros
Copy link
Member Author

@davidfowl example added in README and in the Postgres playground.

@sebastienros sebastienros marked this pull request as ready for review February 8, 2024 23:01
Comment on lines 20 to 25
internal static void ConfigureDbContextOptionsBuilderForTesting(DbContextOptionsBuilder builder)
{
// Don't cache the service provider in testing.
// Works around https://github.com/npgsql/efcore.pg/issues/2891, which is errantly caches connection strings across DI containers.
builder.EnableServiceProviderCaching(false);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
internal static void ConfigureDbContextOptionsBuilderForTesting(DbContextOptionsBuilder builder)
{
// Don't cache the service provider in testing.
// Works around https://github.com/npgsql/efcore.pg/issues/2891, which is errantly caches connection strings across DI containers.
builder.EnableServiceProviderCaching(false);
}

Can we reuse the one from AspireEFPostgreSqlExtensionsTests

Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the great work here.

=> Assert.True(new NpgsqlEntityFrameworkCorePostgreSQLSettings().Metrics);

[Fact]
public void MaxRetryCountIsSameAsInTheDefaultNpgsqlPolicy()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I don't think these are super valuable tests, following the current example we should have one that ensures Retry is true by default.

Assert.Equal(ConnectionString, actualConnectionString);

// ensure the max retry count from config was respected
Assert.NotNull(extension.ExecutionStrategyFactory);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above this line needs to be updated.

Assert.Equal(new WorkaroundToReadProtectedField(dbContext).RetryCount, new NpgsqlEntityFrameworkCorePostgreSQLSettings().MaxRetryCount);
}

public class WorkaroundToReadProtectedField : NpgsqlRetryingExecutionStrategy
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be replaced by the common one now. Or just removed since it isn't used anymore.

Comment on lines 113 to 116
NpgsqlEntityFrameworkCorePostgreSQLSettings settings = new();
var typeSpecificSectionName = $"{DefaultConfigSectionName}:{typeof(TContext).Name}";
var typeSpecificConfigurationSection = builder.Configuration.GetSection(typeSpecificSectionName);
if (typeSpecificConfigurationSection.Exists()) // https://github.com/dotnet/runtime/issues/91380
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we extract this into a common method?

return;
}

// Resolving DbContext<TContextService> will resolve DbContextOptions<TContextImplementation>.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the significance of this line. What exactly does it mean?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a reminder that we seek the service descriptor for DbContextOptions<TestDbContext> (referenced as TContextImplementation in EF APIs) because it is the db context options that is resolved even when we register a db context with an interface (TContextService in EF APIs)

Comment on lines 269 to 270
// ensure the command timeout was respected
Assert.Equal(123, extension.CommandTimeout);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the command timeout 123 stuff in these lower tests? Is it necessary?

// ensure the command timeout was respected
Assert.Equal(123, extension.CommandTimeout);

// ensure the max retry count from config was respected
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ensure the max retry count from config was respected
// ensure the Retry from config was respected

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeated elsewhere.

# Aspire.Npgsql.EntityFrameworkCore.PostgreSQL library

Registers [EntityFrameworkCore](https://learn.microsoft.com/ef/core/) [DbContext](https://learn.microsoft.com/dotnet/api/microsoft.entityframeworkcore.dbcontext) in the DI container for connecting PostgreSQL®* database. Enables connection pooling, health check, logging and telemetry.
Registers [EntityFrameworkCore](https://learn.microsoft.com/ef/core/) [DbContext](https://learn.microsoft.com/dotnet/api/microsoft.entityframeworkcore.dbcontext) in the DI container for connecting PostgreSQL®* database. Enables connection pooling, connection retries, health check, logging and telemetry.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Registers [EntityFrameworkCore](https://learn.microsoft.com/ef/core/) [DbContext](https://learn.microsoft.com/dotnet/api/microsoft.entityframeworkcore.dbcontext) in the DI container for connecting PostgreSQL®* database. Enables connection pooling, connection retries, health check, logging and telemetry.
Registers [EntityFrameworkCore](https://learn.microsoft.com/ef/core/) [DbContext](https://learn.microsoft.com/dotnet/api/microsoft.entityframeworkcore.dbcontext) in the DI container for connecting PostgreSQL®* database. Enables connection pooling, retries, health check, logging and telemetry.

Retries in general, right?

@davidfowl
Copy link
Member

Did we update one of the playground samples?

@sebastienros
Copy link
Member Author

@davidfowl yes

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-codeflow for labeling automated codeflow. intentionally a different color!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redesign EF components public API

5 participants