From 8d4aa62b9790b60f0086f9bf66c84ccbc450a93e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Jun 2025 04:08:35 +0000 Subject: [PATCH 1/5] Initial plan for issue From 82f1b283092823a047028616ccfeebdf624bed9a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Jun 2025 04:20:01 +0000 Subject: [PATCH 2/5] Remove SQLite extension loading support from hosting and client integrations Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com> --- ...munityToolkit.Aspire.Hosting.Sqlite.csproj | 4 - .../SqliteResource.cs | 17 +- .../SqliteResourceBuilderExtensions.cs | 46 ----- .../CommunityToolkit.Aspire.Hosting.Sqlite.cs | 15 -- .../AspireSqliteExtensions.cs | 188 +----------------- ...oolkit.Aspire.Microsoft.Data.Sqlite.csproj | 3 - .../SqliteConnectionSettings.cs | 5 - ...ityToolkit.Aspire.Microsoft.Data.Sqlite.cs | 6 - .../AspireEFSqliteExtensions.cs | 9 +- src/Shared/Sqlite/SqliteExtensionMetadata.cs | 10 - .../AddSqliteTests.cs | 36 +--- .../ResourceWithExtensionTests.cs | 52 ----- .../SqliteConnectionTests.cs | 28 --- 13 files changed, 4 insertions(+), 415 deletions(-) delete mode 100644 src/Shared/Sqlite/SqliteExtensionMetadata.cs delete mode 100644 tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/ResourceWithExtensionTests.cs diff --git a/src/CommunityToolkit.Aspire.Hosting.Sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.csproj b/src/CommunityToolkit.Aspire.Hosting.Sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.csproj index f7207c2b..f5f5de33 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.csproj +++ b/src/CommunityToolkit.Aspire.Hosting.Sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.csproj @@ -11,8 +11,4 @@ - - - - diff --git a/src/CommunityToolkit.Aspire.Hosting.Sqlite/SqliteResource.cs b/src/CommunityToolkit.Aspire.Hosting.Sqlite/SqliteResource.cs index de555164..5da7586d 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Sqlite/SqliteResource.cs +++ b/src/CommunityToolkit.Aspire.Hosting.Sqlite/SqliteResource.cs @@ -1,6 +1,3 @@ -using Microsoft.Extensions.Hosting; -using System.Text.Json; - namespace Aspire.Hosting.ApplicationModel; /// @@ -18,18 +15,6 @@ public class SqliteResource(string name, string databasePath, string databaseFil internal string DatabaseFilePath => Path.Combine(DatabasePath, DatabaseFileName); /// - public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create($"Data Source={DatabaseFilePath};Cache=Shared;Mode=ReadWriteCreate;Extensions={JsonSerializer.Serialize(Extensions)}"); - - private readonly List extensions = []; - - /// - /// Gets the extensions to be loaded into the database. - /// - /// - /// Extensions are not loaded by the hosting integration, the information is provided for the client to load the extensions. - /// - public IReadOnlyCollection Extensions => extensions; - - internal void AddExtension(SqliteExtensionMetadata extension) => extensions.Add(extension); + public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create($"Data Source={DatabaseFilePath};Cache=Shared;Mode=ReadWriteCreate"); } diff --git a/src/CommunityToolkit.Aspire.Hosting.Sqlite/SqliteResourceBuilderExtensions.cs b/src/CommunityToolkit.Aspire.Hosting.Sqlite/SqliteResourceBuilderExtensions.cs index 375a9a29..8b86be72 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Sqlite/SqliteResourceBuilderExtensions.cs +++ b/src/CommunityToolkit.Aspire.Hosting.Sqlite/SqliteResourceBuilderExtensions.cs @@ -1,5 +1,4 @@ using Aspire.Hosting.ApplicationModel; -using System.Diagnostics.CodeAnalysis; namespace Aspire.Hosting; @@ -88,50 +87,5 @@ public static IResourceBuilder WithSqliteWeb(this IResourceBuild return builder; } - /// - /// Adds an extension to the Sqlite resource that will be loaded from a NuGet package. - /// - /// The resource builder. - /// The name of the extension file with to add, eg: vec0, without file extension. - /// The name of the NuGet package. If this is set to null, the value of is used. - /// The resource builder. - /// - /// Extensions are not loaded by the hosting integration, the information is provided for the client to load the extensions. - /// - /// This extension is experimental while the final design of extension loading is decided. - /// - [Experimental("CTASPIRE002", UrlFormat = "https://aka.ms/communitytoolkit/aspire/diagnostics#{0}")] - public static IResourceBuilder WithNuGetExtension(this IResourceBuilder builder, string extension, string? packageName = null) - { - ArgumentNullException.ThrowIfNull(builder, nameof(builder)); - ArgumentException.ThrowIfNullOrEmpty(extension, nameof(extension)); - - builder.Resource.AddExtension(new(extension, packageName ?? extension, IsNuGetPackage: true, ExtensionFolder: null)); - - return builder; - } - /// - /// Adds an extension to the Sqlite resource that will be loaded from a local path. - /// - /// The resource builder. - /// The name of the extension file with to add, eg: vec0, without file extension. - /// The path to the extension file. - /// The resource builder. - /// - /// Extensions are not loaded by the hosting integration, the information is provided for the client to load the extensions. - /// - /// This extension is experimental while the final design of extension loading is decided. - /// - [Experimental("CTASPIRE002", UrlFormat = "https://aka.ms/communitytoolkit/aspire/diagnostics#{0}")] - public static IResourceBuilder WithLocalExtension(this IResourceBuilder builder, string extension, string extensionPath) - { - ArgumentNullException.ThrowIfNull(builder, nameof(builder)); - ArgumentException.ThrowIfNullOrEmpty(extension, nameof(extension)); - ArgumentException.ThrowIfNullOrEmpty(extensionPath, nameof(extensionPath)); - - builder.Resource.AddExtension(new(extension, PackageName: null, IsNuGetPackage: false, extensionPath)); - - return builder; - } } diff --git a/src/CommunityToolkit.Aspire.Hosting.Sqlite/api/CommunityToolkit.Aspire.Hosting.Sqlite.cs b/src/CommunityToolkit.Aspire.Hosting.Sqlite/api/CommunityToolkit.Aspire.Hosting.Sqlite.cs index b16b1f21..61d0c037 100644 --- a/src/CommunityToolkit.Aspire.Hosting.Sqlite/api/CommunityToolkit.Aspire.Hosting.Sqlite.cs +++ b/src/CommunityToolkit.Aspire.Hosting.Sqlite/api/CommunityToolkit.Aspire.Hosting.Sqlite.cs @@ -12,12 +12,6 @@ public static partial class SqliteResourceBuilderExtensions { public static ApplicationModel.IResourceBuilder AddSqlite(this IDistributedApplicationBuilder builder, string name, string? databasePath = null, string? databaseFileName = null) { throw null; } - [System.Diagnostics.CodeAnalysis.Experimental("CTASPIRE002", UrlFormat = "https://aka.ms/communitytoolkit/aspire/diagnostics#{0}")] - public static ApplicationModel.IResourceBuilder WithLocalExtension(this ApplicationModel.IResourceBuilder builder, string extension, string extensionPath) { throw null; } - - [System.Diagnostics.CodeAnalysis.Experimental("CTASPIRE002", UrlFormat = "https://aka.ms/communitytoolkit/aspire/diagnostics#{0}")] - public static ApplicationModel.IResourceBuilder WithNuGetExtension(this ApplicationModel.IResourceBuilder builder, string extension, string? packageName = null) { throw null; } - public static ApplicationModel.IResourceBuilder WithSqliteWeb(this ApplicationModel.IResourceBuilder builder, System.Action>? configureContainer = null, string? containerName = null) { throw null; } } } @@ -29,8 +23,6 @@ public partial class SqliteResource : Resource, IResourceWithConnectionString, I public SqliteResource(string name, string databasePath, string databaseFileName) : base(default!) { } public ReferenceExpression ConnectionStringExpression { get { throw null; } } - - public System.Collections.Generic.IReadOnlyCollection Extensions { get { throw null; } } } public partial class SqliteWebResource : ContainerResource, IResourceWithConnectionString, IResource, IManifestExpressionProvider, IValueProvider, IValueWithReferences @@ -41,11 +33,4 @@ public SqliteWebResource(string name) : base(default!, default) { } public EndpointReference PrimaryEndpoint { get { throw null; } } } -} - -namespace Microsoft.Extensions.Hosting -{ - public partial record SqliteExtensionMetadata(string Extension, string? PackageName, bool IsNuGetPackage, string? ExtensionFolder) - { - } } \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/AspireSqliteExtensions.cs b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/AspireSqliteExtensions.cs index 7ad5be49..1440b21a 100644 --- a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/AspireSqliteExtensions.cs +++ b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/AspireSqliteExtensions.cs @@ -3,13 +3,8 @@ using Microsoft.Data.Sqlite; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Logging; using System.Data.Common; -using System.Runtime.InteropServices; -using System.Text.Json; -using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment; namespace Microsoft.Extensions.Hosting; @@ -67,15 +62,6 @@ private static void AddSqliteClient( settings.ConnectionString = connectionString; } - if (!string.IsNullOrEmpty(settings.ConnectionString)) - { - var cbs = new DbConnectionStringBuilder { ConnectionString = settings.ConnectionString }; - if (cbs.TryGetValue("Extensions", out var extensions)) - { - settings.Extensions = JsonSerializer.Deserialize>((string)extensions) ?? []; - } - } - configureSettings?.Invoke(settings); builder.RegisterSqliteServices(settings, connectionName, serviceKey); @@ -115,181 +101,9 @@ private static void RegisterSqliteServices( SqliteConnection CreateConnection(IServiceProvider sp, object? key) { - var logger = sp.GetRequiredService>(); ConnectionStringValidation.ValidateConnectionString(settings.ConnectionString, connectionName, DefaultConfigSectionName); - var csb = new DbConnectionStringBuilder { ConnectionString = settings.ConnectionString }; - if (csb.ContainsKey("Extensions")) - { - csb.Remove("Extensions"); - } - var connection = new SqliteConnection(csb.ConnectionString); - - foreach (var extension in settings.Extensions) - { - if (extension.IsNuGetPackage) - { - if (string.IsNullOrEmpty(extension.PackageName)) - { - throw new InvalidOperationException("PackageName is required when loading an extension from a NuGet package."); - } - - EnsureLoadableFromNuGet(extension.Extension, extension.PackageName, logger); - } - else - { - if (string.IsNullOrEmpty(extension.ExtensionFolder)) - { - throw new InvalidOperationException("ExtensionFolder is required when loading an extension from a folder."); - } - - EnsureLoadableFromLocalPath(extension.Extension, extension.ExtensionFolder); - } - connection.LoadExtension(extension.Extension); - } - + var connection = new SqliteConnection(settings.ConnectionString); return connection; } } - - // Adapted from https://github.com/dotnet/docs/blob/dbbeda13bf016a6ff76b0baab1488c927a64ff24/samples/snippets/standard/data/sqlite/ExtensionsSample/Program.cs#L40 - internal static void EnsureLoadableFromNuGet(string package, string library, ILogger logger) - { - var runtimeLibrary = DependencyContext.Default?.RuntimeLibraries.FirstOrDefault(l => l.Name == package); - if (runtimeLibrary is null) - { - logger.LogInformation("Could not find the runtime library for package {Package}", package); - return; - } - - string sharedLibraryExtension; - string pathVariableName = "PATH"; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - sharedLibraryExtension = ".dll"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - sharedLibraryExtension = ".so"; - pathVariableName = "LD_LIBRARY_PATH"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - sharedLibraryExtension = ".dylib"; - pathVariableName = "DYLD_LIBRARY_PATH"; - } - else - { - throw new NotSupportedException("Unsupported OS platform"); - } - - var candidateAssets = new Dictionary<(string? Package, string Asset), int>(); - var rid = RuntimeEnvironment.GetRuntimeIdentifier(); - var rids = DependencyContext.Default?.RuntimeGraph.First(g => g.Runtime == rid).Fallbacks.ToList() ?? []; - rids.Insert(0, rid); - - logger.LogInformation("Looking for {Library} in {Package} runtime assets", library, package); - logger.LogInformation("Possible runtime identifiers: {Rids}", string.Join(", ", rids)); - - foreach (var group in runtimeLibrary.NativeLibraryGroups) - { - foreach (var file in group.RuntimeFiles) - { - if (string.Equals( - Path.GetFileName(file.Path), - library + sharedLibraryExtension, - StringComparison.OrdinalIgnoreCase)) - { - var fallbacks = rids.IndexOf(group.Runtime); - if (fallbacks != -1) - { - logger.LogInformation("Found {Library} in {Package} runtime assets at {Path}", library, package, file.Path); - candidateAssets.Add((runtimeLibrary.Path, file.Path), fallbacks); - } - } - } - } - - var assetPath = candidateAssets - .OrderBy(p => p.Value) - .Select(p => p.Key) - .FirstOrDefault(); - if (assetPath != default) - { - string? assetDirectory = null; - if (File.Exists(Path.Combine(AppContext.BaseDirectory, assetPath.Asset))) - { - // NB: Framework-dependent deployments copy assets to the application base directory - assetDirectory = Path.Combine( - AppContext.BaseDirectory, - Path.GetDirectoryName(assetPath.Asset.Replace('/', Path.DirectorySeparatorChar))!); - - logger.LogInformation("Found {Library} in {Package} runtime assets at {Path}", library, package, assetPath.Asset); - } - else - { - string? assetFullPath = null; - var probingDirectories = ((string?)AppDomain.CurrentDomain.GetData("PROBING_DIRECTORIES"))? - .Split(Path.PathSeparator) ?? []; - foreach (var directory in probingDirectories) - { - var candidateFullPath = Path.Combine( - directory, - assetPath.Package ?? "", - assetPath.Asset); - if (File.Exists(candidateFullPath)) - { - assetFullPath = candidateFullPath; - } - } - - assetDirectory = Path.GetDirectoryName(assetFullPath); - logger.LogInformation("Found {Library} in {Package} runtime assets at {Path} (using PROBING_DIRECTORIES: {ProbingDirectories})", library, package, assetFullPath, string.Join(",", probingDirectories)); - } - - var path = new HashSet(Environment.GetEnvironmentVariable(pathVariableName)!.Split(Path.PathSeparator)); - - if (assetDirectory is not null && path.Add(assetDirectory)) - { - logger.LogInformation("Adding {AssetDirectory} to {PathVariableName}", assetDirectory, pathVariableName); - Environment.SetEnvironmentVariable(pathVariableName, string.Join(Path.PathSeparator, path)); - logger.LogInformation("Set {PathVariableName} to: {PathVariableValue}", pathVariableName, Environment.GetEnvironmentVariable(pathVariableName)); - } - } - else - { - logger.LogInformation("Could not find {Library} in {Package} runtime assets", library, package); - } - } - - internal static void EnsureLoadableFromLocalPath(string library, string assetDirectory) - { - string sharedLibraryExtension; - string pathVariableName = "PATH"; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - sharedLibraryExtension = ".dll"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - sharedLibraryExtension = ".so"; - pathVariableName = "LD_LIBRARY_PATH"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - sharedLibraryExtension = ".dylib"; - pathVariableName = "DYLD_LIBRARY_PATH"; - } - else - { - throw new NotSupportedException("Unsupported OS platform"); - } - - if (File.Exists(Path.Combine(assetDirectory, library + sharedLibraryExtension))) - { - var path = new HashSet(Environment.GetEnvironmentVariable(pathVariableName)!.Split(Path.PathSeparator)); - - if (assetDirectory is not null && path.Add(assetDirectory)) - Environment.SetEnvironmentVariable(pathVariableName, string.Join(Path.PathSeparator, path)); - } - } } diff --git a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.csproj b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.csproj index d28599b0..89c1f298 100644 --- a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.csproj +++ b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.csproj @@ -8,8 +8,6 @@ - - @@ -18,7 +16,6 @@ - diff --git a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/SqliteConnectionSettings.cs b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/SqliteConnectionSettings.cs index 8be5716c..b58fccda 100644 --- a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/SqliteConnectionSettings.cs +++ b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/SqliteConnectionSettings.cs @@ -17,9 +17,4 @@ public sealed class SqliteConnectionSettings /// The default value is . /// public bool DisableHealthChecks { get; set; } - - /// - /// Extensions to be loaded into the database. - /// - public IEnumerable Extensions { get; set; } = []; } diff --git a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/api/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.cs b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/api/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.cs index b51f4dfa..6a6e2018 100644 --- a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/api/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.cs +++ b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/api/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.cs @@ -20,11 +20,5 @@ public sealed partial class SqliteConnectionSettings public string? ConnectionString { get { throw null; } set { } } public bool DisableHealthChecks { get { throw null; } set { } } - - public System.Collections.Generic.IEnumerable Extensions { get { throw null; } set { } } - } - - public partial record SqliteExtensionMetadata(string Extension, string? PackageName, bool IsNuGetPackage, string? ExtensionFolder) - { } } \ No newline at end of file diff --git a/src/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite/AspireEFSqliteExtensions.cs b/src/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite/AspireEFSqliteExtensions.cs index 47e7719e..126d476a 100644 --- a/src/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite/AspireEFSqliteExtensions.cs +++ b/src/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite/AspireEFSqliteExtensions.cs @@ -3,7 +3,6 @@ using Microsoft.EntityFrameworkCore.Diagnostics.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System.Data.Common; using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.Hosting; @@ -69,13 +68,7 @@ void ConfigureDbContext(DbContextOptionsBuilder dbContextOptionsBuilder) // delay validating the ConnectionString until the DbContext is requested. This ensures an exception doesn't happen until a Logger is established. ConnectionStringValidation.ValidateConnectionString(settings.ConnectionString, name, DefaultConfigSectionName, $"{DefaultConfigSectionName}:{typeof(TContext).Name}", isEfDesignTime: EF.IsDesignTime); - var csb = new DbConnectionStringBuilder { ConnectionString = settings.ConnectionString }; - if (csb.ContainsKey("Extensions")) - { - csb.Remove("Extensions"); - } - - dbContextOptionsBuilder.UseSqlite(csb.ConnectionString); + dbContextOptionsBuilder.UseSqlite(settings.ConnectionString); configureDbContextOptions?.Invoke(dbContextOptionsBuilder); } } diff --git a/src/Shared/Sqlite/SqliteExtensionMetadata.cs b/src/Shared/Sqlite/SqliteExtensionMetadata.cs deleted file mode 100644 index 5e7d579e..00000000 --- a/src/Shared/Sqlite/SqliteExtensionMetadata.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Microsoft.Extensions.Hosting; - -/// -/// Represents metadata for an extension to be loaded into a database. -/// -/// The name of the extension binary, eg: vec0 -/// The name of the NuGet package. Only required if is . -/// Indicates if the extension will be loaded from a NuGet package. -/// The folder for the extension. Only required if is . -public record SqliteExtensionMetadata(string Extension, string? PackageName, bool IsNuGetPackage, string? ExtensionFolder); \ No newline at end of file diff --git a/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/AddSqliteTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/AddSqliteTests.cs index 8f435cb4..c521a4cf 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/AddSqliteTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/AddSqliteTests.cs @@ -1,7 +1,6 @@ using Aspire.Hosting; namespace CommunityToolkit.Aspire.Hosting.Sqlite; -#pragma warning disable CTASPIRE002 public class AddSqliteTests { [Fact] @@ -99,7 +98,7 @@ public async Task ResourceUsesProvidedPathAndFileName(string? path, string? file var connectionString = await sqlite.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None); - Assert.Equal($"Data Source={sqlite.Resource.DatabaseFilePath};Cache=Shared;Mode=ReadWriteCreate;Extensions=[]", connectionString); + Assert.Equal($"Data Source={sqlite.Resource.DatabaseFilePath};Cache=Shared;Mode=ReadWriteCreate", connectionString); } [Fact] @@ -154,37 +153,4 @@ public void SqliteWebResourceIncludedInManifestByDefault() Assert.False(sqliteWeb.TryGetAnnotationsOfType(out var annotations)); } - [Fact] - public void ResourceWithExtensionFromNuGet() - { - var builder = DistributedApplication.CreateBuilder(); - var sqlite = builder.AddSqlite("sqlite") - .WithNuGetExtension("FTS5"); - - Assert.Single(sqlite.Resource.Extensions, static e => e.Extension == "FTS5" && e.PackageName == "FTS5" && e.IsNuGetPackage && e.ExtensionFolder is null); - } - - [Fact] - public void ResourceWithExtensionFromLocal() - { - var builder = DistributedApplication.CreateBuilder(); - var sqlite = builder.AddSqlite("sqlite") - .WithLocalExtension("FTS5", "/path/to/extension"); - - Assert.Single(sqlite.Resource.Extensions, static e => e.Extension == "FTS5" && e.PackageName is null && !e.IsNuGetPackage && e.ExtensionFolder == "/path/to/extension"); - } - - [Fact] - public async Task ConnectionStringContainsExtensions() - { - var builder = DistributedApplication.CreateBuilder(); - var sqlite = builder.AddSqlite("sqlite") - .WithNuGetExtension("FTS5") - .WithNuGetExtension("mod_spatialite"); - - var connectionString = await sqlite.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None); - - Assert.Contains("FTS5", connectionString); - Assert.Contains("mod_spatialite", connectionString); - } } diff --git a/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/ResourceWithExtensionTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/ResourceWithExtensionTests.cs deleted file mode 100644 index f5747a45..00000000 --- a/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/ResourceWithExtensionTests.cs +++ /dev/null @@ -1,52 +0,0 @@ -#pragma warning disable CTASPIRE002 -using Aspire.Hosting; -using Aspire.Hosting.Utils; -using CommunityToolkit.Aspire.Testing; -using Microsoft.Data.Sqlite; -using Microsoft.Extensions.Hosting; -using Xunit.Abstractions; - -namespace CommunityToolkit.Aspire.Hosting.Sqlite.Tests; - -public class ResourceWithExtensionTests(ITestOutputHelper testOutputHelper) -{ - [Fact(Skip = "Skipping until there is a viable NuGet package for sqlite-vec we can use.")] - public async Task ResourceCreatedWithExtensionIsAccessible() - { - using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); - - var sqlite = builder.AddSqlite("sqlite") - .WithNuGetExtension("sqlite-vec"); - - await using var app = builder.Build(); - - await app.StartAsync(); - - var hb = Host.CreateApplicationBuilder(); - - hb.Configuration[$"ConnectionStrings:{sqlite.Resource.Name}"] = await sqlite.Resource.ConnectionStringExpression.GetValueAsync(default); - - hb.AddSqliteConnection(sqlite.Resource.Name); - - using var host = hb.Build(); - - await host.StartAsync(); - - var connection = host.Services.GetRequiredService(); - - var result = await IsExtensionLoadedAsync(connection, "vec_version()"); - - Assert.NotNull(result); - var version = Assert.IsType(result); - Assert.Equal("0.1.16", version); - } - - private static async Task IsExtensionLoadedAsync(SqliteConnection connection, string checkFunction) - { - await connection.OpenAsync(); - string checkQuery = $"SELECT {checkFunction}"; - using var command = connection.CreateCommand(); - command.CommandText = checkQuery; - return await command.ExecuteScalarAsync(); - } -} \ No newline at end of file diff --git a/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/SqliteConnectionTests.cs b/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/SqliteConnectionTests.cs index 1675a977..2034eaf7 100644 --- a/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/SqliteConnectionTests.cs +++ b/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/SqliteConnectionTests.cs @@ -115,32 +115,4 @@ public void CanSetMultipleKeyedConnections() Assert.NotNull(client2.ConnectionString); Assert.Equal("data source=/tmp/sqlite2.db", client2.ConnectionString); } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void ExtensionsSetViaConnectionString(bool useKeyed) - { - var builder = Host.CreateEmptyApplicationBuilder(null); - builder.Configuration.AddInMemoryCollection([ - new KeyValuePair("ConnectionStrings:sqlite", "Data Source=:memory:;Extensions=[{\"Extension\":\"mod_spatialite\",\"PackageName\":\"mod_spatialite\",\"IsNuGetPackage\":true,\"ExtensionFolder\":null}]") - ]); - - if (useKeyed) - { - builder.AddKeyedSqliteConnection("sqlite", settings => - { - Assert.NotEmpty(settings.Extensions); - Assert.Single(settings.Extensions, e => e.Extension == "mod_spatialite" && e.PackageName == "mod_spatialite" && e.IsNuGetPackage && e.ExtensionFolder is null); - }); - } - else - { - builder.AddSqliteConnection("sqlite", settings => - { - Assert.NotEmpty(settings.Extensions); - Assert.Single(settings.Extensions, e => e.Extension == "mod_spatialite" && e.PackageName == "mod_spatialite" && e.IsNuGetPackage && e.ExtensionFolder is null); - }); - } - } } From 14211a69dcacaad2ead5e460f8bcce9c2bd2eb1e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Jun 2025 04:21:04 +0000 Subject: [PATCH 3/5] Remove CTASPIRE002 diagnostic from documentation as SQLite extension support is removed Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com> --- docs/diagnostics.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/diagnostics.md b/docs/diagnostics.md index a8cd08f8..9fd7dc0a 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -10,10 +10,6 @@ In these cases, refer to the `` docs section of the API for more inform Once a release of .NET Aspire with that API is available, the API in the .NET Aspire Community Toolkit will be marked as obsolete and will be removed in a future release. -## CTASPIRE002 - -Support for loading extensions into SQLite requires either a NuGet package or folder path to the library to be provided, and as a result there is some custom logic to load the extension based on the path or NuGet package. This logic will require some experimenting to figure out edge cases, so the feature for extension loading will be kept as experimental until it is proven to be stable. - ## CTASPIRE003 The API is marked for deprecation and will be removed in a future release. From ac579b040cfd140afea14a27560b3e8e00333755 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Jun 2025 05:12:42 +0000 Subject: [PATCH 4/5] Make CreateConnection return explicit instead of using local variable Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com> --- .../AspireSqliteExtensions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/AspireSqliteExtensions.cs b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/AspireSqliteExtensions.cs index 1440b21a..4b098258 100644 --- a/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/AspireSqliteExtensions.cs +++ b/src/CommunityToolkit.Aspire.Microsoft.Data.Sqlite/AspireSqliteExtensions.cs @@ -102,8 +102,7 @@ private static void RegisterSqliteServices( SqliteConnection CreateConnection(IServiceProvider sp, object? key) { ConnectionStringValidation.ValidateConnectionString(settings.ConnectionString, connectionName, DefaultConfigSectionName); - var connection = new SqliteConnection(settings.ConnectionString); - return connection; + return new SqliteConnection(settings.ConnectionString); } } } From a614e77cf16d79248bcfcf4513c13b4682352589 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 18 Jun 2025 01:10:44 +0000 Subject: [PATCH 5/5] Finishing the SQLite integration changes with some test fixes --- .../ConfigurationTests.cs | 4 ---- .../SqliteConnectionTests.cs | 10 +++++----- .../SqliteConnectionTests.cs | 6 +++--- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/ConfigurationTests.cs b/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/ConfigurationTests.cs index bc583421..f940e2a5 100644 --- a/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/ConfigurationTests.cs +++ b/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/ConfigurationTests.cs @@ -11,8 +11,4 @@ public void ConnectionStringIsNullByDefault() => [Fact] public void HealthChecksEnabledByDefault() => Assert.False(new SqliteConnectionSettings().DisableHealthChecks); - - [Fact] - public void ExtensionsIsEmptyByDefault() => - Assert.Empty(new SqliteConnectionSettings().Extensions); } diff --git a/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/SqliteConnectionTests.cs b/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/SqliteConnectionTests.cs index 2034eaf7..eaa2e440 100644 --- a/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/SqliteConnectionTests.cs +++ b/tests/CommunityToolkit.Aspire.Microsoft.Data.Sqlite.Tests/SqliteConnectionTests.cs @@ -32,7 +32,7 @@ public void ReadsFromConnectionStringCorrectly(bool useKeyed) host.Services.GetRequiredService(); Assert.NotNull(client.ConnectionString); - Assert.Equal("data source=:memory:", client.ConnectionString); + Assert.Equal("data source=:memory:", client.ConnectionString, ignoreCase: true); } [Theory] @@ -60,7 +60,7 @@ public void CanSetConnectionStringInCode(bool useKeyed) host.Services.GetRequiredService(); Assert.NotNull(client.ConnectionString); - Assert.Equal("data source=:memory:", client.ConnectionString); + Assert.Equal("data source=:memory:", client.ConnectionString, ignoreCase: true); } [Theory] @@ -89,7 +89,7 @@ public void CanSetConnectionStringInCodeWithKey(bool useKeyed) host.Services.GetRequiredService(); Assert.NotNull(client.ConnectionString); - Assert.Equal("data source=:memory:", client.ConnectionString); + Assert.Equal("data source=:memory:", client.ConnectionString, ignoreCase: true); } [Fact] @@ -110,9 +110,9 @@ public void CanSetMultipleKeyedConnections() var client2 = host.Services.GetRequiredKeyedService("sqlite2"); Assert.NotNull(client1.ConnectionString); - Assert.Equal("data source=/tmp/sqlite1.db", client1.ConnectionString); + Assert.Equal("data source=/tmp/sqlite1.db", client1.ConnectionString, ignoreCase: true); Assert.NotNull(client2.ConnectionString); - Assert.Equal("data source=/tmp/sqlite2.db", client2.ConnectionString); + Assert.Equal("data source=/tmp/sqlite2.db", client2.ConnectionString, ignoreCase: true); } } diff --git a/tests/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite.Tests/SqliteConnectionTests.cs b/tests/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite.Tests/SqliteConnectionTests.cs index 1211ee78..6546c69e 100644 --- a/tests/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite.Tests/SqliteConnectionTests.cs +++ b/tests/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite.Tests/SqliteConnectionTests.cs @@ -22,7 +22,7 @@ public void ReadsFromConnectionStringCorrectly() var client = host.Services.GetRequiredService(); Assert.NotNull(client.Database.GetConnectionString()); - Assert.Equal("data source=:memory:", client.Database.GetConnectionString()); + Assert.Equal("data source=:memory:", client.Database.GetConnectionString(), ignoreCase: true); } [Fact] @@ -38,7 +38,7 @@ public void CanSetConnectionStringInCode() var client = host.Services.GetRequiredService(); Assert.NotNull(client.Database.GetConnectionString()); - Assert.Equal("data source=:memory:", client.Database.GetConnectionString()); + Assert.Equal("data source=:memory:", client.Database.GetConnectionString(), ignoreCase: true); } [Fact] @@ -56,6 +56,6 @@ public void CanSetConnectionStringInCodeWithKey() var client = host.Services.GetRequiredService(); Assert.NotNull(client.Database.GetConnectionString()); - Assert.Equal("data source=:memory:", client.Database.GetConnectionString()); + Assert.Equal("data source=:memory:", client.Database.GetConnectionString(), ignoreCase: true); } } \ No newline at end of file