From 0b0b337140e35b03ebf4b38b47c3aa88009b9d4b Mon Sep 17 00:00:00 2001 From: Dagalon <63908589+Dagalon@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:23:28 +0200 Subject: [PATCH 1/4] Fix DuckDB parameterized reader test --- DuckDB-Memory-Lib/ConnectionManager.cs | 110 +++--------------- DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj | 8 +- DuckDB-Memory-Lib/DuckTools.cs | 4 +- DuckDB-Memory-Lib/QueryExecutor.cs | 48 ++++++-- DuckDB-Memory-Tests/T_DuckTools.cs | 26 ++++- DuckDB-Memory-Tests/T_Execute_Queries.cs | 18 ++- Shared/ConnectionManagerBase.cs | 98 ++++++++++++++++ SqliteDB-Memory-Lib/ConnectionManager.cs | 96 +-------------- .../SqliteDB-Memory-Lib.csproj | 4 + 9 files changed, 206 insertions(+), 206 deletions(-) create mode 100644 Shared/ConnectionManagerBase.cs diff --git a/DuckDB-Memory-Lib/ConnectionManager.cs b/DuckDB-Memory-Lib/ConnectionManager.cs index 5a2f800..aa55ece 100644 --- a/DuckDB-Memory-Lib/ConnectionManager.cs +++ b/DuckDB-Memory-Lib/ConnectionManager.cs @@ -1,17 +1,14 @@ -using System.Data; using DuckDB.NET.Data; +using MemoryDb_Lib.Shared; namespace DuckDb_Memory_Lib; -public sealed class ConnectionManager +public sealed class ConnectionManager : ConnectionManagerBase { private static readonly Lazy LazyInstance = new(() => new ConnectionManager(), LazyThreadSafetyMode.ExecutionAndPublication); - private readonly object _syncRoot = new(); - private readonly Dictionary _connections = new(StringComparer.OrdinalIgnoreCase); - private ConnectionManager() { } /// @@ -23,98 +20,15 @@ public static ConnectionManager GetInstance() } /// - /// Retrieves an open SQLite connection identified by the provided alias, creating it when necessary. + /// Retrieves an open DuckDB connection identified by the provided alias, creating it when necessary. /// public DuckDBConnection GetConnection(string? alias = null, string? path = null) { - var normalizedAlias = NormalizeAlias(alias); - - lock (_syncRoot) - { - if (_connections.TryGetValue(normalizedAlias, out var existingConnection)) - { - EnsureOpen(existingConnection); - return existingConnection; - } - - var newConnection = DuckTools.GetInstance(path); - EnsureOpen(newConnection); - _connections[normalizedAlias] = newConnection; - - return newConnection; - } - } - - /// - /// Closes and disposes the connection associated with the provided alias. - /// - public void CloseConnection(string? alias = null) - { - var normalizedAlias = NormalizeAlias(alias); - - lock (_syncRoot) - { - if (!_connections.TryGetValue(normalizedAlias, out var connection)) - { - return; - } - - try - { - connection.Close(); - } - finally - { - connection.Dispose(); - _connections.Remove(normalizedAlias); - } - } - } - - /// - /// Closes and disposes all active SQLite connections managed by this instance. - /// - public void CloseAllConnections() - { - lock (_syncRoot) - { - foreach (var connection in _connections.Values) - { - try - { - connection.Close(); - } - finally - { - connection.Dispose(); - } - } - - _connections.Clear(); - } + return GetConnectionCore(alias, path, DuckTools.GetInstance); } /// - /// Opens the provided SQLite connection when it is not already open. - /// - private static void EnsureOpen(DuckDBConnection connection) - { - if (connection.State != ConnectionState.Open) - { - connection.Open(); - } - } - - /// - /// Normalizes aliases to ensure consistent lookups inside the connection dictionary. - /// - private static string NormalizeAlias(string? alias) - { - return string.IsNullOrWhiteSpace(alias) ? "default" : alias.Trim(); - } - - /// - /// Static helper that delegates to . + /// Static helper that delegates to . /// public static void Close(string? alias = null) { @@ -128,7 +42,7 @@ public static void CloseAll() { GetInstance().CloseAllConnections(); } - } +} public sealed class KeeperRegisterIdDataBase { @@ -182,11 +96,13 @@ public static void Register(string path, string idDataBase) /// public static void DeleteRegister(string idDataBase) { - var keepIdDataBases = _mapIdDataBase.Values.ToList(); - int indexValues = keepIdDataBases.IndexOf(idDataBase); - string pathIdDataBase = _mapIdDataBase.Keys.ToList()[indexValues]; - - _mapIdDataBase.Remove(pathIdDataBase); + var pathIdDataBase = _mapIdDataBase + .FirstOrDefault(entry => string.Equals(entry.Value, idDataBase, StringComparison.OrdinalIgnoreCase)) + .Key; + if (pathIdDataBase is not null) + { + _mapIdDataBase.Remove(pathIdDataBase); + } } } diff --git a/DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj b/DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj index 55d0e02..2729815 100644 --- a/DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj +++ b/DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj @@ -1,4 +1,4 @@ - + DuckDb_Memory_Lib @@ -7,7 +7,11 @@ - + + + + + diff --git a/DuckDB-Memory-Lib/DuckTools.cs b/DuckDB-Memory-Lib/DuckTools.cs index 59df907..12d7d57 100644 --- a/DuckDB-Memory-Lib/DuckTools.cs +++ b/DuckDB-Memory-Lib/DuckTools.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using DuckDB.NET.Data; using DuckDB.NET.Data.DataChunk.Reader; using DuckDB.NET.Data.DataChunk.Writer; @@ -177,7 +177,7 @@ public static EnumsDuckMemory.Output AttachedDataBase(DuckDBConnection db, strin } else { - attachedQry = $"ATTACH '{strConnection}' AS \"{aliasDataBase}\" "; + attachedQry = $"ATTACH '{strConnection}' AS {QuoteIdentifier(aliasDataBase)}"; } try diff --git a/DuckDB-Memory-Lib/QueryExecutor.cs b/DuckDB-Memory-Lib/QueryExecutor.cs index c3c0693..911b402 100644 --- a/DuckDB-Memory-Lib/QueryExecutor.cs +++ b/DuckDB-Memory-Lib/QueryExecutor.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using DuckDB.NET.Data; using System.IO; @@ -30,15 +30,16 @@ public static EnumsDuckMemory.Output CreateTable(DuckDBConnection db, string idD for (var i = 0; i < noFields; i++) { var t = NetTypeToDuckDbType.GetDuckDbType(types[i]); - fieldsDefinition.Add(headers[i] + " " + t); + fieldsDefinition.Add(DuckTools.QuoteIdentifier(headers[i]) + " " + t); } } try { - var qry = $"CREATE TABLE IF NOT EXISTS {idDataBase}.{idTable}({string.Join(",", fieldsDefinition)})"; - var cmd = new DuckDBCommand(qry, db); + var tableName = $"{DuckTools.QuoteIdentifier(idDataBase)}.{DuckTools.QuoteIdentifier(idTable)}"; + var qry = $"CREATE TABLE IF NOT EXISTS {tableName}({string.Join(",", fieldsDefinition)})"; + using var cmd = new DuckDBCommand(qry, db); cmd.ExecuteNonQuery(); return EnumsDuckMemory.Output.SUCCESS; @@ -71,7 +72,7 @@ public static EnumsDuckMemory.Output CreateParquetTable(DuckDBConnection db, str var tableName = $"{DuckTools.QuoteIdentifier(idDataBase)}.{DuckTools.QuoteIdentifier(idTable)}"; var parquetPath = parquetPathFile.Replace("'", "''"); var qry = $"CREATE OR REPLACE TABLE {tableName} AS SELECT * FROM '{parquetPath}';"; - var cmd = new DuckDBCommand(qry, db); + using var cmd = new DuckDBCommand(qry, db); cmd.ExecuteNonQuery(); return EnumsDuckMemory.Output.SUCCESS; @@ -90,8 +91,8 @@ public static (EnumsDuckMemory.Output, List>) Execute { try { - var cmd = new DuckDBCommand(qry, db); - var qryResult = cmd.ExecuteReader(); + using var cmd = new DuckDBCommand(qry, db); + using var qryResult = cmd.ExecuteReader(); var resultList = new List>(); @@ -104,7 +105,6 @@ public static (EnumsDuckMemory.Output, List>) Execute } } - qryResult.Close(); return (EnumsDuckMemory.Output.SUCCESS, resultList); } catch (Exception ex) @@ -121,14 +121,38 @@ public static (EnumsDuckMemory.Output, List>) Execute { try { - qry = parameters.Keys.Aggregate(qry, - (current, param) => current.Replace(param, parameters[param], StringComparison.OrdinalIgnoreCase)); - return ExecuteQryReader(db, qry); + using var cmd = new DuckDBCommand(qry, db); + foreach (var parameter in parameters) + { + var dbParameter = cmd.CreateParameter(); + dbParameter.ParameterName = NormalizeParameterName(parameter.Key); + dbParameter.Value = parameter.Value; + cmd.Parameters.Add(dbParameter); + } + + using var qryResult = cmd.ExecuteReader(); + var resultList = new List>(); + + if (qryResult.HasRows) + { + while (qryResult.Read()) + { + resultList.Add(Enumerable.Range(0, qryResult.FieldCount) + .ToDictionary(qryResult.GetName, qryResult.GetValue)); + } + } + + return (EnumsDuckMemory.Output.SUCCESS, resultList); } catch (Exception ex) { Debug.WriteLine(ex); - throw new Exception($"{ex.Message}-{EnumsDuckMemory.Output.ERROR_TO_EXECUTE_QUERY}", ex); + return (EnumsDuckMemory.Output.ERROR_TO_EXECUTE_QUERY, []); } } + + private static string NormalizeParameterName(string parameterName) + { + return parameterName.TrimStart('@', '$', ':'); + } } \ No newline at end of file diff --git a/DuckDB-Memory-Tests/T_DuckTools.cs b/DuckDB-Memory-Tests/T_DuckTools.cs index 4ca40a9..9af0ca4 100644 --- a/DuckDB-Memory-Tests/T_DuckTools.cs +++ b/DuckDB-Memory-Tests/T_DuckTools.cs @@ -1,4 +1,4 @@ -using DuckDb_Memory_Lib; +using DuckDb_Memory_Lib; namespace DuckDB_Memory_Tests; @@ -88,6 +88,30 @@ public void T_GetListTables_And_DropTable_Handle_Existing_And_Missing_Tables() }); } + + [Test] + public void T_DeleteRegister_Ignores_Missing_Id() + { + Assert.DoesNotThrow(() => KeeperRegisterIdDataBase.DeleteRegister($"MISSING_{Guid.NewGuid():N}")); + } + + [Test] + public void T_CreateTable_Quotes_Database_And_Table_Identifiers() + { + var databaseId = $"TOOLS DB {Guid.NewGuid():N}"; + var tableId = $"TOOLS TABLE {Guid.NewGuid():N}"; + var conn = ConnectionManager.GetInstance().GetConnection(databaseId); + + var dbOutput = DuckTools.CreateDatabase(conn, databaseId, null); + Assert.That(dbOutput, Is.EqualTo(EnumsDuckMemory.Output.SUCCESS)); + + var createOutput = QueryExecutor.CreateTable(conn, databaseId, tableId, ["id INTEGER"]); + Assert.That(createOutput, Is.EqualTo(EnumsDuckMemory.Output.SUCCESS)); + + var listTables = DuckTools.GetListTables(conn, databaseId); + Assert.That(listTables.Item2, Does.Contain(tableId)); + } + [Test] public void T_DeleteDataBase_Detaches_Existing_Database_And_Validates_Blank_Id() { diff --git a/DuckDB-Memory-Tests/T_Execute_Queries.cs b/DuckDB-Memory-Tests/T_Execute_Queries.cs index 9158797..52e2937 100644 --- a/DuckDB-Memory-Tests/T_Execute_Queries.cs +++ b/DuckDB-Memory-Tests/T_Execute_Queries.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using DuckDb_Memory_Lib; namespace DuckDB_Memory_Tests; @@ -41,4 +41,20 @@ public void T_Create_Table_From_Parquet() Assert.That(resultQry.Item1, Is.EqualTo(EnumsDuckMemory.Output.SUCCESS)); } + [Test] + public void T_ExecuteQryReader_Uses_DuckDb_Parameters() + { + var conn = ConnectionManager.GetInstance().GetConnection($"PARAM_DB_{Guid.NewGuid():N}"); + var result = QueryExecutor.ExecuteQryReader( + conn, + "SELECT $value AS VALUE", + new Dictionary { ["value"] = "O'Reilly" }); + + Assert.Multiple(() => + { + Assert.That(result.Item1, Is.EqualTo(EnumsDuckMemory.Output.SUCCESS)); + Assert.That(result.Item2.Single()["VALUE"], Is.EqualTo("O'Reilly")); + }); + } + } diff --git a/Shared/ConnectionManagerBase.cs b/Shared/ConnectionManagerBase.cs new file mode 100644 index 0000000..d2fba45 --- /dev/null +++ b/Shared/ConnectionManagerBase.cs @@ -0,0 +1,98 @@ +using System.Data; +using System.Data.Common; + +namespace MemoryDb_Lib.Shared; + +/// +/// Thread-safe base implementation for named database connection managers. +/// +public abstract class ConnectionManagerBase where TConnection : DbConnection +{ + private readonly object _syncRoot = new(); + private readonly Dictionary _connections = new(StringComparer.OrdinalIgnoreCase); + + /// + /// Retrieves an open connection identified by the provided alias, creating it when necessary. + /// + protected TConnection GetConnectionCore(string? alias, string? path, Func connectionFactory) + { + var normalizedAlias = NormalizeAlias(alias); + + lock (_syncRoot) + { + if (_connections.TryGetValue(normalizedAlias, out var existingConnection)) + { + EnsureOpen(existingConnection); + return existingConnection; + } + + var newConnection = connectionFactory(path); + EnsureOpen(newConnection); + _connections[normalizedAlias] = newConnection; + + return newConnection; + } + } + + /// + /// Closes and disposes the connection associated with the provided alias. + /// + public void CloseConnection(string? alias = null) + { + var normalizedAlias = NormalizeAlias(alias); + + lock (_syncRoot) + { + if (!_connections.TryGetValue(normalizedAlias, out var connection)) + { + return; + } + + try + { + connection.Close(); + } + finally + { + connection.Dispose(); + _connections.Remove(normalizedAlias); + } + } + } + + /// + /// Closes and disposes all active connections managed by this instance. + /// + public void CloseAllConnections() + { + lock (_syncRoot) + { + foreach (var connection in _connections.Values) + { + try + { + connection.Close(); + } + finally + { + connection.Dispose(); + } + } + + _connections.Clear(); + } + } + + private static void EnsureOpen(TConnection connection) + { + if (connection.State != ConnectionState.Open) + { + connection.Open(); + } + } + + private static string NormalizeAlias(string? alias) + { + return string.IsNullOrWhiteSpace(alias) ? "default" : alias.Trim(); + } +} diff --git a/SqliteDB-Memory-Lib/ConnectionManager.cs b/SqliteDB-Memory-Lib/ConnectionManager.cs index be61eeb..9fd0065 100644 --- a/SqliteDB-Memory-Lib/ConnectionManager.cs +++ b/SqliteDB-Memory-Lib/ConnectionManager.cs @@ -1,16 +1,13 @@ using Microsoft.Data.Sqlite; -using System.Data; +using MemoryDb_Lib.Shared; namespace SqliteDB_Memory_Lib; -public sealed class ConnectionManager +public sealed class ConnectionManager : ConnectionManagerBase { private static readonly Lazy LazyInstance = new(() => new ConnectionManager(), LazyThreadSafetyMode.ExecutionAndPublication); - private readonly object _syncRoot = new(); - private readonly Dictionary _connections = new(StringComparer.OrdinalIgnoreCase); - private ConnectionManager() { } /// @@ -26,94 +23,11 @@ public static ConnectionManager GetInstance() /// public SqliteConnection GetConnection(string? alias = null, string? path = null) { - var normalizedAlias = NormalizeAlias(alias); - - lock (_syncRoot) - { - if (_connections.TryGetValue(normalizedAlias, out var existingConnection)) - { - EnsureOpen(existingConnection); - return existingConnection; - } - - var newConnection = SqLiteLiteTools.GetInstance(path); - EnsureOpen(newConnection); - _connections[normalizedAlias] = newConnection; - - return newConnection; - } - } - - /// - /// Closes and disposes the connection associated with the provided alias. - /// - public void CloseConnection(string? alias = null) - { - var normalizedAlias = NormalizeAlias(alias); - - lock (_syncRoot) - { - if (!_connections.TryGetValue(normalizedAlias, out var connection)) - { - return; - } - - try - { - connection.Close(); - } - finally - { - connection.Dispose(); - _connections.Remove(normalizedAlias); - } - } + return GetConnectionCore(alias, path, SqLiteLiteTools.GetInstance); } /// - /// Closes and disposes all active SQLite connections managed by this instance. - /// - public void CloseAllConnections() - { - lock (_syncRoot) - { - foreach (var connection in _connections.Values) - { - try - { - connection.Close(); - } - finally - { - connection.Dispose(); - } - } - - _connections.Clear(); - } - } - - /// - /// Opens the provided SQLite connection when it is not already open. - /// - private static void EnsureOpen(SqliteConnection connection) - { - if (connection.State != ConnectionState.Open) - { - connection.Open(); - } - } - - /// - /// Normalizes aliases to ensure consistent lookups inside the connection dictionary. - /// - private static string NormalizeAlias(string? alias) - { - return string.IsNullOrWhiteSpace(alias) ? "default" : alias.Trim(); - } - - /// - /// Static helper that delegates to . + /// Static helper that delegates to . /// public static void Close(string? alias = null) { @@ -127,7 +41,7 @@ public static void CloseAll() { GetInstance().CloseAllConnections(); } - } +} public sealed class KeeperRegisterIdDataBase { diff --git a/SqliteDB-Memory-Lib/SqliteDB-Memory-Lib.csproj b/SqliteDB-Memory-Lib/SqliteDB-Memory-Lib.csproj index 457f1f0..afe9a85 100644 --- a/SqliteDB-Memory-Lib/SqliteDB-Memory-Lib.csproj +++ b/SqliteDB-Memory-Lib/SqliteDB-Memory-Lib.csproj @@ -9,4 +9,8 @@ + + + + From 148316032ee148c07ad83537fea709293fd01af7 Mon Sep 17 00:00:00 2001 From: Dagalon <63908589+Dagalon@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:38:04 +0200 Subject: [PATCH 2/4] Use DuckDBParameter for reader parameters --- DuckDB-Memory-Lib/QueryExecutor.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/DuckDB-Memory-Lib/QueryExecutor.cs b/DuckDB-Memory-Lib/QueryExecutor.cs index 911b402..9381a65 100644 --- a/DuckDB-Memory-Lib/QueryExecutor.cs +++ b/DuckDB-Memory-Lib/QueryExecutor.cs @@ -124,10 +124,9 @@ public static (EnumsDuckMemory.Output, List>) Execute using var cmd = new DuckDBCommand(qry, db); foreach (var parameter in parameters) { - var dbParameter = cmd.CreateParameter(); - dbParameter.ParameterName = NormalizeParameterName(parameter.Key); - dbParameter.Value = parameter.Value; - cmd.Parameters.Add(dbParameter); + cmd.Parameters.Add(new DuckDBParameter( + NormalizeParameterName(parameter.Key), + parameter.Value)); } using var qryResult = cmd.ExecuteReader(); From 19af842885434a33ca4ca6c49d090c2fb73716aa Mon Sep 17 00:00:00 2001 From: Dagalon <63908589+Dagalon@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:38:08 +0200 Subject: [PATCH 3/4] Avoid named DuckDB parameter in debug-sensitive test --- DuckDB-Memory-Tests/T_Execute_Queries.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDB-Memory-Tests/T_Execute_Queries.cs b/DuckDB-Memory-Tests/T_Execute_Queries.cs index 52e2937..6782061 100644 --- a/DuckDB-Memory-Tests/T_Execute_Queries.cs +++ b/DuckDB-Memory-Tests/T_Execute_Queries.cs @@ -47,7 +47,7 @@ public void T_ExecuteQryReader_Uses_DuckDb_Parameters() var conn = ConnectionManager.GetInstance().GetConnection($"PARAM_DB_{Guid.NewGuid():N}"); var result = QueryExecutor.ExecuteQryReader( conn, - "SELECT $value AS VALUE", + "SELECT ? AS VALUE", new Dictionary { ["value"] = "O'Reilly" }); Assert.Multiple(() => From dd65a3a13ac8964889aa56f719370dce2e36e8f4 Mon Sep 17 00:00:00 2001 From: Dagalon <63908589+Dagalon@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:37:17 +0200 Subject: [PATCH 4/4] Upgrade net 10.0 --- Directory.Build.props | 4 ++-- DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj | 2 +- DuckDB-Memory-Lib/DuckTools.cs | 3 +-- DuckDB-Memory-Tests/T_Execute_Queries.cs | 8 +++++--- XLS-Memory-Lib/Properties/launchSettings.json | 9 +++++++++ XLS-Memory-Lib/XLS-Memory-Lib.csproj | 2 +- 6 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 XLS-Memory-Lib/Properties/launchSettings.json diff --git a/Directory.Build.props b/Directory.Build.props index 2986c46..90dab28 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - net8.0-windows + net10.0-windows 13.0 enable enable @@ -20,6 +20,6 @@ - + diff --git a/DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj b/DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj index 2729815..a71a0cc 100644 --- a/DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj +++ b/DuckDB-Memory-Lib/DuckDB-Memory-Lib.csproj @@ -7,7 +7,7 @@ - + diff --git a/DuckDB-Memory-Lib/DuckTools.cs b/DuckDB-Memory-Lib/DuckTools.cs index 12d7d57..1defec4 100644 --- a/DuckDB-Memory-Lib/DuckTools.cs +++ b/DuckDB-Memory-Lib/DuckTools.cs @@ -128,8 +128,7 @@ public static EnumsDuckMemory.Output RegisterScalarFunction( { db.RegisterScalarFunction( idFunction, - func, - isPureFunction: true); + func); return EnumsDuckMemory.Output.SUCCESS; } diff --git a/DuckDB-Memory-Tests/T_Execute_Queries.cs b/DuckDB-Memory-Tests/T_Execute_Queries.cs index 6782061..3e7f8f2 100644 --- a/DuckDB-Memory-Tests/T_Execute_Queries.cs +++ b/DuckDB-Memory-Tests/T_Execute_Queries.cs @@ -44,11 +44,13 @@ public void T_Create_Table_From_Parquet() [Test] public void T_ExecuteQryReader_Uses_DuckDb_Parameters() { - var conn = ConnectionManager.GetInstance().GetConnection($"PARAM_DB_{Guid.NewGuid():N}"); + var manager = ConnectionManager.GetInstance(); + var conn = manager.GetConnection(); + var result = QueryExecutor.ExecuteQryReader( conn, - "SELECT ? AS VALUE", - new Dictionary { ["value"] = "O'Reilly" }); + "SELECT $param AS VALUE", + new Dictionary { ["param"] = "O'Reilly" }); Assert.Multiple(() => { diff --git a/XLS-Memory-Lib/Properties/launchSettings.json b/XLS-Memory-Lib/Properties/launchSettings.json new file mode 100644 index 0000000..8fec72d --- /dev/null +++ b/XLS-Memory-Lib/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Excel": { + "commandName": "Executable", + "executablePath": "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE", + "commandLineArgs": "/x \"XLS-Memory-Lib.xll\"" + } + } +} \ No newline at end of file diff --git a/XLS-Memory-Lib/XLS-Memory-Lib.csproj b/XLS-Memory-Lib/XLS-Memory-Lib.csproj index f271d45..0251e71 100644 --- a/XLS-Memory-Lib/XLS-Memory-Lib.csproj +++ b/XLS-Memory-Lib/XLS-Memory-Lib.csproj @@ -1,7 +1,7 @@ - net8.0-windows + net10.0-windows v8.0