A:Access接口增加可加密选项
This commit is contained in:
1 parent
dd993e5b45
commit
2739a71134
12 files changed
+469
-252
No files matched your search
@@ -1,8 +1,12 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.OleDb;
|
using System.Data.OleDb;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Sharp.Data.Access.Connections;
|
||||||
|
using Sharp.Data.Access.Initialization;
|
||||||
|
using Sharp.Data.Access.Internal;
|
||||||
|
using Sharp.Data.Access.Options;
|
||||||
|
using Sharp.Data.Access.Security;
|
||||||
|
|
||||||
namespace Sharp.Data.Access;
|
namespace Sharp.Data.Access;
|
||||||
|
|
||||||
@@ -14,26 +18,27 @@ internal class AccessTransaction : IAccessTransaction
|
|||||||
private readonly OleDbConnection _connection;
|
private readonly OleDbConnection _connection;
|
||||||
private readonly OleDbTransaction _transaction;
|
private readonly OleDbTransaction _transaction;
|
||||||
private readonly ILogger? _logger;
|
private readonly ILogger? _logger;
|
||||||
|
private readonly bool _ownsConnection;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public AccessTransaction(OleDbConnection connection, ILogger? logger)
|
public AccessTransaction(OleDbConnection connection, bool ownsConnection, ILogger? logger)
|
||||||
{
|
{
|
||||||
_connection = connection;
|
_connection = connection;
|
||||||
|
_ownsConnection = ownsConnection;
|
||||||
_transaction = connection.BeginTransaction();
|
_transaction = connection.BeginTransaction();
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> ExecuteAsync(string sql, params OleDbParameter[] parameters)
|
public int Execute(string sql, params OleDbParameter[] parameters)
|
||||||
{
|
{
|
||||||
using var command = new OleDbCommand(sql, _connection, _transaction);
|
using var command = new OleDbCommand(sql, _connection, _transaction);
|
||||||
if (parameters.Length > 0)
|
if (parameters.Length > 0)
|
||||||
command.Parameters.AddRange(parameters);
|
command.Parameters.AddRange(parameters);
|
||||||
|
|
||||||
var result = command.ExecuteNonQuery();
|
return command.ExecuteNonQuery();
|
||||||
return Task.FromResult(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<List<T>> QueryAsync<T>(string sql, params OleDbParameter[] parameters) where T : new()
|
public List<T> Query<T>(string sql, params OleDbParameter[] parameters) where T : new()
|
||||||
{
|
{
|
||||||
using var command = new OleDbCommand(sql, _connection, _transaction);
|
using var command = new OleDbCommand(sql, _connection, _transaction);
|
||||||
if (parameters.Length > 0)
|
if (parameters.Length > 0)
|
||||||
@@ -48,17 +53,16 @@ internal class AccessTransaction : IAccessTransaction
|
|||||||
results.Add(item);
|
results.Add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult(results);
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<object?> ExecuteScalarAsync(string sql, params OleDbParameter[] parameters)
|
public object? ExecuteScalar(string sql, params OleDbParameter[] parameters)
|
||||||
{
|
{
|
||||||
using var command = new OleDbCommand(sql, _connection, _transaction);
|
using var command = new OleDbCommand(sql, _connection, _transaction);
|
||||||
if (parameters.Length > 0)
|
if (parameters.Length > 0)
|
||||||
command.Parameters.AddRange(parameters);
|
command.Parameters.AddRange(parameters);
|
||||||
|
|
||||||
var result = command.ExecuteScalar();
|
return command.ExecuteScalar();
|
||||||
return Task.FromResult(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Commit()
|
public void Commit()
|
||||||
@@ -77,8 +81,18 @@ internal class AccessTransaction : IAccessTransaction
|
|||||||
{
|
{
|
||||||
if (_disposed) return;
|
if (_disposed) return;
|
||||||
|
|
||||||
_transaction.Dispose();
|
try
|
||||||
_connection.Dispose();
|
{
|
||||||
|
_transaction.Dispose();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (_ownsConnection)
|
||||||
|
{
|
||||||
|
_connection.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,34 +158,65 @@ internal class AccessTransaction : IAccessTransaction
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Access 数据服务实现
|
/// Access 数据服务实现(基于 System.Data.OleDb)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AccessDataService : IAccessDataService
|
public class AccessDataService : IAccessDataService
|
||||||
{
|
{
|
||||||
private readonly AccessDataServiceOptions _options;
|
private readonly AccessDatabaseOptions _options;
|
||||||
|
private readonly IAccessConnectionStringFactory _connectionFactory;
|
||||||
|
private readonly IAccessDatabaseInitializer _initializer;
|
||||||
|
private readonly IAccessPasswordDecryptor _decryptor;
|
||||||
private readonly ILogger<AccessDataService>? _logger;
|
private readonly ILogger<AccessDataService>? _logger;
|
||||||
|
private readonly string _connectionString;
|
||||||
|
|
||||||
public AccessDataService(AccessDataServiceOptions options, ILogger<AccessDataService>? logger = null)
|
public AccessDataService(
|
||||||
|
AccessDatabaseOptions options,
|
||||||
|
IAccessConnectionStringFactory? connectionFactory = null,
|
||||||
|
IAccessDatabaseInitializer? initializer = null,
|
||||||
|
IAccessPasswordDecryptor? decryptor = null,
|
||||||
|
ILogger<AccessDataService>? logger = null)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
_options.Validate();
|
||||||
|
|
||||||
|
_connectionFactory = connectionFactory ?? new AccessConnectionStringFactory(decryptor ?? new PassthroughAccessPasswordDecryptor());
|
||||||
|
_initializer = initializer ?? new AccessDatabaseInitializer();
|
||||||
|
_decryptor = decryptor ?? new PassthroughAccessPasswordDecryptor();
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_options.DatabasePath))
|
try
|
||||||
{
|
{
|
||||||
throw new ArgumentException("数据库路径不能为空", nameof(options.DatabasePath));
|
// 确保数据库存在(如果配置了模板复制)
|
||||||
}
|
_initializer.EnsureDatabase(_options, NullServiceProvider.Instance);
|
||||||
|
|
||||||
if (!File.Exists(_options.DatabasePath))
|
// 构建连接字符串
|
||||||
|
_connectionString = _connectionFactory.Create(_options, NullServiceProvider.Instance);
|
||||||
|
|
||||||
|
_logger?.LogInformation("AccessDataService 已初始化,数据库: {DatabaseFile}, Provider: {Provider}",
|
||||||
|
_options.DatabaseFile, _options.ProviderName);
|
||||||
|
_logger?.LogDebug("实际连接字符串: {ConnectionString}", _connectionString);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException($"找不到 Access 数据库文件: {_options.DatabasePath}");
|
_logger?.LogError(ex, "AccessDataService 初始化失败");
|
||||||
|
|
||||||
|
// 提供详细的错误诊断信息
|
||||||
|
if (ex.Message.Contains("找不到") || ex is FileNotFoundException)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"找不到 Access 数据库文件: {_options.DatabaseFile}。" +
|
||||||
|
$"请确保文件存在或配置 TemplateFile 以自动创建。", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<T>> QueryAsync<T>(string sql, params OleDbParameter[] parameters) where T : new()
|
public List<T> Query<T>(string sql, params OleDbParameter[] parameters) where T : new()
|
||||||
{
|
{
|
||||||
_logger?.LogDebug("执行查询: {Sql}", sql);
|
_logger?.LogDebug("执行查询: {Sql}", sql);
|
||||||
|
|
||||||
return await Task.Run(() =>
|
return ExecuteWithRetry(() =>
|
||||||
{
|
{
|
||||||
using var connection = CreateConnection();
|
using var connection = CreateConnection();
|
||||||
connection.Open();
|
connection.Open();
|
||||||
@@ -183,29 +228,8 @@ public class AccessDataService : IAccessDataService
|
|||||||
var results = new List<T>();
|
var results = new List<T>();
|
||||||
using var reader = command.ExecuteReader();
|
using var reader = command.ExecuteReader();
|
||||||
|
|
||||||
LogFieldMap(reader);
|
|
||||||
|
|
||||||
if (_logger?.IsEnabled(LogLevel.Information) == true)
|
|
||||||
{
|
|
||||||
var columns = Enumerable.Range(0, reader.FieldCount)
|
|
||||||
.Select(i => $"{reader.GetName(i)}(idx:{i})")
|
|
||||||
.ToArray();
|
|
||||||
_logger.LogInformation("查询列: {Columns}", string.Join(", ", columns));
|
|
||||||
}
|
|
||||||
|
|
||||||
var rowIndex = 0;
|
|
||||||
while (reader.Read())
|
while (reader.Read())
|
||||||
{
|
{
|
||||||
rowIndex++;
|
|
||||||
|
|
||||||
if (_logger?.IsEnabled(LogLevel.Information) == true)
|
|
||||||
{
|
|
||||||
var fields = Enumerable.Range(0, reader.FieldCount)
|
|
||||||
.Select(i => $"{reader.GetName(i)}='{SafeGetValue(reader, i)}'")
|
|
||||||
.ToArray();
|
|
||||||
_logger.LogInformation("Row {RowIndex}: {Fields}", rowIndex, string.Join(", ", fields));
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = MapReaderToObject<T>(reader);
|
var item = MapReaderToObject<T>(reader);
|
||||||
results.Add(item);
|
results.Add(item);
|
||||||
}
|
}
|
||||||
@@ -215,11 +239,11 @@ public class AccessDataService : IAccessDataService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T?> QueryFirstOrDefaultAsync<T>(string sql, params OleDbParameter[] parameters) where T : new()
|
public T? QueryFirstOrDefault<T>(string sql, params OleDbParameter[] parameters) where T : new()
|
||||||
{
|
{
|
||||||
_logger?.LogDebug("执行单条查询: {Sql}", sql);
|
_logger?.LogDebug("执行单条查询: {Sql}", sql);
|
||||||
|
|
||||||
return await Task.Run(() =>
|
return ExecuteWithRetry(() =>
|
||||||
{
|
{
|
||||||
using var connection = CreateConnection();
|
using var connection = CreateConnection();
|
||||||
connection.Open();
|
connection.Open();
|
||||||
@@ -230,14 +254,6 @@ public class AccessDataService : IAccessDataService
|
|||||||
|
|
||||||
using var reader = command.ExecuteReader();
|
using var reader = command.ExecuteReader();
|
||||||
|
|
||||||
if (_logger?.IsEnabled(LogLevel.Debug) == true)
|
|
||||||
{
|
|
||||||
var columns = Enumerable.Range(0, reader.FieldCount)
|
|
||||||
.Select(reader.GetName)
|
|
||||||
.ToArray();
|
|
||||||
_logger.LogDebug("查询列: {Columns}", string.Join(", ", columns));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader.Read())
|
if (reader.Read())
|
||||||
{
|
{
|
||||||
return MapReaderToObject<T>(reader);
|
return MapReaderToObject<T>(reader);
|
||||||
@@ -247,11 +263,11 @@ public class AccessDataService : IAccessDataService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object?> ExecuteScalarAsync(string sql, params OleDbParameter[] parameters)
|
public object? ExecuteScalar(string sql, params OleDbParameter[] parameters)
|
||||||
{
|
{
|
||||||
_logger?.LogDebug("执行标量查询: {Sql}", sql);
|
_logger?.LogDebug("执行标量查询: {Sql}", sql);
|
||||||
|
|
||||||
return await Task.Run(() =>
|
return ExecuteWithRetry(() =>
|
||||||
{
|
{
|
||||||
using var connection = CreateConnection();
|
using var connection = CreateConnection();
|
||||||
connection.Open();
|
connection.Open();
|
||||||
@@ -266,11 +282,11 @@ public class AccessDataService : IAccessDataService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> ExecuteAsync(string sql, params OleDbParameter[] parameters)
|
public int Execute(string sql, params OleDbParameter[] parameters)
|
||||||
{
|
{
|
||||||
_logger?.LogDebug("执行命令: {Sql}", sql);
|
_logger?.LogDebug("执行命令: {Sql}", sql);
|
||||||
|
|
||||||
return await Task.Run(() =>
|
return ExecuteWithRetry(() =>
|
||||||
{
|
{
|
||||||
using var connection = CreateConnection();
|
using var connection = CreateConnection();
|
||||||
connection.Open();
|
connection.Open();
|
||||||
@@ -290,17 +306,17 @@ public class AccessDataService : IAccessDataService
|
|||||||
_logger?.LogDebug("开始事务");
|
_logger?.LogDebug("开始事务");
|
||||||
var connection = CreateConnection();
|
var connection = CreateConnection();
|
||||||
connection.Open();
|
connection.Open();
|
||||||
return new AccessTransaction(connection, _logger);
|
return new AccessTransaction(connection, ownsConnection: true, _logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExecuteInTransactionAsync(Func<IAccessTransaction, Task> action)
|
public void ExecuteInTransaction(Action<IAccessTransaction> action)
|
||||||
{
|
{
|
||||||
_logger?.LogDebug("在事务中执行操作");
|
_logger?.LogDebug("在事务中执行操作");
|
||||||
|
|
||||||
using var transaction = BeginTransaction();
|
using var transaction = BeginTransaction();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await action(transaction);
|
action(transaction);
|
||||||
transaction.Commit();
|
transaction.Commit();
|
||||||
_logger?.LogDebug("事务执行成功");
|
_logger?.LogDebug("事务执行成功");
|
||||||
}
|
}
|
||||||
@@ -312,13 +328,13 @@ public class AccessDataService : IAccessDataService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> TestConnectionAsync()
|
public bool TestConnection()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger?.LogDebug("测试数据库连接");
|
_logger?.LogDebug("测试数据库连接");
|
||||||
using var connection = CreateConnection();
|
using var connection = CreateConnection();
|
||||||
await connection.OpenAsync();
|
connection.Open();
|
||||||
_logger?.LogDebug("数据库连接成功");
|
_logger?.LogDebug("数据库连接成功");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -331,30 +347,60 @@ public class AccessDataService : IAccessDataService
|
|||||||
|
|
||||||
private OleDbConnection CreateConnection()
|
private OleDbConnection CreateConnection()
|
||||||
{
|
{
|
||||||
return new OleDbConnection(_options.ConnectionString);
|
return new OleDbConnection(_connectionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogFieldMap(IDataReader reader)
|
private T ExecuteWithRetry<T>(Func<T> operation)
|
||||||
{
|
{
|
||||||
if (!(_logger?.IsEnabled(LogLevel.Information) ?? false)) return;
|
var maxRetries = _options.MaxRetryCount;
|
||||||
|
var retryDelay = _options.RetryDelayMilliseconds;
|
||||||
|
|
||||||
var columns = Enumerable.Range(0, reader.FieldCount)
|
for (int attempt = 0; attempt <= maxRetries; attempt++)
|
||||||
.Select(i => $"'{reader.GetName(i)}'->idx:{i}")
|
{
|
||||||
.ToArray();
|
try
|
||||||
_logger!.LogInformation("字段映射: {Columns}", string.Join(", ", columns));
|
{
|
||||||
|
return operation();
|
||||||
|
}
|
||||||
|
catch (OleDbException ex) when (attempt < maxRetries && IsRetriableError(ex))
|
||||||
|
{
|
||||||
|
_logger?.LogWarning(ex, "数据库操作失败,正在重试 ({Attempt}/{MaxRetries})...", attempt + 1, maxRetries);
|
||||||
|
Thread.Sleep(retryDelay);
|
||||||
|
}
|
||||||
|
catch (OleDbException ex)
|
||||||
|
{
|
||||||
|
// 提供详细的错误诊断信息
|
||||||
|
var errorMessage = $"Access 数据库操作失败: {ex.Message}";
|
||||||
|
|
||||||
|
// 检查是否是 Provider 相关的错误
|
||||||
|
if (ex.Message.Contains("找不到") || ex.Message.Contains("未注册") ||
|
||||||
|
ex.Message.Contains("OLE DB") || ex.Message.Contains("多步"))
|
||||||
|
{
|
||||||
|
errorMessage += $"\n\n可能的原因:" +
|
||||||
|
$"\n1. 未安装 Access Database Engine:当前使用 Provider = {_options.ProviderName}" +
|
||||||
|
$"\n - 如果使用 16.0,需要安装 Access Database Engine 2016 或 Office 2016+" +
|
||||||
|
$"\n - 如果使用 12.0,需要安装 Access Database Engine 2010 或 Office 2010+" +
|
||||||
|
$"\n - 建议:优先使用 12.0 版本,兼容性最好" +
|
||||||
|
$"\n2. 32位/64位不匹配:进程架构必须与 Access Driver 架构一致" +
|
||||||
|
$"\n3. 数据库文件损坏或被其他程序锁定" +
|
||||||
|
$"\n4. 数据库文件路径不正确:{_options.DatabaseFile}";
|
||||||
|
|
||||||
|
_logger?.LogError(ex, "数据库连接失败,Provider: {Provider}", _options.ProviderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException(errorMessage, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不会到这里,因为最后一次尝试会抛出异常
|
||||||
|
throw new InvalidOperationException("重试逻辑错误");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object? SafeGetValue(IDataRecord reader, int ordinal)
|
private static bool IsRetriableError(OleDbException ex)
|
||||||
{
|
{
|
||||||
try
|
// Access 数据库常见的可重试错误
|
||||||
{
|
// -2147467259: 数据库被锁定
|
||||||
return reader.GetValue(ordinal);
|
// -2147217865: 无法打开数据库(可能正在被其他进程使用)
|
||||||
}
|
return ex.ErrorCode == -2147467259 || ex.ErrorCode == -2147217865;
|
||||||
catch
|
|
||||||
{
|
|
||||||
// 某些 OleDb 列类型在获取值时可能抛 COM 异常,这里做容错日志使用
|
|
||||||
return "<unavailable>";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T MapReaderToObject<T>(IDataReader reader) where T : new()
|
private static T MapReaderToObject<T>(IDataReader reader) where T : new()
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Sharp.Data.Access.Options;
|
||||||
|
using Sharp.Data.Access.Security;
|
||||||
|
using Sharp.Data.Access.Connections;
|
||||||
|
using Sharp.Data.Access.Initialization;
|
||||||
|
|
||||||
namespace Sharp.Data.Access;
|
namespace Sharp.Data.Access;
|
||||||
|
|
||||||
@@ -8,25 +13,63 @@ namespace Sharp.Data.Access;
|
|||||||
public static class AccessDataServiceExtensions
|
public static class AccessDataServiceExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加 Access 数据服务
|
/// 添加 Access 数据服务(最简单方式 - 仅指定数据库路径)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="services">服务集合</param>
|
/// <param name="services">服务集合</param>
|
||||||
/// <param name="databasePath">数据库文件路径</param>
|
/// <param name="databasePath">数据库文件路径(绝对或相对路径)</param>
|
||||||
/// <param name="provider">OLE DB 提供程序(默认:Microsoft.ACE.OLEDB.12.0)</param>
|
/// <param name="password">数据库密码(可选)</param>
|
||||||
|
/// <param name="provider">OLE DB 提供程序,默认:Microsoft.ACE.OLEDB.16.0</param>
|
||||||
/// <returns>服务集合</returns>
|
/// <returns>服务集合</returns>
|
||||||
public static IServiceCollection AddAccessDataService(
|
public static IServiceCollection AddAccessDataService(
|
||||||
this IServiceCollection services,
|
this IServiceCollection services,
|
||||||
string databasePath,
|
string databasePath,
|
||||||
string provider = "Microsoft.ACE.OLEDB.12.0")
|
string? password = null,
|
||||||
|
string? provider = null)
|
||||||
{
|
{
|
||||||
var options = new AccessDataServiceOptions
|
return services.AddAccessDataService(options =>
|
||||||
{
|
{
|
||||||
DatabasePath = databasePath,
|
options.DatabaseFile = databasePath;
|
||||||
Provider = provider
|
if (!string.IsNullOrEmpty(password))
|
||||||
};
|
options.Password = password;
|
||||||
|
if (!string.IsNullOrEmpty(provider))
|
||||||
|
options.ProviderName = provider;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 Access 数据服务(使用配置委托)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">服务集合</param>
|
||||||
|
/// <param name="configureOptions">配置委托</param>
|
||||||
|
/// <returns>服务集合</returns>
|
||||||
|
public static IServiceCollection AddAccessDataService(
|
||||||
|
this IServiceCollection services,
|
||||||
|
Action<AccessDatabaseOptions> configureOptions)
|
||||||
|
{
|
||||||
|
if (services == null) throw new ArgumentNullException(nameof(services));
|
||||||
|
if (configureOptions == null) throw new ArgumentNullException(nameof(configureOptions));
|
||||||
|
|
||||||
|
var options = new AccessDatabaseOptions();
|
||||||
|
configureOptions(options);
|
||||||
|
options.Validate();
|
||||||
|
|
||||||
|
// 注册核心服务
|
||||||
|
services.TryAddSingleton<IAccessPasswordDecryptor, PassthroughAccessPasswordDecryptor>();
|
||||||
|
services.TryAddSingleton<IAccessConnectionStringFactory>(sp =>
|
||||||
|
new AccessConnectionStringFactory(sp.GetRequiredService<IAccessPasswordDecryptor>()));
|
||||||
|
services.TryAddSingleton<IAccessDatabaseInitializer, AccessDatabaseInitializer>();
|
||||||
|
|
||||||
|
// 注册配置
|
||||||
services.AddSingleton(options);
|
services.AddSingleton(options);
|
||||||
services.AddSingleton<IAccessDataService, AccessDataService>();
|
|
||||||
|
// 注册 AccessDataService
|
||||||
|
services.AddSingleton<IAccessDataService>(sp =>
|
||||||
|
new AccessDataService(
|
||||||
|
options,
|
||||||
|
sp.GetRequiredService<IAccessConnectionStringFactory>(),
|
||||||
|
sp.GetRequiredService<IAccessDatabaseInitializer>(),
|
||||||
|
sp.GetRequiredService<IAccessPasswordDecryptor>(),
|
||||||
|
sp.GetService<Microsoft.Extensions.Logging.ILogger<AccessDataService>>()));
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@@ -39,30 +82,37 @@ public static class AccessDataServiceExtensions
|
|||||||
/// <returns>服务集合</returns>
|
/// <returns>服务集合</returns>
|
||||||
public static IServiceCollection AddAccessDataService(
|
public static IServiceCollection AddAccessDataService(
|
||||||
this IServiceCollection services,
|
this IServiceCollection services,
|
||||||
AccessDataServiceOptions options)
|
AccessDatabaseOptions options)
|
||||||
{
|
{
|
||||||
services.AddSingleton(options);
|
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||||
services.AddSingleton<IAccessDataService, AccessDataService>();
|
|
||||||
|
|
||||||
|
return services.AddAccessDataService(_ => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册自定义密码解密器
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TDecryptor">解密器类型</typeparam>
|
||||||
|
/// <param name="services">服务集合</param>
|
||||||
|
/// <returns>服务集合</returns>
|
||||||
|
public static IServiceCollection AddAccessPasswordDecryptor<TDecryptor>(this IServiceCollection services)
|
||||||
|
where TDecryptor : class, IAccessPasswordDecryptor
|
||||||
|
{
|
||||||
|
services.AddSingleton<IAccessPasswordDecryptor, TDecryptor>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加 Access 数据服务(使用配置委托)
|
/// 注册自定义密码解密器(使用工厂方法)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="services">服务集合</param>
|
/// <param name="services">服务集合</param>
|
||||||
/// <param name="configureOptions">配置委托</param>
|
/// <param name="implementationFactory">解密器工厂</param>
|
||||||
/// <returns>服务集合</returns>
|
/// <returns>服务集合</returns>
|
||||||
public static IServiceCollection AddAccessDataService(
|
public static IServiceCollection AddAccessPasswordDecryptor(
|
||||||
this IServiceCollection services,
|
this IServiceCollection services,
|
||||||
Action<AccessDataServiceOptions> configureOptions)
|
Func<IServiceProvider, IAccessPasswordDecryptor> implementationFactory)
|
||||||
{
|
{
|
||||||
var options = new AccessDataServiceOptions();
|
services.AddSingleton(implementationFactory);
|
||||||
configureOptions(options);
|
|
||||||
|
|
||||||
services.AddSingleton(options);
|
|
||||||
services.AddSingleton<IAccessDataService, AccessDataService>();
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
namespace Sharp.Data.Access;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Access 数据服务配置选项
|
|
||||||
/// </summary>
|
|
||||||
public class AccessDataServiceOptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 数据库文件路径
|
|
||||||
/// </summary>
|
|
||||||
public string DatabasePath { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// OLE DB 提供程序(默认:Microsoft.ACE.OLEDB.12.0)
|
|
||||||
/// </summary>
|
|
||||||
public string Provider { get; set; } = "Microsoft.ACE.OLEDB.12.0";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 完整的连接字符串
|
|
||||||
/// </summary>
|
|
||||||
public string ConnectionString => $"Provider={Provider};Data Source={DatabasePath}";
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,8 @@ internal sealed class AccessConnectionStringFactory : IAccessConnectionStringFac
|
|||||||
throw new InvalidOperationException("Access 数据库需要提供 DatabaseFile 或 ConnectionString。");
|
throw new InvalidOperationException("Access 数据库需要提供 DatabaseFile 或 ConnectionString。");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 基础连接字符串
|
||||||
|
// 注意:Connect Timeout 参数在某些 ACE 版本中不稳定,已移除
|
||||||
var builder = new OleDbConnectionStringBuilder
|
var builder = new OleDbConnectionStringBuilder
|
||||||
{
|
{
|
||||||
["Provider"] = configuration.ProviderName,
|
["Provider"] = configuration.ProviderName,
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ public interface IAccessTransaction : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 执行非查询命令(INSERT, UPDATE, DELETE)
|
/// 执行非查询命令(INSERT, UPDATE, DELETE)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<int> ExecuteAsync(string sql, params OleDbParameter[] parameters);
|
int Execute(string sql, params OleDbParameter[] parameters);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询多条记录
|
/// 查询多条记录
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<List<T>> QueryAsync<T>(string sql, params OleDbParameter[] parameters) where T : new();
|
List<T> Query<T>(string sql, params OleDbParameter[] parameters) where T : new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 执行标量查询(COUNT, SUM等)
|
/// 执行标量查询(COUNT, SUM等)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<object?> ExecuteScalarAsync(string sql, params OleDbParameter[] parameters);
|
object? ExecuteScalar(string sql, params OleDbParameter[] parameters);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提交事务
|
/// 提交事务
|
||||||
@@ -34,29 +34,29 @@ public interface IAccessTransaction : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Access 数据服务接口
|
/// Access 数据服务接口(基于 System.Data.OleDb)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IAccessDataService
|
public interface IAccessDataService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询多条记录
|
/// 查询多条记录
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<List<T>> QueryAsync<T>(string sql, params OleDbParameter[] parameters) where T : new();
|
List<T> Query<T>(string sql, params OleDbParameter[] parameters) where T : new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询单条记录
|
/// 查询单条记录
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<T?> QueryFirstOrDefaultAsync<T>(string sql, params OleDbParameter[] parameters) where T : new();
|
T? QueryFirstOrDefault<T>(string sql, params OleDbParameter[] parameters) where T : new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 执行标量查询(COUNT, SUM等)
|
/// 执行标量查询(COUNT, SUM等)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<object?> ExecuteScalarAsync(string sql, params OleDbParameter[] parameters);
|
object? ExecuteScalar(string sql, params OleDbParameter[] parameters);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 执行非查询命令(INSERT, UPDATE, DELETE)
|
/// 执行非查询命令(INSERT, UPDATE, DELETE)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<int> ExecuteAsync(string sql, params OleDbParameter[] parameters);
|
int Execute(string sql, params OleDbParameter[] parameters);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 开始事务
|
/// 开始事务
|
||||||
@@ -66,10 +66,10 @@ public interface IAccessDataService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在事务中执行操作(自动提交/回滚)
|
/// 在事务中执行操作(自动提交/回滚)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task ExecuteInTransactionAsync(Func<IAccessTransaction, Task> action);
|
void ExecuteInTransaction(Action<IAccessTransaction> action);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 测试数据库连接
|
/// 测试数据库连接
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<bool> TestConnectionAsync();
|
bool TestConnection();
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,9 @@ internal sealed record AccessDatabaseConfiguration(
|
|||||||
string? EncryptedPassword,
|
string? EncryptedPassword,
|
||||||
string? TemplateFile,
|
string? TemplateFile,
|
||||||
string? DataDirectory,
|
string? DataDirectory,
|
||||||
|
int ConnectionTimeout,
|
||||||
|
int MaxRetryCount,
|
||||||
|
int RetryDelayMilliseconds,
|
||||||
IReadOnlyDictionary<string, string> AdditionalParameters,
|
IReadOnlyDictionary<string, string> AdditionalParameters,
|
||||||
bool CopyTemplateWhenMissing,
|
bool CopyTemplateWhenMissing,
|
||||||
bool ThrowIfMissing)
|
bool ThrowIfMissing)
|
||||||
@@ -38,6 +41,9 @@ internal sealed record AccessDatabaseConfiguration(
|
|||||||
encryptedPassword,
|
encryptedPassword,
|
||||||
templateFile,
|
templateFile,
|
||||||
dataDirectory,
|
dataDirectory,
|
||||||
|
options.ConnectionTimeout,
|
||||||
|
options.MaxRetryCount,
|
||||||
|
options.RetryDelayMilliseconds,
|
||||||
new Dictionary<string, string>(options.AdditionalParameters, StringComparer.OrdinalIgnoreCase),
|
new Dictionary<string, string>(options.AdditionalParameters, StringComparer.OrdinalIgnoreCase),
|
||||||
options.CopyTemplateWhenMissing,
|
options.CopyTemplateWhenMissing,
|
||||||
options.ThrowIfMissing);
|
options.ThrowIfMissing);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Collections.Generic;
|
|||||||
namespace Sharp.Data.Access.Options;
|
namespace Sharp.Data.Access.Options;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Access 数据库配置项,调用方负责提供所有需要的参数。
|
/// Access 数据库统一配置项,同时支持 OleDb 和 EF Core 两种方式。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AccessDatabaseOptions
|
public class AccessDatabaseOptions
|
||||||
{
|
{
|
||||||
@@ -40,7 +40,7 @@ public class AccessDatabaseOptions
|
|||||||
public Func<IServiceProvider, string?>? DataDirectoryFactory { get; set; }
|
public Func<IServiceProvider, string?>? DataDirectoryFactory { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 明文密码,可选。
|
/// 明文密码,可选。用于访问加密的 Access 数据库。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Password { get; set; }
|
public string? Password { get; set; }
|
||||||
|
|
||||||
@@ -77,13 +77,32 @@ public class AccessDatabaseOptions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当数据库文件缺失(且无法复制)时是否抛出异常。
|
/// 当数据库文件缺失(且无法复制)时是否抛出异常。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ThrowIfMissing { get; set; }
|
public bool ThrowIfMissing { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provider 名称,默认为 ACE OLE DB 16。
|
/// Provider 名称,默认为 ACE OLE DB 16.0(功能最完整)。
|
||||||
|
/// 可选值:
|
||||||
|
/// - AccessProviderNames.AceOleDb16 (Microsoft.ACE.OLEDB.16.0) - 推荐,需要 Office 2016+
|
||||||
|
/// - AccessProviderNames.AceOleDb12 (Microsoft.ACE.OLEDB.12.0) - 需要 Office 2010+,不支持 Connect Timeout
|
||||||
|
/// - AccessProviderNames.JetOleDb4 (Microsoft.Jet.OLEDB.4.0) - 仅支持 32 位和旧版 Access
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ProviderName { get; set; } = AccessProviderNames.AceOleDb16;
|
public string ProviderName { get; set; } = AccessProviderNames.AceOleDb16;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接超时时间(秒),默认 15 秒。
|
||||||
|
/// </summary>
|
||||||
|
public int ConnectionTimeout { get; set; } = 15;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最大重试次数,默认 3 次。用于增强稳健性。
|
||||||
|
/// </summary>
|
||||||
|
public int MaxRetryCount { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重试延迟(毫秒),默认 100 毫秒。
|
||||||
|
/// </summary>
|
||||||
|
public int RetryDelayMilliseconds { get; set; } = 100;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 额外的 OLE DB 参数,按需添加。
|
/// 额外的 OLE DB 参数,按需添加。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -105,5 +124,20 @@ public class AccessDatabaseOptions
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("必须指定 Access Provider。");
|
throw new InvalidOperationException("必须指定 Access Provider。");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ConnectionTimeout <= 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("ConnectionTimeout 必须大于 0。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MaxRetryCount < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("MaxRetryCount 不能小于 0。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RetryDelayMilliseconds < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("RetryDelayMilliseconds 不能小于 0。");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ using Sharp.Log;
|
|||||||
using Sharp.Data;
|
using Sharp.Data;
|
||||||
using Sharp.Data.Context;
|
using Sharp.Data.Context;
|
||||||
using Sharp.Data.Access;
|
using Sharp.Data.Access;
|
||||||
|
using Sharp.Data.Access.Options;
|
||||||
using Server.Config;
|
using Server.Config;
|
||||||
using Server.Services;
|
using Server.Services;
|
||||||
using Server.Data;
|
using Server.Data;
|
||||||
@@ -110,7 +111,7 @@ namespace Server
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeAccessDataService()
|
private void InitializeAccessDataService()
|
||||||
{
|
{
|
||||||
if (LoggerFactory == null)
|
if (LoggerFactory == null)
|
||||||
@@ -123,13 +124,39 @@ namespace Server
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var databasePath = Path.GetFullPath("dbDwDmsSetup.accdb");
|
// 诊断:输出当前目录信息
|
||||||
var options = new AccessDataServiceOptions
|
logger.LogInformation("AppContext.BaseDirectory: {BaseDirectory}", AppContext.BaseDirectory);
|
||||||
|
logger.LogInformation("Environment.CurrentDirectory: {CurrentDirectory}", Environment.CurrentDirectory);
|
||||||
|
logger.LogInformation("数据库文件在输出目录: {DbPath}",
|
||||||
|
Path.Combine(AppContext.BaseDirectory, "dbDwDmsSetup.accdb"));
|
||||||
|
logger.LogInformation("数据库文件是否存在: {Exists}",
|
||||||
|
File.Exists(Path.Combine(AppContext.BaseDirectory, "dbDwDmsSetup.accdb")));
|
||||||
|
|
||||||
|
// 配置 Access 数据库选项
|
||||||
|
var options = new AccessDatabaseOptions
|
||||||
{
|
{
|
||||||
DatabasePath = databasePath
|
DatabaseFile = "dbDwDmsSetup.accdb",
|
||||||
|
|
||||||
|
// 使用 ACE OLE DB 16.0(功能最完整,支持所有连接字符串参数)
|
||||||
|
// 注意:12.0 不支持 Connect Timeout 参数,会导致 0x80040E21 错误
|
||||||
|
ProviderName = AccessProviderNames.AceOleDb16,
|
||||||
|
|
||||||
|
// 如果数据库有密码,可以在这里设置:
|
||||||
|
// Password = "your_password",
|
||||||
|
|
||||||
|
// 或者使用加密密码(需要先注册自定义解密器):
|
||||||
|
// EncryptedPassword = "encrypted_password",
|
||||||
|
|
||||||
|
// 可选:调整稳健性配置
|
||||||
|
// MaxRetryCount = 3, // 最大重试次数
|
||||||
|
// RetryDelayMilliseconds = 100, // 重试延迟
|
||||||
|
// ConnectionTimeout = 15, // 连接超时(秒)
|
||||||
};
|
};
|
||||||
|
|
||||||
AccessDataService = new AccessDataService(options, LoggerFactory.CreateLogger<AccessDataService>());
|
// 创建 Access 数据服务
|
||||||
|
AccessDataService = new AccessDataService(
|
||||||
|
options,
|
||||||
|
logger: LoggerFactory.CreateLogger<AccessDataService>());
|
||||||
|
|
||||||
logger.LogInformation("Access 数据服务初始化成功");
|
logger.LogInformation("Access 数据服务初始化成功");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
|
|
||||||
|
<!-- 强制使用 x64 架构,与已安装的 64 位 ACE 驱动匹配 -->
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
|
||||||
<PublishSingleFile>true</PublishSingleFile>
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Data.OleDb;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using EntityFrameworkCore.Jet;
|
using EntityFrameworkCore.Jet;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Sharp.Data.Access.Options;
|
||||||
using Server.Data.Access;
|
using Server.Data.Access;
|
||||||
|
|
||||||
namespace Server.Views;
|
namespace Server.Views;
|
||||||
@@ -110,7 +112,7 @@ public partial class AccessDatabaseWindow : Window
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建 Access 数据库上下文
|
/// 创建 Access 数据库上下文(使用新的统一配置体系)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private AccessDbContext CreateDbContext()
|
private AccessDbContext CreateDbContext()
|
||||||
{
|
{
|
||||||
@@ -122,8 +124,18 @@ public partial class AccessDatabaseWindow : Window
|
|||||||
throw new FileNotFoundException($"找不到 Access 数据库文件: {dbPath}");
|
throw new FileNotFoundException($"找不到 Access 数据库文件: {dbPath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建连接字符串
|
// 使用统一的配置方式构建连接字符串
|
||||||
var connectionString = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath}";
|
var builder = new OleDbConnectionStringBuilder
|
||||||
|
{
|
||||||
|
["Provider"] = AccessProviderNames.AceOleDb16, // 使用 16.0
|
||||||
|
["Data Source"] = dbPath
|
||||||
|
// Connect Timeout 在某些情况下不稳定,已移除
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果数据库有密码,可以在这里设置:
|
||||||
|
// builder["Jet OLEDB:Database Password"] = "your_password";
|
||||||
|
|
||||||
|
var connectionString = builder.ConnectionString;
|
||||||
|
|
||||||
// 创建 DbContext 选项
|
// 创建 DbContext 选项
|
||||||
var optionsBuilder = new DbContextOptionsBuilder<AccessDbContext>();
|
var optionsBuilder = new DbContextOptionsBuilder<AccessDbContext>();
|
||||||
|
|||||||
@@ -46,14 +46,14 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
|
|
||||||
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await LoadDataAsync();
|
await Task.Run(() => LoadData());
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 基础操作
|
#region 基础操作
|
||||||
|
|
||||||
private async void RefreshButton_Click(object sender, RoutedEventArgs e)
|
private async void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await LoadDataAsync();
|
await Task.Run(() => LoadData());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void TestConnectionButton_Click(object sender, RoutedEventArgs e)
|
private async void TestConnectionButton_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -68,7 +68,7 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger?.LogInformation("测试 Access 数据库连接...");
|
_logger?.LogInformation("测试 Access 数据库连接...");
|
||||||
var success = await _accessDataService.TestConnectionAsync();
|
var success = await Task.Run(() => _accessDataService.TestConnection());
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
@@ -120,31 +120,42 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 检查用户是否已存在
|
await Task.Run(() =>
|
||||||
var count = await _accessDataService.ExecuteScalarAsync(
|
|
||||||
"SELECT COUNT(*) FROM UserInfo WHERE UserName = ?",
|
|
||||||
new OleDbParameter("@UserName", userName));
|
|
||||||
|
|
||||||
if (Convert.ToInt32(count) > 0)
|
|
||||||
{
|
{
|
||||||
MessageBox.Show($"用户 '{userName}' 已存在!", "警告",
|
// 检查用户是否已存在
|
||||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
var count = _accessDataService.ExecuteScalar(
|
||||||
return;
|
"SELECT COUNT(*) FROM UserInfo WHERE UserName = ?",
|
||||||
}
|
new OleDbParameter("@UserName", userName));
|
||||||
|
|
||||||
// 插入新用户
|
if (Convert.ToInt32(count) > 0)
|
||||||
await _accessDataService.ExecuteAsync(
|
{
|
||||||
"INSERT INTO UserInfo (UserName, [Password]) VALUES (?, ?)",
|
Dispatcher.Invoke(() =>
|
||||||
new OleDbParameter("@UserName", userName),
|
{
|
||||||
new OleDbParameter("@Password", password));
|
MessageBox.Show($"用户 '{userName}' 已存在!", "警告",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_logger?.LogInformation($"成功添加用户: {userName}");
|
// 插入新用户
|
||||||
MessageBox.Show($"成功添加用户 '{userName}'!", "成功",
|
_accessDataService.Execute(
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
"INSERT INTO UserInfo (UserName, [Password]) VALUES (?, ?)",
|
||||||
|
new OleDbParameter("@UserName", userName),
|
||||||
|
new OleDbParameter("@Password", password));
|
||||||
|
|
||||||
UserNameTextBox.Clear();
|
_logger?.LogInformation($"成功添加用户: {userName}");
|
||||||
PasswordTextBox.Clear();
|
|
||||||
await LoadDataAsync();
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Show($"成功添加用户 '{userName}'!", "成功",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
|
||||||
|
UserNameTextBox.Clear();
|
||||||
|
PasswordTextBox.Clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
LoadData();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -189,25 +200,36 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 更新密码
|
await Task.Run(() =>
|
||||||
var rowsAffected = await _accessDataService.ExecuteAsync(
|
|
||||||
"UPDATE UserInfo SET [Password] = ? WHERE UserName = ?",
|
|
||||||
new OleDbParameter("@Password", newPassword),
|
|
||||||
new OleDbParameter("@UserName", userName));
|
|
||||||
|
|
||||||
if (rowsAffected == 0)
|
|
||||||
{
|
{
|
||||||
MessageBox.Show($"用户 '{userName}' 不存在!", "警告",
|
// 更新密码
|
||||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
var rowsAffected = _accessDataService.Execute(
|
||||||
return;
|
"UPDATE UserInfo SET [Password] = ? WHERE UserName = ?",
|
||||||
}
|
new OleDbParameter("@Password", newPassword),
|
||||||
|
new OleDbParameter("@UserName", userName));
|
||||||
|
|
||||||
_logger?.LogInformation($"成功修改用户 {userName} 的密码");
|
if (rowsAffected == 0)
|
||||||
MessageBox.Show($"成功修改用户 '{userName}' 的密码!", "成功",
|
{
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Show($"用户 '{userName}' 不存在!", "警告",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PasswordTextBox.Clear();
|
_logger?.LogInformation($"成功修改用户 {userName} 的密码");
|
||||||
await LoadDataAsync();
|
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Show($"成功修改用户 '{userName}' 的密码!", "成功",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
|
||||||
|
PasswordTextBox.Clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
LoadData();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -245,31 +267,39 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 使用事务删除用户及其权限
|
// 使用事务删除用户及其权限
|
||||||
await _accessDataService.ExecuteInTransactionAsync(async transaction =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
// 先删除用户的所有权限
|
_accessDataService.ExecuteInTransaction(transaction =>
|
||||||
await transaction.ExecuteAsync(
|
|
||||||
"DELETE FROM UserPermission WHERE UserInfo_UserName = ?",
|
|
||||||
new OleDbParameter("@UserName", userName));
|
|
||||||
|
|
||||||
// 再删除用户
|
|
||||||
var rowsAffected = await transaction.ExecuteAsync(
|
|
||||||
"DELETE FROM UserInfo WHERE UserName = ?",
|
|
||||||
new OleDbParameter("@UserName", userName));
|
|
||||||
|
|
||||||
if (rowsAffected == 0)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"用户 '{userName}' 不存在!");
|
// 先删除用户的所有权限
|
||||||
}
|
transaction.Execute(
|
||||||
|
"DELETE FROM UserPermission WHERE UserInfo_UserName = ?",
|
||||||
|
new OleDbParameter("@UserName", userName));
|
||||||
|
|
||||||
|
// 再删除用户
|
||||||
|
var rowsAffected = transaction.Execute(
|
||||||
|
"DELETE FROM UserInfo WHERE UserName = ?",
|
||||||
|
new OleDbParameter("@UserName", userName));
|
||||||
|
|
||||||
|
if (rowsAffected == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"用户 '{userName}' 不存在!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_logger?.LogInformation($"成功删除用户: {userName}");
|
||||||
|
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Show($"成功删除用户 '{userName}'!", "成功",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
|
||||||
|
UserNameTextBox.Clear();
|
||||||
|
PasswordTextBox.Clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
LoadData();
|
||||||
});
|
});
|
||||||
|
|
||||||
_logger?.LogInformation($"成功删除用户: {userName}");
|
|
||||||
MessageBox.Show($"成功删除用户 '{userName}'!", "成功",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
|
|
||||||
UserNameTextBox.Clear();
|
|
||||||
PasswordTextBox.Clear();
|
|
||||||
await LoadDataAsync();
|
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException ex)
|
catch (InvalidOperationException ex)
|
||||||
{
|
{
|
||||||
@@ -312,31 +342,41 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 检查权限是否已存在
|
await Task.Run(() =>
|
||||||
var count = await _accessDataService.ExecuteScalarAsync(
|
|
||||||
"SELECT COUNT(*) FROM UserPermission WHERE UserInfo_UserName = ? AND Permission_Code = ?",
|
|
||||||
new OleDbParameter("@UserName", _selectedUserName),
|
|
||||||
new OleDbParameter("@PermissionCode", permissionCode));
|
|
||||||
|
|
||||||
if (Convert.ToInt32(count) > 0)
|
|
||||||
{
|
{
|
||||||
MessageBox.Show($"用户 '{_selectedUserName}' 已拥有权限 '{permissionCode}'!", "警告",
|
// 检查权限是否已存在
|
||||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
var count = _accessDataService.ExecuteScalar(
|
||||||
return;
|
"SELECT COUNT(*) FROM UserPermission WHERE UserInfo_UserName = ? AND Permission_Code = ?",
|
||||||
}
|
new OleDbParameter("@UserName", _selectedUserName),
|
||||||
|
new OleDbParameter("@PermissionCode", permissionCode));
|
||||||
|
|
||||||
// 插入权限
|
if (Convert.ToInt32(count) > 0)
|
||||||
await _accessDataService.ExecuteAsync(
|
{
|
||||||
"INSERT INTO UserPermission (UserInfo_UserName, Permission_Code) VALUES (?, ?)",
|
Dispatcher.Invoke(() =>
|
||||||
new OleDbParameter("@UserName", _selectedUserName),
|
{
|
||||||
new OleDbParameter("@PermissionCode", permissionCode));
|
MessageBox.Show($"用户 '{_selectedUserName}' 已拥有权限 '{permissionCode}'!", "警告",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_logger?.LogInformation($"成功为用户 {_selectedUserName} 添加权限: {permissionCode}");
|
// 插入权限
|
||||||
MessageBox.Show($"成功添加权限 '{permissionCode}'!", "成功",
|
_accessDataService.Execute(
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
"INSERT INTO UserPermission (UserInfo_UserName, Permission_Code) VALUES (?, ?)",
|
||||||
|
new OleDbParameter("@UserName", _selectedUserName),
|
||||||
|
new OleDbParameter("@PermissionCode", permissionCode));
|
||||||
|
|
||||||
PermissionCodeTextBox.Clear();
|
_logger?.LogInformation($"成功为用户 {_selectedUserName} 添加权限: {permissionCode}");
|
||||||
LoadUserPermissions(_selectedUserName);
|
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Show($"成功添加权限 '{permissionCode}'!", "成功",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
|
||||||
|
PermissionCodeTextBox.Clear();
|
||||||
|
LoadUserPermissions(_selectedUserName);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -378,16 +418,23 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _accessDataService.ExecuteAsync(
|
await Task.Run(() =>
|
||||||
"DELETE FROM UserPermission WHERE UserInfo_UserName = ? AND Permission_Code = ?",
|
{
|
||||||
new OleDbParameter("@UserName", _selectedUserName),
|
_accessDataService.Execute(
|
||||||
new OleDbParameter("@PermissionCode", selectedPermission.Permission_Code));
|
"DELETE FROM UserPermission WHERE UserInfo_UserName = ? AND Permission_Code = ?",
|
||||||
|
new OleDbParameter("@UserName", _selectedUserName),
|
||||||
|
new OleDbParameter("@PermissionCode", selectedPermission.Permission_Code));
|
||||||
|
|
||||||
_logger?.LogInformation($"成功删除用户 {_selectedUserName} 的权限: {selectedPermission.Permission_Code}");
|
_logger?.LogInformation($"成功删除用户 {_selectedUserName} 的权限: {selectedPermission.Permission_Code}");
|
||||||
MessageBox.Show($"成功删除权限 '{selectedPermission.Permission_Code}'!", "成功",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
|
|
||||||
LoadUserPermissions(_selectedUserName);
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Show($"成功删除权限 '{selectedPermission.Permission_Code}'!", "成功",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
|
||||||
|
LoadUserPermissions(_selectedUserName);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -400,8 +447,7 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 数据加载
|
#region 数据加载
|
||||||
|
private void LoadData()
|
||||||
private async System.Threading.Tasks.Task LoadDataAsync()
|
|
||||||
{
|
{
|
||||||
if (_accessDataService == null) return;
|
if (_accessDataService == null) return;
|
||||||
|
|
||||||
@@ -410,7 +456,7 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
_logger?.LogInformation("开始加载 Access 数据库数据...");
|
_logger?.LogInformation("开始加载 Access 数据库数据...");
|
||||||
|
|
||||||
// 查询所有用户(使用泛型映射)
|
// 查询所有用户(使用泛型映射)
|
||||||
var users = await _accessDataService.QueryAsync<UserInfoDto>(
|
var users = _accessDataService.Query<UserInfoDto>(
|
||||||
"SELECT [UserName], [Password] AS [Password] FROM [UserInfo]");
|
"SELECT [UserName], [Password] AS [Password] FROM [UserInfo]");
|
||||||
|
|
||||||
var userDetails = string.Join(" | ",
|
var userDetails = string.Join(" | ",
|
||||||
@@ -420,7 +466,7 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
// 为每个用户查询权限数量
|
// 为每个用户查询权限数量
|
||||||
foreach (var user in users)
|
foreach (var user in users)
|
||||||
{
|
{
|
||||||
var count = await _accessDataService.ExecuteScalarAsync(
|
var count = _accessDataService.ExecuteScalar(
|
||||||
"SELECT COUNT(*) FROM UserPermission WHERE UserInfo_UserName = ?",
|
"SELECT COUNT(*) FROM UserPermission WHERE UserInfo_UserName = ?",
|
||||||
new OleDbParameter("@UserName", user.UserName));
|
new OleDbParameter("@UserName", user.UserName));
|
||||||
user.PermissionCount = Convert.ToInt32(count);
|
user.PermissionCount = Convert.ToInt32(count);
|
||||||
@@ -428,16 +474,22 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
|
|
||||||
_logger?.LogInformation($"加载了 {users.Count} 个用户");
|
_logger?.LogInformation($"加载了 {users.Count} 个用户");
|
||||||
|
|
||||||
// 更新 UI
|
// 更新 UI(需要在 UI 线程上执行)
|
||||||
UsersDataGrid.ItemsSource = users;
|
Dispatcher.Invoke(() =>
|
||||||
PermissionsDataGrid.ItemsSource = null;
|
{
|
||||||
_selectedUserName = null;
|
UsersDataGrid.ItemsSource = users;
|
||||||
|
PermissionsDataGrid.ItemsSource = null;
|
||||||
|
_selectedUserName = null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger?.LogError(ex, "加载数据失败");
|
_logger?.LogError(ex, "加载数据失败");
|
||||||
MessageBox.Show($"加载数据失败: {ex.Message}", "错误",
|
Dispatcher.Invoke(() =>
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
{
|
||||||
|
MessageBox.Show($"加载数据失败: {ex.Message}", "错误",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,25 +509,32 @@ public partial class AccessDatabaseWindowOleDb : Window
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void LoadUserPermissions(string userName)
|
private void LoadUserPermissions(string userName)
|
||||||
{
|
{
|
||||||
if (_accessDataService == null) return;
|
if (_accessDataService == null) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 查询用户权限(使用泛型映射)
|
// 查询用户权限(使用泛型映射)
|
||||||
var permissions = await _accessDataService.QueryAsync<UserPermissionDto>(
|
var permissions = _accessDataService.Query<UserPermissionDto>(
|
||||||
"SELECT Permission_Code FROM UserPermission WHERE UserInfo_UserName = ?",
|
"SELECT Permission_Code FROM UserPermission WHERE UserInfo_UserName = ?",
|
||||||
new OleDbParameter("@UserName", userName));
|
new OleDbParameter("@UserName", userName));
|
||||||
|
|
||||||
PermissionsDataGrid.ItemsSource = permissions;
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
PermissionsDataGrid.ItemsSource = permissions;
|
||||||
|
});
|
||||||
|
|
||||||
_logger?.LogInformation($"用户 {userName} 有 {permissions.Count} 个权限");
|
_logger?.LogInformation($"用户 {userName} 有 {permissions.Count} 个权限");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger?.LogError(ex, "加载权限失败");
|
_logger?.LogError(ex, "加载权限失败");
|
||||||
MessageBox.Show($"加载权限失败: {ex.Message}", "错误",
|
Dispatcher.Invoke(() =>
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
{
|
||||||
|
MessageBox.Show($"加载权限失败: {ex.Message}", "错误",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"SharpLog": {
|
"SharpLog": {
|
||||||
"UseConsole": true,
|
"UseConsole": true,
|
||||||
"MinLevel": "Information",
|
"MinLevel": "Debug",
|
||||||
"File": {
|
"File": {
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Path": "logs/server-.log",
|
"Path": "logs/server-.log",
|
||||||
|
|||||||
Reference in new issue
Block a user