A:增加Access数据库支持
This commit is contained in:
1 parent
e334e65c2d
commit
dd993e5b45
29 files changed
+2939
-8
No files matched your search
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,419 @@
|
||||
using System.Data;
|
||||
using System.Data.OleDb;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Sharp.Data.Access;
|
||||
|
||||
/// <summary>
|
||||
/// Access 数据库事务实现
|
||||
/// </summary>
|
||||
internal class AccessTransaction : IAccessTransaction
|
||||
{
|
||||
private readonly OleDbConnection _connection;
|
||||
private readonly OleDbTransaction _transaction;
|
||||
private readonly ILogger? _logger;
|
||||
private bool _disposed;
|
||||
|
||||
public AccessTransaction(OleDbConnection connection, ILogger? logger)
|
||||
{
|
||||
_connection = connection;
|
||||
_transaction = connection.BeginTransaction();
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task<int> ExecuteAsync(string sql, params OleDbParameter[] parameters)
|
||||
{
|
||||
using var command = new OleDbCommand(sql, _connection, _transaction);
|
||||
if (parameters.Length > 0)
|
||||
command.Parameters.AddRange(parameters);
|
||||
|
||||
var result = command.ExecuteNonQuery();
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
public Task<List<T>> QueryAsync<T>(string sql, params OleDbParameter[] parameters) where T : new()
|
||||
{
|
||||
using var command = new OleDbCommand(sql, _connection, _transaction);
|
||||
if (parameters.Length > 0)
|
||||
command.Parameters.AddRange(parameters);
|
||||
|
||||
var results = new List<T>();
|
||||
using var reader = command.ExecuteReader();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
var item = MapReaderToObject<T>(reader);
|
||||
results.Add(item);
|
||||
}
|
||||
|
||||
return Task.FromResult(results);
|
||||
}
|
||||
|
||||
public Task<object?> ExecuteScalarAsync(string sql, params OleDbParameter[] parameters)
|
||||
{
|
||||
using var command = new OleDbCommand(sql, _connection, _transaction);
|
||||
if (parameters.Length > 0)
|
||||
command.Parameters.AddRange(parameters);
|
||||
|
||||
var result = command.ExecuteScalar();
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
{
|
||||
_transaction.Commit();
|
||||
_logger?.LogDebug("事务已提交");
|
||||
}
|
||||
|
||||
public void Rollback()
|
||||
{
|
||||
_transaction.Rollback();
|
||||
_logger?.LogDebug("事务已回滚");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_transaction.Dispose();
|
||||
_connection.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
private static T MapReaderToObject<T>(IDataReader reader) where T : new()
|
||||
{
|
||||
var obj = new T();
|
||||
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// 创建字段名到索引的映射(大小写不敏感)
|
||||
var fieldMap = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
var fieldName = reader.GetName(i);
|
||||
fieldMap[fieldName] = i;
|
||||
|
||||
// Access 列名有时包含末尾空格,提前做一次修剪以便映射
|
||||
var trimmed = fieldName.Trim();
|
||||
if (!fieldMap.ContainsKey(trimmed))
|
||||
{
|
||||
fieldMap[trimmed] = i;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
if (!property.CanWrite) continue;
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试查找匹配的字段(大小写不敏感)
|
||||
if (fieldMap.TryGetValue(property.Name, out int ordinal))
|
||||
{
|
||||
var value = reader.GetValue(ordinal);
|
||||
|
||||
if (value != DBNull.Value && value != null)
|
||||
{
|
||||
// 尝试类型转换
|
||||
if (property.PropertyType == value.GetType())
|
||||
{
|
||||
property.SetValue(obj, value);
|
||||
}
|
||||
else if (property.PropertyType == typeof(string))
|
||||
{
|
||||
property.SetValue(obj, value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// 尝试使用 Convert.ChangeType
|
||||
var convertedValue = Convert.ChangeType(value, property.PropertyType);
|
||||
property.SetValue(obj, convertedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 字段不存在或类型转换失败,保持默认值
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Access 数据服务实现
|
||||
/// </summary>
|
||||
public class AccessDataService : IAccessDataService
|
||||
{
|
||||
private readonly AccessDataServiceOptions _options;
|
||||
private readonly ILogger<AccessDataService>? _logger;
|
||||
|
||||
public AccessDataService(AccessDataServiceOptions options, ILogger<AccessDataService>? logger = null)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_logger = logger;
|
||||
|
||||
if (string.IsNullOrEmpty(_options.DatabasePath))
|
||||
{
|
||||
throw new ArgumentException("数据库路径不能为空", nameof(options.DatabasePath));
|
||||
}
|
||||
|
||||
if (!File.Exists(_options.DatabasePath))
|
||||
{
|
||||
throw new FileNotFoundException($"找不到 Access 数据库文件: {_options.DatabasePath}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<T>> QueryAsync<T>(string sql, params OleDbParameter[] parameters) where T : new()
|
||||
{
|
||||
_logger?.LogDebug("执行查询: {Sql}", sql);
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
using var connection = CreateConnection();
|
||||
connection.Open();
|
||||
|
||||
using var command = new OleDbCommand(sql, connection);
|
||||
if (parameters.Length > 0)
|
||||
command.Parameters.AddRange(parameters);
|
||||
|
||||
var results = new List<T>();
|
||||
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())
|
||||
{
|
||||
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);
|
||||
results.Add(item);
|
||||
}
|
||||
|
||||
_logger?.LogDebug("查询返回 {Count} 条记录", results.Count);
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<T?> QueryFirstOrDefaultAsync<T>(string sql, params OleDbParameter[] parameters) where T : new()
|
||||
{
|
||||
_logger?.LogDebug("执行单条查询: {Sql}", sql);
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
using var connection = CreateConnection();
|
||||
connection.Open();
|
||||
|
||||
using var command = new OleDbCommand(sql, connection);
|
||||
if (parameters.Length > 0)
|
||||
command.Parameters.AddRange(parameters);
|
||||
|
||||
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())
|
||||
{
|
||||
return MapReaderToObject<T>(reader);
|
||||
}
|
||||
|
||||
return default;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<object?> ExecuteScalarAsync(string sql, params OleDbParameter[] parameters)
|
||||
{
|
||||
_logger?.LogDebug("执行标量查询: {Sql}", sql);
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
using var connection = CreateConnection();
|
||||
connection.Open();
|
||||
|
||||
using var command = new OleDbCommand(sql, connection);
|
||||
if (parameters.Length > 0)
|
||||
command.Parameters.AddRange(parameters);
|
||||
|
||||
var result = command.ExecuteScalar();
|
||||
_logger?.LogDebug("标量查询返回: {Result}", result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteAsync(string sql, params OleDbParameter[] parameters)
|
||||
{
|
||||
_logger?.LogDebug("执行命令: {Sql}", sql);
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
using var connection = CreateConnection();
|
||||
connection.Open();
|
||||
|
||||
using var command = new OleDbCommand(sql, connection);
|
||||
if (parameters.Length > 0)
|
||||
command.Parameters.AddRange(parameters);
|
||||
|
||||
var rowsAffected = command.ExecuteNonQuery();
|
||||
_logger?.LogDebug("命令影响了 {RowsAffected} 行", rowsAffected);
|
||||
return rowsAffected;
|
||||
});
|
||||
}
|
||||
|
||||
public IAccessTransaction BeginTransaction()
|
||||
{
|
||||
_logger?.LogDebug("开始事务");
|
||||
var connection = CreateConnection();
|
||||
connection.Open();
|
||||
return new AccessTransaction(connection, _logger);
|
||||
}
|
||||
|
||||
public async Task ExecuteInTransactionAsync(Func<IAccessTransaction, Task> action)
|
||||
{
|
||||
_logger?.LogDebug("在事务中执行操作");
|
||||
|
||||
using var transaction = BeginTransaction();
|
||||
try
|
||||
{
|
||||
await action(transaction);
|
||||
transaction.Commit();
|
||||
_logger?.LogDebug("事务执行成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "事务执行失败,正在回滚");
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TestConnectionAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger?.LogDebug("测试数据库连接");
|
||||
using var connection = CreateConnection();
|
||||
await connection.OpenAsync();
|
||||
_logger?.LogDebug("数据库连接成功");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "数据库连接失败");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private OleDbConnection CreateConnection()
|
||||
{
|
||||
return new OleDbConnection(_options.ConnectionString);
|
||||
}
|
||||
|
||||
private void LogFieldMap(IDataReader reader)
|
||||
{
|
||||
if (!(_logger?.IsEnabled(LogLevel.Information) ?? false)) return;
|
||||
|
||||
var columns = Enumerable.Range(0, reader.FieldCount)
|
||||
.Select(i => $"'{reader.GetName(i)}'->idx:{i}")
|
||||
.ToArray();
|
||||
_logger!.LogInformation("字段映射: {Columns}", string.Join(", ", columns));
|
||||
}
|
||||
|
||||
private static object? SafeGetValue(IDataRecord reader, int ordinal)
|
||||
{
|
||||
try
|
||||
{
|
||||
return reader.GetValue(ordinal);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 某些 OleDb 列类型在获取值时可能抛 COM 异常,这里做容错日志使用
|
||||
return "<unavailable>";
|
||||
}
|
||||
}
|
||||
|
||||
private static T MapReaderToObject<T>(IDataReader reader) where T : new()
|
||||
{
|
||||
var obj = new T();
|
||||
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// 创建字段名到索引的映射(大小写不敏感)
|
||||
var fieldMap = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
var fieldName = reader.GetName(i);
|
||||
fieldMap[fieldName] = i;
|
||||
|
||||
// Access 列名有时包含末尾空格,提前做一次修剪以便映射
|
||||
var trimmed = fieldName.Trim();
|
||||
if (!fieldMap.ContainsKey(trimmed))
|
||||
{
|
||||
fieldMap[trimmed] = i;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
if (!property.CanWrite) continue;
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试查找匹配的字段(大小写不敏感)
|
||||
if (fieldMap.TryGetValue(property.Name, out int ordinal))
|
||||
{
|
||||
var value = reader.GetValue(ordinal);
|
||||
|
||||
if (value != DBNull.Value && value != null)
|
||||
{
|
||||
// 尝试类型转换
|
||||
if (property.PropertyType == value.GetType())
|
||||
{
|
||||
property.SetValue(obj, value);
|
||||
}
|
||||
else if (property.PropertyType == typeof(string))
|
||||
{
|
||||
property.SetValue(obj, value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// 尝试使用 Convert.ChangeType
|
||||
var convertedValue = Convert.ChangeType(value, property.PropertyType);
|
||||
property.SetValue(obj, convertedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 字段不存在或类型转换失败,保持默认值
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Sharp.Data.Access;
|
||||
|
||||
/// <summary>
|
||||
/// Access 数据服务的依赖注入扩展
|
||||
/// </summary>
|
||||
public static class AccessDataServiceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加 Access 数据服务
|
||||
/// </summary>
|
||||
/// <param name="services">服务集合</param>
|
||||
/// <param name="databasePath">数据库文件路径</param>
|
||||
/// <param name="provider">OLE DB 提供程序(默认:Microsoft.ACE.OLEDB.12.0)</param>
|
||||
/// <returns>服务集合</returns>
|
||||
public static IServiceCollection AddAccessDataService(
|
||||
this IServiceCollection services,
|
||||
string databasePath,
|
||||
string provider = "Microsoft.ACE.OLEDB.12.0")
|
||||
{
|
||||
var options = new AccessDataServiceOptions
|
||||
{
|
||||
DatabasePath = databasePath,
|
||||
Provider = provider
|
||||
};
|
||||
|
||||
services.AddSingleton(options);
|
||||
services.AddSingleton<IAccessDataService, AccessDataService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加 Access 数据服务(使用配置对象)
|
||||
/// </summary>
|
||||
/// <param name="services">服务集合</param>
|
||||
/// <param name="options">配置选项</param>
|
||||
/// <returns>服务集合</returns>
|
||||
public static IServiceCollection AddAccessDataService(
|
||||
this IServiceCollection services,
|
||||
AccessDataServiceOptions options)
|
||||
{
|
||||
services.AddSingleton(options);
|
||||
services.AddSingleton<IAccessDataService, AccessDataService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加 Access 数据服务(使用配置委托)
|
||||
/// </summary>
|
||||
/// <param name="services">服务集合</param>
|
||||
/// <param name="configureOptions">配置委托</param>
|
||||
/// <returns>服务集合</returns>
|
||||
public static IServiceCollection AddAccessDataService(
|
||||
this IServiceCollection services,
|
||||
Action<AccessDataServiceOptions> configureOptions)
|
||||
{
|
||||
var options = new AccessDataServiceOptions();
|
||||
configureOptions(options);
|
||||
|
||||
services.AddSingleton(options);
|
||||
services.AddSingleton<IAccessDataService, AccessDataService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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}";
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Data.OleDb;
|
||||
|
||||
namespace Sharp.Data.Access;
|
||||
|
||||
/// <summary>
|
||||
/// Access 数据库事务接口
|
||||
/// </summary>
|
||||
public interface IAccessTransaction : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 执行非查询命令(INSERT, UPDATE, DELETE)
|
||||
/// </summary>
|
||||
Task<int> ExecuteAsync(string sql, params OleDbParameter[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// 查询多条记录
|
||||
/// </summary>
|
||||
Task<List<T>> QueryAsync<T>(string sql, params OleDbParameter[] parameters) where T : new();
|
||||
|
||||
/// <summary>
|
||||
/// 执行标量查询(COUNT, SUM等)
|
||||
/// </summary>
|
||||
Task<object?> ExecuteScalarAsync(string sql, params OleDbParameter[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// 提交事务
|
||||
/// </summary>
|
||||
void Commit();
|
||||
|
||||
/// <summary>
|
||||
/// 回滚事务
|
||||
/// </summary>
|
||||
void Rollback();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Access 数据服务接口
|
||||
/// </summary>
|
||||
public interface IAccessDataService
|
||||
{
|
||||
/// <summary>
|
||||
/// 查询多条记录
|
||||
/// </summary>
|
||||
Task<List<T>> QueryAsync<T>(string sql, params OleDbParameter[] parameters) where T : new();
|
||||
|
||||
/// <summary>
|
||||
/// 查询单条记录
|
||||
/// </summary>
|
||||
Task<T?> QueryFirstOrDefaultAsync<T>(string sql, params OleDbParameter[] parameters) where T : new();
|
||||
|
||||
/// <summary>
|
||||
/// 执行标量查询(COUNT, SUM等)
|
||||
/// </summary>
|
||||
Task<object?> ExecuteScalarAsync(string sql, params OleDbParameter[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// 执行非查询命令(INSERT, UPDATE, DELETE)
|
||||
/// </summary>
|
||||
Task<int> ExecuteAsync(string sql, params OleDbParameter[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// 开始事务
|
||||
/// </summary>
|
||||
IAccessTransaction BeginTransaction();
|
||||
|
||||
/// <summary>
|
||||
/// 在事务中执行操作(自动提交/回滚)
|
||||
/// </summary>
|
||||
Task ExecuteInTransactionAsync(Func<IAccessTransaction, Task> action);
|
||||
|
||||
/// <summary>
|
||||
/// 测试数据库连接
|
||||
/// </summary>
|
||||
Task<bool> TestConnectionAsync();
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
# AccessDataService 简化 API 使用指南
|
||||
|
||||
## 🎯 设计目标
|
||||
|
||||
Data.Access 模块的核心目标是**让调用者使用起来更简单**,将连接管理、SQL 执行、参数处理、事务管理等复杂度封装起来。
|
||||
|
||||
## 📊 代码对比
|
||||
|
||||
### ❌ 之前:直接使用 OleDb(复杂)
|
||||
|
||||
```csharp
|
||||
// 每次都要手动创建连接
|
||||
private OleDbConnection CreateConnection()
|
||||
{
|
||||
var dbPath = Path.GetFullPath("dbDwDmsSetup.accdb");
|
||||
var connectionString = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath}";
|
||||
return new OleDbConnection(connectionString);
|
||||
}
|
||||
|
||||
// 查询用户 - 需要写大量样板代码
|
||||
private async Task<List<UserInfoDto>> LoadUsersAsync()
|
||||
{
|
||||
using var connection = CreateConnection();
|
||||
await connection.OpenAsync();
|
||||
|
||||
using var command = new OleDbCommand("SELECT UserName, Password FROM UserInfo", connection);
|
||||
using var reader = await Task.Run(() => command.ExecuteReader());
|
||||
|
||||
var users = new List<UserInfoDto>();
|
||||
while (await Task.Run(() => reader.Read()))
|
||||
{
|
||||
var user = new UserInfoDto
|
||||
{
|
||||
UserName = reader["UserName"]?.ToString() ?? "",
|
||||
Password = reader["Password"]?.ToString() ?? ""
|
||||
};
|
||||
users.Add(user);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
// 删除用户 - 需要手动管理事务
|
||||
private async Task DeleteUserAsync(string userName)
|
||||
{
|
||||
using var connection = CreateConnection();
|
||||
await connection.OpenAsync();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
|
||||
try
|
||||
{
|
||||
using (var deletePermCmd = new OleDbCommand(
|
||||
"DELETE FROM UserPermission WHERE UserInfo_UserName = ?", connection, transaction))
|
||||
{
|
||||
deletePermCmd.Parameters.AddWithValue("@UserName", userName);
|
||||
await Task.Run(() => deletePermCmd.ExecuteNonQuery());
|
||||
}
|
||||
|
||||
using (var deleteUserCmd = new OleDbCommand(
|
||||
"DELETE FROM UserInfo WHERE UserName = ?", connection, transaction))
|
||||
{
|
||||
deleteUserCmd.Parameters.AddWithValue("@UserName", userName);
|
||||
await Task.Run(() => deleteUserCmd.ExecuteNonQuery());
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
catch
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 大量重复的连接管理代码
|
||||
- 手动映射查询结果到对象
|
||||
- 手动管理事务的提交和回滚
|
||||
- 代码冗长,不易维护
|
||||
|
||||
---
|
||||
|
||||
### ✅ 现在:使用 AccessDataService(简洁)
|
||||
|
||||
```csharp
|
||||
// 注入服务,无需手动创建连接
|
||||
private readonly IAccessDataService _accessDataService;
|
||||
|
||||
// 查询用户 - 一行代码!自动映射!
|
||||
private async Task<List<UserInfoDto>> LoadUsersAsync()
|
||||
{
|
||||
return await _accessDataService.QueryAsync<UserInfoDto>(
|
||||
"SELECT UserName, [Password] FROM UserInfo");
|
||||
}
|
||||
|
||||
// 删除用户 - 自动管理事务!
|
||||
private async Task DeleteUserAsync(string userName)
|
||||
{
|
||||
await _accessDataService.ExecuteInTransactionAsync(async transaction =>
|
||||
{
|
||||
await transaction.ExecuteAsync(
|
||||
"DELETE FROM UserPermission WHERE UserInfo_UserName = ?",
|
||||
new OleDbParameter("@UserName", userName));
|
||||
|
||||
await transaction.ExecuteAsync(
|
||||
"DELETE FROM UserInfo WHERE UserName = ?",
|
||||
new OleDbParameter("@UserName", userName));
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- ✅ 无需手动管理连接
|
||||
- ✅ 自动泛型映射查询结果
|
||||
- ✅ 自动管理事务(提交/回滚)
|
||||
- ✅ 代码简洁,易于维护
|
||||
- ✅ 从 60+ 行减少到 10 行左右
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 初始化服务
|
||||
|
||||
在 `App.xaml.cs` 中初始化:
|
||||
|
||||
```csharp
|
||||
private void InitializeAccessDataService()
|
||||
{
|
||||
var databasePath = Path.GetFullPath("dbDwDmsSetup.accdb");
|
||||
var options = new AccessDataServiceOptions
|
||||
{
|
||||
DatabasePath = databasePath
|
||||
};
|
||||
|
||||
AccessDataService = new AccessDataService(options, LoggerFactory.CreateLogger<AccessDataService>());
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 在窗口中使用
|
||||
|
||||
```csharp
|
||||
public partial class AccessDatabaseWindowOleDb : Window
|
||||
{
|
||||
private readonly IAccessDataService _accessDataService;
|
||||
|
||||
public AccessDatabaseWindowOleDb()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
var app = (App)Application.Current;
|
||||
_accessDataService = app.AccessDataService;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 API 使用示例
|
||||
|
||||
### 1. 查询多条记录
|
||||
|
||||
```csharp
|
||||
// 自动映射到对象
|
||||
var users = await _accessDataService.QueryAsync<UserInfoDto>(
|
||||
"SELECT UserName, [Password] FROM UserInfo");
|
||||
|
||||
// 带参数的查询
|
||||
var users = await _accessDataService.QueryAsync<UserInfoDto>(
|
||||
"SELECT UserName, [Password] FROM UserInfo WHERE UserName LIKE ?",
|
||||
new OleDbParameter("@Pattern", "%admin%"));
|
||||
```
|
||||
|
||||
### 2. 查询单条记录
|
||||
|
||||
```csharp
|
||||
var user = await _accessDataService.QueryFirstOrDefaultAsync<UserInfoDto>(
|
||||
"SELECT UserName, [Password] FROM UserInfo WHERE UserName = ?",
|
||||
new OleDbParameter("@UserName", "admin"));
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
Console.WriteLine($"找到用户: {user.UserName}");
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 执行标量查询
|
||||
|
||||
```csharp
|
||||
// 查询数量
|
||||
var count = await _accessDataService.ExecuteScalarAsync(
|
||||
"SELECT COUNT(*) FROM UserInfo WHERE UserName = ?",
|
||||
new OleDbParameter("@UserName", "admin"));
|
||||
|
||||
int userCount = Convert.ToInt32(count);
|
||||
```
|
||||
|
||||
### 4. 插入数据
|
||||
|
||||
```csharp
|
||||
var rowsAffected = await _accessDataService.ExecuteAsync(
|
||||
"INSERT INTO UserInfo (UserName, [Password]) VALUES (?, ?)",
|
||||
new OleDbParameter("@UserName", "newuser"),
|
||||
new OleDbParameter("@Password", "password123"));
|
||||
|
||||
Console.WriteLine($"插入了 {rowsAffected} 行");
|
||||
```
|
||||
|
||||
### 5. 更新数据
|
||||
|
||||
```csharp
|
||||
var rowsAffected = await _accessDataService.ExecuteAsync(
|
||||
"UPDATE UserInfo SET [Password] = ? WHERE UserName = ?",
|
||||
new OleDbParameter("@Password", "newpassword"),
|
||||
new OleDbParameter("@UserName", "admin"));
|
||||
|
||||
Console.WriteLine($"更新了 {rowsAffected} 行");
|
||||
```
|
||||
|
||||
### 6. 删除数据
|
||||
|
||||
```csharp
|
||||
var rowsAffected = await _accessDataService.ExecuteAsync(
|
||||
"DELETE FROM UserInfo WHERE UserName = ?",
|
||||
new OleDbParameter("@UserName", "testuser"));
|
||||
|
||||
Console.WriteLine($"删除了 {rowsAffected} 行");
|
||||
```
|
||||
|
||||
### 7. 使用事务(自动提交/回滚)
|
||||
|
||||
```csharp
|
||||
// 推荐:使用 ExecuteInTransactionAsync(自动管理)
|
||||
await _accessDataService.ExecuteInTransactionAsync(async transaction =>
|
||||
{
|
||||
// 删除权限
|
||||
await transaction.ExecuteAsync(
|
||||
"DELETE FROM UserPermission WHERE UserInfo_UserName = ?",
|
||||
new OleDbParameter("@UserName", userName));
|
||||
|
||||
// 删除用户
|
||||
await transaction.ExecuteAsync(
|
||||
"DELETE FROM UserInfo WHERE UserName = ?",
|
||||
new OleDbParameter("@UserName", userName));
|
||||
|
||||
// 自动提交!如果有异常,自动回滚!
|
||||
});
|
||||
```
|
||||
|
||||
### 8. 手动管理事务
|
||||
|
||||
```csharp
|
||||
// 如果需要手动控制
|
||||
using var transaction = _accessDataService.BeginTransaction();
|
||||
try
|
||||
{
|
||||
await transaction.ExecuteAsync("INSERT INTO ...", ...);
|
||||
await transaction.ExecuteAsync("UPDATE ...", ...);
|
||||
|
||||
transaction.Commit(); // 手动提交
|
||||
}
|
||||
catch
|
||||
{
|
||||
transaction.Rollback(); // 手动回滚
|
||||
throw;
|
||||
}
|
||||
```
|
||||
|
||||
### 9. 测试连接
|
||||
|
||||
```csharp
|
||||
var success = await _accessDataService.TestConnectionAsync();
|
||||
if (success)
|
||||
{
|
||||
Console.WriteLine("数据库连接成功!");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 完整示例
|
||||
|
||||
```csharp
|
||||
public class UserService
|
||||
{
|
||||
private readonly IAccessDataService _accessDataService;
|
||||
|
||||
public UserService(IAccessDataService accessDataService)
|
||||
{
|
||||
_accessDataService = accessDataService;
|
||||
}
|
||||
|
||||
// 获取所有用户
|
||||
public async Task<List<UserInfoDto>> GetAllUsersAsync()
|
||||
{
|
||||
return await _accessDataService.QueryAsync<UserInfoDto>(
|
||||
"SELECT UserName, [Password] FROM UserInfo");
|
||||
}
|
||||
|
||||
// 获取用户权限数量
|
||||
public async Task<int> GetUserPermissionCountAsync(string userName)
|
||||
{
|
||||
var count = await _accessDataService.ExecuteScalarAsync(
|
||||
"SELECT COUNT(*) FROM UserPermission WHERE UserInfo_UserName = ?",
|
||||
new OleDbParameter("@UserName", userName));
|
||||
|
||||
return Convert.ToInt32(count);
|
||||
}
|
||||
|
||||
// 添加用户
|
||||
public async Task<bool> AddUserAsync(string userName, string password)
|
||||
{
|
||||
// 检查是否已存在
|
||||
var exists = await _accessDataService.ExecuteScalarAsync(
|
||||
"SELECT COUNT(*) FROM UserInfo WHERE UserName = ?",
|
||||
new OleDbParameter("@UserName", userName));
|
||||
|
||||
if (Convert.ToInt32(exists) > 0)
|
||||
return false;
|
||||
|
||||
// 插入用户
|
||||
await _accessDataService.ExecuteAsync(
|
||||
"INSERT INTO UserInfo (UserName, [Password]) VALUES (?, ?)",
|
||||
new OleDbParameter("@UserName", userName),
|
||||
new OleDbParameter("@Password", password));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 删除用户(包含权限)
|
||||
public async Task DeleteUserAsync(string userName)
|
||||
{
|
||||
await _accessDataService.ExecuteInTransactionAsync(async transaction =>
|
||||
{
|
||||
// 先删除权限
|
||||
await transaction.ExecuteAsync(
|
||||
"DELETE FROM UserPermission WHERE UserInfo_UserName = ?",
|
||||
new OleDbParameter("@UserName", userName));
|
||||
|
||||
// 再删除用户
|
||||
await transaction.ExecuteAsync(
|
||||
"DELETE FROM UserInfo WHERE UserName = ?",
|
||||
new OleDbParameter("@UserName", userName));
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 核心优势总结
|
||||
|
||||
| 特性 | 直接使用 OleDb | 使用 AccessDataService |
|
||||
|------|---------------|----------------------|
|
||||
| 连接管理 | 手动创建、打开、释放 | ✅ 自动管理 |
|
||||
| 结果映射 | 手动读取 DataReader | ✅ 自动泛型映射 |
|
||||
| 事务管理 | 手动 BeginTransaction/Commit/Rollback | ✅ 自动管理 |
|
||||
| 代码量 | 60+ 行 | ✅ 10 行左右 |
|
||||
| 可维护性 | 低(大量重复代码) | ✅ 高 |
|
||||
| 易用性 | 低(需要理解 ADO.NET) | ✅ 高 |
|
||||
|
||||
---
|
||||
|
||||
## 📖 参考文件
|
||||
|
||||
- **核心实现**: `Data.Access/AccessDataService.cs`
|
||||
- **接口定义**: `Data.Access/IAccessDataService.cs`
|
||||
- **配置选项**: `Data.Access/AccessDataServiceOptions.cs`
|
||||
- **使用示例**: `Server/Views/AccessDatabaseWindowOleDb.xaml.cs`
|
||||
|
||||
---
|
||||
|
||||
**总结**:Data.Access 模块通过封装 OleDb 的复杂度,提供了简洁优雅的 API,让调用者可以用更少的代码完成相同的功能!
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Windows;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Config;
|
||||
using Sharp.Log;
|
||||
using Sharp.Data;
|
||||
using Sharp.Data.Context;
|
||||
using Sharp.Data.Access;
|
||||
using Server.Config;
|
||||
using Server.Services;
|
||||
using Server.Data;
|
||||
@@ -20,6 +22,7 @@ namespace Server
|
||||
public ILoggerFactory? LoggerFactory { get; private set; }
|
||||
public ServerService? ServerService { get; private set; }
|
||||
public IDataContext? DataContext { get; private set; }
|
||||
public IAccessDataService? AccessDataService { get; private set; }
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
@@ -36,7 +39,10 @@ namespace Server
|
||||
// 3. 初始化数据库
|
||||
InitializeDatabase();
|
||||
|
||||
// 4. 初始化服务器服务
|
||||
// 4. 初始化 Access 数据服务
|
||||
InitializeAccessDataService();
|
||||
|
||||
// 5. 初始化服务器服务
|
||||
InitializeServerService();
|
||||
|
||||
var logger = LoggerFactory?.CreateLogger<App>();
|
||||
@@ -105,6 +111,36 @@ namespace Server
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAccessDataService()
|
||||
{
|
||||
if (LoggerFactory == null)
|
||||
{
|
||||
throw new InvalidOperationException("日志系统未初始化");
|
||||
}
|
||||
|
||||
var logger = LoggerFactory.CreateLogger<App>();
|
||||
logger.LogInformation("正在初始化 Access 数据服务...");
|
||||
|
||||
try
|
||||
{
|
||||
var databasePath = Path.GetFullPath("dbDwDmsSetup.accdb");
|
||||
var options = new AccessDataServiceOptions
|
||||
{
|
||||
DatabasePath = databasePath
|
||||
};
|
||||
|
||||
AccessDataService = new AccessDataService(options, LoggerFactory.CreateLogger<AccessDataService>());
|
||||
|
||||
logger.LogInformation("Access 数据服务初始化成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Access 数据服务初始化失败");
|
||||
// 不抛出异常,因为 Access 数据库是可选的
|
||||
logger.LogWarning("Access 数据服务初始化失败,但应用程序将继续运行");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeServerService()
|
||||
{
|
||||
if (AppConfig == null || LoggerFactory == null)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Server.Data.Access;
|
||||
|
||||
/// <summary>
|
||||
/// Access 数据库上下文
|
||||
/// </summary>
|
||||
public class AccessDbContext : DbContext
|
||||
{
|
||||
public AccessDbContext(DbContextOptions<AccessDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<UserInfo> UserInfos => Set<UserInfo>();
|
||||
public DbSet<UserPermission> UserPermissions => Set<UserPermission>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<UserInfo>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.UserName);
|
||||
entity.Property(e => e.UserName).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Password).IsRequired().HasMaxLength(255);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<UserPermission>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.UserInfo_UserName, e.Permission_Code });
|
||||
entity.Property(e => e.UserInfo_UserName).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Permission_Code).IsRequired().HasMaxLength(255);
|
||||
|
||||
entity.HasOne(e => e.UserInfo)
|
||||
.WithMany(u => u.Permissions)
|
||||
.HasForeignKey(e => e.UserInfo_UserName)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Server.Data.Access;
|
||||
|
||||
/// <summary>
|
||||
/// UserInfo 表实体
|
||||
/// </summary>
|
||||
[Table("UserInfo")]
|
||||
public class UserInfo
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
public ICollection<UserPermission> Permissions { get; set; } = new List<UserPermission>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UserPermission 表实体
|
||||
/// </summary>
|
||||
[Table("UserPermission")]
|
||||
public class UserPermission
|
||||
{
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string UserInfo_UserName { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Permission_Code { get; set; } = string.Empty;
|
||||
|
||||
[ForeignKey(nameof(UserInfo_UserName))]
|
||||
public UserInfo? UserInfo { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
# Access 数据库 CRUD 完整示例
|
||||
|
||||
使用 **System.Data.OleDb** 对 Access 数据库进行增删改查操作。
|
||||
|
||||
## 📦 连接字符串
|
||||
|
||||
```csharp
|
||||
var connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=dbDwDmsSetup.accdb";
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 查询(SELECT)
|
||||
|
||||
### 查询所有用户
|
||||
|
||||
```csharp
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
var users = new List<UserInfo>();
|
||||
using var command = new OleDbCommand("SELECT UserName, Password FROM UserInfo", connection);
|
||||
using var reader = command.ExecuteReader();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
var user = new UserInfo
|
||||
{
|
||||
UserName = reader["UserName"]?.ToString() ?? "",
|
||||
Password = reader["Password"]?.ToString() ?? ""
|
||||
};
|
||||
users.Add(user);
|
||||
}
|
||||
|
||||
Console.WriteLine($"查询到 {users.Count} 个用户");
|
||||
```
|
||||
|
||||
### 条件查询
|
||||
|
||||
```csharp
|
||||
// 根据用户名查询
|
||||
using var command = new OleDbCommand(
|
||||
"SELECT UserName, Password FROM UserInfo WHERE UserName = ?", connection);
|
||||
command.Parameters.AddWithValue("@UserName", "admin");
|
||||
|
||||
using var reader = command.ExecuteReader();
|
||||
if (reader.Read())
|
||||
{
|
||||
var userName = reader["UserName"]?.ToString();
|
||||
var password = reader["Password"]?.ToString();
|
||||
Console.WriteLine($"找到用户: {userName}");
|
||||
}
|
||||
```
|
||||
|
||||
### 统计数量
|
||||
|
||||
```csharp
|
||||
using var command = new OleDbCommand(
|
||||
"SELECT COUNT(*) FROM UserPermission WHERE UserInfo_UserName = ?", connection);
|
||||
command.Parameters.AddWithValue("@UserName", "admin");
|
||||
|
||||
var count = Convert.ToInt32(command.ExecuteScalar());
|
||||
Console.WriteLine($"该用户有 {count} 个权限");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 添加(INSERT)
|
||||
|
||||
### 添加用户
|
||||
|
||||
```csharp
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
// 1. 先检查用户是否已存在
|
||||
using (var checkCmd = new OleDbCommand(
|
||||
"SELECT COUNT(*) FROM UserInfo WHERE UserName = ?", connection))
|
||||
{
|
||||
checkCmd.Parameters.AddWithValue("@UserName", "newuser");
|
||||
var exists = Convert.ToInt32(checkCmd.ExecuteScalar()) > 0;
|
||||
|
||||
if (exists)
|
||||
{
|
||||
Console.WriteLine("用户已存在!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 插入新用户
|
||||
using (var insertCmd = new OleDbCommand(
|
||||
"INSERT INTO UserInfo (UserName, Password) VALUES (?, ?)", connection))
|
||||
{
|
||||
insertCmd.Parameters.AddWithValue("@UserName", "newuser");
|
||||
insertCmd.Parameters.AddWithValue("@Password", "password123");
|
||||
|
||||
var rowsAffected = insertCmd.ExecuteNonQuery();
|
||||
Console.WriteLine($"成功添加 {rowsAffected} 个用户");
|
||||
}
|
||||
```
|
||||
|
||||
### 添加权限
|
||||
|
||||
```csharp
|
||||
using var insertCmd = new OleDbCommand(
|
||||
"INSERT INTO UserPermission (UserInfo_UserName, Permission_Code) VALUES (?, ?)", connection);
|
||||
insertCmd.Parameters.AddWithValue("@UserName", "newuser");
|
||||
insertCmd.Parameters.AddWithValue("@PermissionCode", "READ_DATA");
|
||||
|
||||
var rowsAffected = insertCmd.ExecuteNonQuery();
|
||||
Console.WriteLine($"成功添加权限: {rowsAffected} 条");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 修改(UPDATE)
|
||||
|
||||
### 修改用户密码
|
||||
|
||||
```csharp
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
using var updateCmd = new OleDbCommand(
|
||||
"UPDATE UserInfo SET Password = ? WHERE UserName = ?", connection);
|
||||
updateCmd.Parameters.AddWithValue("@Password", "newpassword");
|
||||
updateCmd.Parameters.AddWithValue("@UserName", "admin");
|
||||
|
||||
var rowsAffected = updateCmd.ExecuteNonQuery();
|
||||
|
||||
if (rowsAffected > 0)
|
||||
{
|
||||
Console.WriteLine("密码修改成功!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("用户不存在!");
|
||||
}
|
||||
```
|
||||
|
||||
### 批量更新
|
||||
|
||||
```csharp
|
||||
// 将所有用户的密码更新为默认密码
|
||||
using var updateCmd = new OleDbCommand(
|
||||
"UPDATE UserInfo SET Password = ? WHERE Password IS NULL OR Password = ''", connection);
|
||||
updateCmd.Parameters.AddWithValue("@Password", "default123");
|
||||
|
||||
var rowsAffected = updateCmd.ExecuteNonQuery();
|
||||
Console.WriteLine($"更新了 {rowsAffected} 个用户的密码");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4️⃣ 删除(DELETE)
|
||||
|
||||
### 删除用户(含事务)
|
||||
|
||||
```csharp
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
// 使用事务确保数据一致性
|
||||
using var transaction = connection.BeginTransaction();
|
||||
|
||||
try
|
||||
{
|
||||
// 1. 先删除用户的所有权限
|
||||
using (var deletePermCmd = new OleDbCommand(
|
||||
"DELETE FROM UserPermission WHERE UserInfo_UserName = ?", connection, transaction))
|
||||
{
|
||||
deletePermCmd.Parameters.AddWithValue("@UserName", "testuser");
|
||||
var permCount = deletePermCmd.ExecuteNonQuery();
|
||||
Console.WriteLine($"删除了 {permCount} 个权限");
|
||||
}
|
||||
|
||||
// 2. 再删除用户
|
||||
using (var deleteUserCmd = new OleDbCommand(
|
||||
"DELETE FROM UserInfo WHERE UserName = ?", connection, transaction))
|
||||
{
|
||||
deleteUserCmd.Parameters.AddWithValue("@UserName", "testuser");
|
||||
var userCount = deleteUserCmd.ExecuteNonQuery();
|
||||
|
||||
if (userCount == 0)
|
||||
{
|
||||
transaction.Rollback();
|
||||
Console.WriteLine("用户不存在!");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"删除了 {userCount} 个用户");
|
||||
}
|
||||
|
||||
// 3. 提交事务
|
||||
transaction.Commit();
|
||||
Console.WriteLine("删除成功!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 回滚事务
|
||||
transaction.Rollback();
|
||||
Console.WriteLine($"删除失败: {ex.Message}");
|
||||
}
|
||||
```
|
||||
|
||||
### 删除权限
|
||||
|
||||
```csharp
|
||||
using var deleteCmd = new OleDbCommand(
|
||||
"DELETE FROM UserPermission WHERE UserInfo_UserName = ? AND Permission_Code = ?", connection);
|
||||
deleteCmd.Parameters.AddWithValue("@UserName", "admin");
|
||||
deleteCmd.Parameters.AddWithValue("@PermissionCode", "DELETE_DATA");
|
||||
|
||||
var rowsAffected = deleteCmd.ExecuteNonQuery();
|
||||
Console.WriteLine($"删除了 {rowsAffected} 个权限");
|
||||
```
|
||||
|
||||
### 批量删除
|
||||
|
||||
```csharp
|
||||
// 删除所有没有权限的用户
|
||||
using var deleteCmd = new OleDbCommand(@"
|
||||
DELETE FROM UserInfo
|
||||
WHERE UserName NOT IN (
|
||||
SELECT DISTINCT UserInfo_UserName FROM UserPermission
|
||||
)", connection);
|
||||
|
||||
var rowsAffected = deleteCmd.ExecuteNonQuery();
|
||||
Console.WriteLine($"删除了 {rowsAffected} 个无权限用户");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔥 高级操作
|
||||
|
||||
### 1. JOIN 查询
|
||||
|
||||
```csharp
|
||||
// 查询用户及其所有权限
|
||||
using var command = new OleDbCommand(@"
|
||||
SELECT u.UserName, u.Password, p.Permission_Code
|
||||
FROM UserInfo u
|
||||
LEFT JOIN UserPermission p ON u.UserName = p.UserInfo_UserName
|
||||
ORDER BY u.UserName", connection);
|
||||
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
var userName = reader["UserName"]?.ToString();
|
||||
var permission = reader["Permission_Code"]?.ToString();
|
||||
Console.WriteLine($"{userName}: {permission ?? "(无权限)"}");
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 分组统计
|
||||
|
||||
```csharp
|
||||
// 统计每个用户的权限数量
|
||||
using var command = new OleDbCommand(@"
|
||||
SELECT u.UserName, COUNT(p.Permission_Code) AS PermCount
|
||||
FROM UserInfo u
|
||||
LEFT JOIN UserPermission p ON u.UserName = p.UserInfo_UserName
|
||||
GROUP BY u.UserName
|
||||
ORDER BY PermCount DESC", connection);
|
||||
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
var userName = reader["UserName"]?.ToString();
|
||||
var count = Convert.ToInt32(reader["PermCount"]);
|
||||
Console.WriteLine($"{userName}: {count} 个权限");
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 参数化批量插入
|
||||
|
||||
```csharp
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
var users = new[]
|
||||
{
|
||||
("user1", "pass1"),
|
||||
("user2", "pass2"),
|
||||
("user3", "pass3")
|
||||
};
|
||||
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
using var insertCmd = new OleDbCommand(
|
||||
"INSERT INTO UserInfo (UserName, Password) VALUES (?, ?)", connection, transaction);
|
||||
|
||||
foreach (var (userName, password) in users)
|
||||
{
|
||||
insertCmd.Parameters.Clear();
|
||||
insertCmd.Parameters.AddWithValue("@UserName", userName);
|
||||
insertCmd.Parameters.AddWithValue("@Password", password);
|
||||
insertCmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
Console.WriteLine($"成功添加 {users.Length} 个用户");
|
||||
}
|
||||
catch
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 存在性检查
|
||||
|
||||
```csharp
|
||||
// 检查用户是否存在
|
||||
public bool UserExists(string userName)
|
||||
{
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
using var command = new OleDbCommand(
|
||||
"SELECT COUNT(*) FROM UserInfo WHERE UserName = ?", connection);
|
||||
command.Parameters.AddWithValue("@UserName", userName);
|
||||
|
||||
return Convert.ToInt32(command.ExecuteScalar()) > 0;
|
||||
}
|
||||
|
||||
// 使用
|
||||
if (UserExists("admin"))
|
||||
{
|
||||
Console.WriteLine("用户存在");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 最佳实践
|
||||
|
||||
### 1. 使用 using 语句
|
||||
|
||||
```csharp
|
||||
// ✅ 推荐:自动释放资源
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
using var command = new OleDbCommand(sql, connection);
|
||||
using var reader = command.ExecuteReader();
|
||||
|
||||
// ❌ 不推荐:需要手动释放
|
||||
var connection = new OleDbConnection(connectionString);
|
||||
try
|
||||
{
|
||||
// ...
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.Dispose();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 使用参数化查询(防止 SQL 注入)
|
||||
|
||||
```csharp
|
||||
// ✅ 推荐:参数化查询
|
||||
var sql = "SELECT * FROM UserInfo WHERE UserName = ?";
|
||||
command.Parameters.AddWithValue("@UserName", userName);
|
||||
|
||||
// ❌ 危险:字符串拼接(SQL 注入风险)
|
||||
var sql = $"SELECT * FROM UserInfo WHERE UserName = '{userName}'";
|
||||
```
|
||||
|
||||
### 3. 使用事务保证数据一致性
|
||||
|
||||
```csharp
|
||||
// ✅ 推荐:使用事务
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
// 多个操作
|
||||
command1.ExecuteNonQuery();
|
||||
command2.ExecuteNonQuery();
|
||||
transaction.Commit();
|
||||
}
|
||||
catch
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 异常处理
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
// 数据库操作
|
||||
}
|
||||
catch (OleDbException ex)
|
||||
{
|
||||
Console.WriteLine($"数据库错误: {ex.Message}");
|
||||
// 记录日志
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"未知错误: {ex.Message}");
|
||||
// 记录日志
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 完整示例项目
|
||||
|
||||
完整的 CRUD 操作已集成到 **Server 项目**中:
|
||||
|
||||
**文件位置**:
|
||||
- UI: `Server/Views/AccessDatabaseWindowOleDb.xaml`
|
||||
- 代码: `Server/Views/AccessDatabaseWindowOleDb.xaml.cs`
|
||||
|
||||
**功能列表**:
|
||||
- ✅ 查询所有用户
|
||||
- ✅ 添加新用户
|
||||
- ✅ 修改用户密码
|
||||
- ✅ 删除用户(含级联删除权限)
|
||||
- ✅ 添加用户权限
|
||||
- ✅ 删除用户权限
|
||||
- ✅ 事务支持
|
||||
- ✅ 异常处理
|
||||
- ✅ 异步操作(不阻塞 UI)
|
||||
|
||||
**运行方式**:
|
||||
1. 启动 Server 项目
|
||||
2. 点击 "Access数据库" 按钮
|
||||
3. 尽情测试增删改查功能!
|
||||
|
||||
数据库随意修改,不用担心!🎉
|
||||
@@ -0,0 +1,186 @@
|
||||
# Access 数据库访问方案对比
|
||||
|
||||
## 方案选择
|
||||
|
||||
### ✅ 方案 A:System.Data.OleDb(推荐)
|
||||
|
||||
**适用场景**:
|
||||
- 简单的增删改查操作
|
||||
- 性能要求高
|
||||
- 需要稳定可靠的方案
|
||||
- 不需要复杂的 LINQ 查询
|
||||
|
||||
**实现文件**:
|
||||
- `Views/AccessDatabaseWindowOleDb.xaml`
|
||||
- `Views/AccessDatabaseWindowOleDb.xaml.cs`
|
||||
|
||||
**示例代码**:
|
||||
```csharp
|
||||
// 1. 创建连接
|
||||
var connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=dbDwDmsSetup.accdb";
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
// 2. 查询数据
|
||||
using var command = new OleDbCommand("SELECT UserName, Password FROM UserInfo", connection);
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
var userName = reader["UserName"]?.ToString();
|
||||
var password = reader["Password"]?.ToString();
|
||||
}
|
||||
|
||||
// 3. 插入数据
|
||||
using var insertCmd = new OleDbCommand(
|
||||
"INSERT INTO UserInfo (UserName, Password) VALUES (?, ?)", connection);
|
||||
insertCmd.Parameters.AddWithValue("@UserName", "newuser");
|
||||
insertCmd.Parameters.AddWithValue("@Password", "password");
|
||||
insertCmd.ExecuteNonQuery();
|
||||
|
||||
// 4. 更新数据
|
||||
using var updateCmd = new OleDbCommand(
|
||||
"UPDATE UserInfo SET Password = ? WHERE UserName = ?", connection);
|
||||
updateCmd.Parameters.AddWithValue("@Password", "newpassword");
|
||||
updateCmd.Parameters.AddWithValue("@UserName", "username");
|
||||
updateCmd.ExecuteNonQuery();
|
||||
|
||||
// 5. 删除数据
|
||||
using var deleteCmd = new OleDbCommand(
|
||||
"DELETE FROM UserInfo WHERE UserName = ?", connection);
|
||||
deleteCmd.Parameters.AddWithValue("@UserName", "username");
|
||||
deleteCmd.ExecuteNonQuery();
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 微软官方支持
|
||||
- ✅ 无需额外 NuGet 包
|
||||
- ✅ 性能最佳
|
||||
- ✅ 稳定可靠
|
||||
- ✅ 适用于所有 .NET 版本
|
||||
|
||||
**缺点**:
|
||||
- ❌ 需要手写 SQL
|
||||
- ❌ 没有 LINQ 支持
|
||||
- ❌ 没有自动实体映射
|
||||
|
||||
---
|
||||
|
||||
### 方案 B:EntityFrameworkCore.Jet
|
||||
|
||||
**适用场景**:
|
||||
- 需要复杂的 LINQ 查询
|
||||
- 需要实体关系映射
|
||||
- 代码优雅度要求高
|
||||
- 可以接受第三方库
|
||||
|
||||
**实现文件**:
|
||||
- `Views/AccessDatabaseWindow.xaml`
|
||||
- `Views/AccessDatabaseWindow.xaml.cs`
|
||||
- `Data/Access/AccessEntities.cs`
|
||||
- `Data/Access/AccessDbContext.cs`
|
||||
|
||||
**示例代码**:
|
||||
```csharp
|
||||
// 1. 创建 DbContext
|
||||
var optionsBuilder = new DbContextOptionsBuilder<AccessDbContext>();
|
||||
optionsBuilder.UseJet("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=dbDwDmsSetup.accdb");
|
||||
using var dbContext = new AccessDbContext(optionsBuilder.Options);
|
||||
|
||||
// 2. LINQ 查询
|
||||
var users = await dbContext.UserInfos
|
||||
.Include(u => u.Permissions)
|
||||
.Where(u => u.UserName.Contains("admin"))
|
||||
.ToListAsync();
|
||||
|
||||
// 3. 插入数据
|
||||
var newUser = new UserInfo { UserName = "newuser", Password = "password" };
|
||||
dbContext.UserInfos.Add(newUser);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
// 4. 更新数据
|
||||
var user = await dbContext.UserInfos.FindAsync("username");
|
||||
if (user != null)
|
||||
{
|
||||
user.Password = "newpassword";
|
||||
await dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// 5. 删除数据
|
||||
var userToDelete = await dbContext.UserInfos.FindAsync("username");
|
||||
if (userToDelete != null)
|
||||
{
|
||||
dbContext.UserInfos.Remove(userToDelete);
|
||||
await dbContext.SaveChangesAsync();
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ LINQ 查询支持
|
||||
- ✅ 自动实体映射
|
||||
- ✅ 代码更优雅
|
||||
- ✅ 支持导航属性
|
||||
|
||||
**缺点**:
|
||||
- ❌ 第三方库(EntityFrameworkCore.Jet)
|
||||
- ❌ 版本兼容性问题
|
||||
- ❌ 性能开销
|
||||
- ❌ 可能与其他 EF Core 提供程序冲突
|
||||
|
||||
---
|
||||
|
||||
## 推荐方案
|
||||
|
||||
### 对于简单的增删改查:
|
||||
|
||||
**推荐使用方案 A(System.Data.OleDb)**
|
||||
|
||||
理由:
|
||||
1. 稳定可靠,微软官方支持
|
||||
2. 性能最佳
|
||||
3. 无依赖问题
|
||||
4. 代码简单直接
|
||||
|
||||
### 对于复杂的业务逻辑:
|
||||
|
||||
可以考虑方案 B(EntityFrameworkCore.Jet),但需要:
|
||||
1. 接受第三方库的风险
|
||||
2. 处理版本兼容性问题
|
||||
3. 确保不与其他数据库提供程序冲突
|
||||
|
||||
---
|
||||
|
||||
## 当前 Server 项目使用的方案
|
||||
|
||||
当前使用:**方案 A(System.Data.OleDb)** ✅
|
||||
|
||||
主窗口按钮 "Access数据库" 打开的是 `AccessDatabaseWindowOleDb` 窗口。
|
||||
|
||||
如需切换到方案 B,修改 `MainWindow.xaml.cs` 中的 `AccessDbButton_Click` 方法:
|
||||
|
||||
```csharp
|
||||
// 方案 A(当前)
|
||||
var window = new AccessDatabaseWindowOleDb { Owner = this };
|
||||
|
||||
// 方案 B
|
||||
var window = new AccessDatabaseWindow { Owner = this };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能对比
|
||||
|
||||
| 操作 | OleDb | EF Core Jet |
|
||||
|------|-------|-------------|
|
||||
| 简单查询 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
| 复杂查询 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| 插入操作 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
| 批量操作 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
|
||||
| 内存占用 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
对于你的需求(简单的增删改查),**强烈推荐使用 System.Data.OleDb(方案 A)**。
|
||||
|
||||
它是最稳定、性能最好、最简单的方案。
|
||||
@@ -55,6 +55,8 @@
|
||||
Background="#FF9800" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand" IsEnabled="False"/>
|
||||
<Button x:Name="DataManagementButton" Content="数据管理" Width="100" Height="32" Margin="0,0,10,0" Click="DataManagementButton_Click"
|
||||
Background="#673AB7" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
||||
<Button x:Name="AccessDbButton" Content="Access数据库" Width="110" Height="32" Margin="0,0,10,0" Click="AccessDbButton_Click"
|
||||
Background="#009688" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
||||
<Button x:Name="ClearLogButton" Content="清空日志" Width="100" Height="32" Click="ClearLogButton_Click"
|
||||
Background="#9E9E9E" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
||||
</StackPanel>
|
||||
|
||||
@@ -155,6 +155,26 @@ namespace Server
|
||||
}
|
||||
}
|
||||
|
||||
private void AccessDbButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation("打开 Access 数据库管理窗口 (OleDb)");
|
||||
var window = new AccessDatabaseWindowOleDb
|
||||
{
|
||||
Owner = this
|
||||
};
|
||||
window.ShowDialog();
|
||||
_logger?.LogInformation("Access 数据库管理窗口已关闭");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "打开 Access 数据库管理窗口失败");
|
||||
AppendLog($"打开 Access 数据库管理窗口失败: {ex.Message}");
|
||||
MessageBox.Show($"打开 Access 数据库管理窗口失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnServerStatusChanged(object? sender, string status)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
<ProjectReference Include="..\Data\Data.csproj" />
|
||||
<ProjectReference Include="..\Data.Access\Data.Access.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -34,6 +35,9 @@
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="dbDwDmsSetup.accdb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,54 @@
|
||||
<Window x:Class="Server.Views.AccessDatabaseWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="Access 数据库管理" Height="600" Width="900"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="200"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题和操作栏 -->
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="Access 数据库管理" FontSize="20" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text=" (dbDwDmsSetup.accdb)" FontSize="14" Foreground="Gray" VerticalAlignment="Center" Margin="5,0,0,0"/>
|
||||
<Button Content="刷新数据" Click="RefreshButton_Click" Margin="20,0,0,0" Padding="10,5"/>
|
||||
<Button Content="测试连接" Click="TestConnectionButton_Click" Margin="10,0,0,0" Padding="10,5"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<GroupBox Grid.Row="1" Header="用户列表" Margin="0,0,0,10">
|
||||
<DataGrid x:Name="UsersDataGrid"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="UsersDataGrid_SelectionChanged">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="用户名" Binding="{Binding UserName}" Width="200"/>
|
||||
<DataGridTextColumn Header="密码" Binding="{Binding Password}" Width="*"/>
|
||||
<DataGridTextColumn Header="权限数量" Binding="{Binding Permissions.Count}" Width="100"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 分隔符 -->
|
||||
<GridSplitter Grid.Row="2" Height="5" HorizontalAlignment="Stretch" Background="LightGray"/>
|
||||
|
||||
<!-- 权限列表 -->
|
||||
<GroupBox Grid.Row="3" Header="选中用户的权限">
|
||||
<DataGrid x:Name="PermissionsDataGrid"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="权限代码" Binding="{Binding Permission_Code}" Width="*"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using EntityFrameworkCore.Jet;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Server.Data.Access;
|
||||
|
||||
namespace Server.Views;
|
||||
|
||||
public partial class AccessDatabaseWindow : Window
|
||||
{
|
||||
private readonly ILogger<AccessDatabaseWindow>? _logger;
|
||||
private const string DatabaseFile = "dbDwDmsSetup.accdb";
|
||||
|
||||
public AccessDatabaseWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
try
|
||||
{
|
||||
var app = (App)Application.Current;
|
||||
_logger = app.LoggerFactory?.CreateLogger<AccessDatabaseWindow>();
|
||||
|
||||
Loaded += AccessDatabaseWindow_Loaded;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"初始化失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void AccessDatabaseWindow_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await LoadDataAsync();
|
||||
}
|
||||
|
||||
private async void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await LoadDataAsync();
|
||||
}
|
||||
|
||||
private async void TestConnectionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation("测试 Access 数据库连接...");
|
||||
|
||||
using var dbContext = CreateDbContext();
|
||||
var canConnect = await dbContext.Database.CanConnectAsync();
|
||||
|
||||
if (canConnect)
|
||||
{
|
||||
_logger?.LogInformation("数据库连接成功!");
|
||||
MessageBox.Show("数据库连接成功!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogWarning("无法连接到数据库");
|
||||
MessageBox.Show("无法连接到数据库", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "测试连接失败");
|
||||
MessageBox.Show($"测试连接失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation("开始加载 Access 数据库数据...");
|
||||
|
||||
using var dbContext = CreateDbContext();
|
||||
|
||||
// 查询所有用户及其权限
|
||||
var users = await dbContext.UserInfos
|
||||
.Include(u => u.Permissions)
|
||||
.ToListAsync();
|
||||
|
||||
_logger?.LogInformation($"加载了 {users.Count} 个用户");
|
||||
|
||||
// 更新 UI
|
||||
UsersDataGrid.ItemsSource = users;
|
||||
PermissionsDataGrid.ItemsSource = null;
|
||||
|
||||
MessageBox.Show($"成功加载 {users.Count} 个用户!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "加载数据失败");
|
||||
MessageBox.Show($"加载数据失败: {ex.Message}\n\n详细信息:\n{ex.ToString()}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void UsersDataGrid_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||
{
|
||||
if (UsersDataGrid.SelectedItem is UserInfo selectedUser)
|
||||
{
|
||||
_logger?.LogInformation($"选中用户: {selectedUser.UserName}, 权限数: {selectedUser.Permissions.Count}");
|
||||
PermissionsDataGrid.ItemsSource = selectedUser.Permissions;
|
||||
}
|
||||
else
|
||||
{
|
||||
PermissionsDataGrid.ItemsSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建 Access 数据库上下文
|
||||
/// </summary>
|
||||
private AccessDbContext CreateDbContext()
|
||||
{
|
||||
// 获取数据库文件的完整路径
|
||||
var dbPath = Path.GetFullPath(DatabaseFile);
|
||||
|
||||
if (!File.Exists(dbPath))
|
||||
{
|
||||
throw new FileNotFoundException($"找不到 Access 数据库文件: {dbPath}");
|
||||
}
|
||||
|
||||
// 构建连接字符串
|
||||
var connectionString = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath}";
|
||||
|
||||
// 创建 DbContext 选项
|
||||
var optionsBuilder = new DbContextOptionsBuilder<AccessDbContext>();
|
||||
optionsBuilder.UseJet(connectionString);
|
||||
|
||||
return new AccessDbContext(optionsBuilder.Options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<Window x:Class="Server.Views.AccessDatabaseWindowOleDb"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Access 数据库管理 (OleDb方式)" Height="700" Width="1000"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="200"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题和操作栏 -->
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="Access 数据库管理" FontSize="20" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text=" (System.Data.OleDb)" FontSize="14" Foreground="Green" VerticalAlignment="Center" Margin="5,0,0,0"/>
|
||||
<Button Content="刷新数据" Click="RefreshButton_Click" Margin="20,0,0,0" Padding="10,5"/>
|
||||
<Button Content="测试连接" Click="TestConnectionButton_Click" Margin="10,0,0,0" Padding="10,5"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 用户操作区 -->
|
||||
<GroupBox Grid.Row="1" Header="用户操作" Margin="0,0,0,10">
|
||||
<StackPanel Orientation="Horizontal" Margin="5">
|
||||
<TextBlock Text="用户名:" VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBox x:Name="UserNameTextBox" Width="150" Margin="0,0,10,0"/>
|
||||
<TextBlock Text="密码:" VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBox x:Name="PasswordTextBox" Width="150" Margin="0,0,20,0"/>
|
||||
<Button Content="添加用户" Click="AddUserButton_Click" Padding="10,5" Margin="0,0,5,0" Background="#4CAF50" Foreground="White"/>
|
||||
<Button Content="修改密码" Click="UpdatePasswordButton_Click" Padding="10,5" Margin="0,0,5,0" Background="#FF9800" Foreground="White"/>
|
||||
<Button Content="删除用户" Click="DeleteUserButton_Click" Padding="10,5" Background="#F44336" Foreground="White"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<GroupBox Grid.Row="2" Header="用户列表" Margin="0,0,0,10">
|
||||
<DataGrid x:Name="UsersDataGrid"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
SelectionMode="Single"
|
||||
SelectionChanged="UsersDataGrid_SelectionChanged">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="用户名" Binding="{Binding UserName}" Width="200"/>
|
||||
<DataGridTextColumn Header="密码" Binding="{Binding Password}" Width="*"/>
|
||||
<DataGridTextColumn Header="权限数量" Binding="{Binding PermissionCount}" Width="100"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 分隔符 -->
|
||||
<GridSplitter Grid.Row="3" Height="5" HorizontalAlignment="Stretch" Background="LightGray"/>
|
||||
|
||||
<!-- 权限列表 -->
|
||||
<GroupBox Grid.Row="4" Header="选中用户的权限">
|
||||
<DataGrid x:Name="PermissionsDataGrid"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="权限代码" Binding="{Binding Permission_Code}" Width="*"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- 权限操作区 -->
|
||||
<GroupBox Grid.Row="5" Header="权限操作" Margin="0,10,0,0">
|
||||
<StackPanel Orientation="Horizontal" Margin="5">
|
||||
<TextBlock Text="权限代码:" VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBox x:Name="PermissionCodeTextBox" Width="200" Margin="0,0,20,0"/>
|
||||
<Button Content="添加权限" Click="AddPermissionButton_Click" Padding="10,5" Margin="0,0,5,0" Background="#4CAF50" Foreground="White"/>
|
||||
<Button Content="删除权限" Click="DeletePermissionButton_Click" Padding="10,5" Background="#F44336" Foreground="White"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,501 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.OleDb;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sharp.Data.Access;
|
||||
|
||||
namespace Server.Views;
|
||||
|
||||
/// <summary>
|
||||
/// 使用 AccessDataService 访问 Access 数据库(推荐方式)
|
||||
/// 包含完整的增删改查功能
|
||||
/// </summary>
|
||||
public partial class AccessDatabaseWindowOleDb : Window
|
||||
{
|
||||
private readonly IAccessDataService? _accessDataService;
|
||||
private readonly ILogger<AccessDatabaseWindowOleDb>? _logger;
|
||||
private string? _selectedUserName;
|
||||
|
||||
public AccessDatabaseWindowOleDb()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
try
|
||||
{
|
||||
var app = (App)Application.Current;
|
||||
_logger = app.LoggerFactory?.CreateLogger<AccessDatabaseWindowOleDb>();
|
||||
_accessDataService = app.AccessDataService;
|
||||
|
||||
if (_accessDataService == null)
|
||||
{
|
||||
_logger?.LogWarning("AccessDataService 未初始化");
|
||||
MessageBox.Show("Access 数据服务未初始化,某些功能可能不可用", "警告",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
|
||||
Loaded += Window_Loaded;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"初始化失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await LoadDataAsync();
|
||||
}
|
||||
|
||||
#region 基础操作
|
||||
|
||||
private async void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await LoadDataAsync();
|
||||
}
|
||||
|
||||
private async void TestConnectionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_accessDataService == null)
|
||||
{
|
||||
MessageBox.Show("Access 数据服务未初始化", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation("测试 Access 数据库连接...");
|
||||
var success = await _accessDataService.TestConnectionAsync();
|
||||
|
||||
if (success)
|
||||
{
|
||||
_logger?.LogInformation("数据库连接成功!");
|
||||
MessageBox.Show("数据库连接成功!", "成功",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("数据库连接失败", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "测试连接失败");
|
||||
MessageBox.Show($"测试连接失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 用户管理(增删改查)
|
||||
|
||||
/// <summary>
|
||||
/// 添加用户
|
||||
/// </summary>
|
||||
private async void AddUserButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_accessDataService == null) return;
|
||||
|
||||
var userName = UserNameTextBox.Text.Trim();
|
||||
var password = PasswordTextBox.Text.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(userName))
|
||||
{
|
||||
MessageBox.Show("请输入用户名", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(password))
|
||||
{
|
||||
MessageBox.Show("请输入密码", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 检查用户是否已存在
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
// 插入新用户
|
||||
await _accessDataService.ExecuteAsync(
|
||||
"INSERT INTO UserInfo (UserName, [Password]) VALUES (?, ?)",
|
||||
new OleDbParameter("@UserName", userName),
|
||||
new OleDbParameter("@Password", password));
|
||||
|
||||
_logger?.LogInformation($"成功添加用户: {userName}");
|
||||
MessageBox.Show($"成功添加用户 '{userName}'!", "成功",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
UserNameTextBox.Clear();
|
||||
PasswordTextBox.Clear();
|
||||
await LoadDataAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "添加用户失败");
|
||||
MessageBox.Show($"添加用户失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改用户密码
|
||||
/// </summary>
|
||||
private async void UpdatePasswordButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_accessDataService == null) return;
|
||||
|
||||
var userName = UserNameTextBox.Text.Trim();
|
||||
var newPassword = PasswordTextBox.Text.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(userName))
|
||||
{
|
||||
MessageBox.Show("请输入用户名或从列表中选择用户", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(newPassword))
|
||||
{
|
||||
MessageBox.Show("请输入新密码", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MessageBox.Show(
|
||||
$"确定要修改用户 '{userName}' 的密码吗?",
|
||||
"确认",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// 更新密码
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger?.LogInformation($"成功修改用户 {userName} 的密码");
|
||||
MessageBox.Show($"成功修改用户 '{userName}' 的密码!", "成功",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
PasswordTextBox.Clear();
|
||||
await LoadDataAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "修改密码失败");
|
||||
MessageBox.Show($"修改密码失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除用户
|
||||
/// </summary>
|
||||
private async void DeleteUserButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_accessDataService == null) return;
|
||||
|
||||
var userName = UserNameTextBox.Text.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(userName))
|
||||
{
|
||||
MessageBox.Show("请输入用户名或从列表中选择用户", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MessageBox.Show(
|
||||
$"确定要删除用户 '{userName}' 及其所有权限吗?\n此操作不可恢复!",
|
||||
"确认删除",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// 使用事务删除用户及其权限
|
||||
await _accessDataService.ExecuteInTransactionAsync(async 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}' 不存在!");
|
||||
}
|
||||
});
|
||||
|
||||
_logger?.LogInformation($"成功删除用户: {userName}");
|
||||
MessageBox.Show($"成功删除用户 '{userName}'!", "成功",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
UserNameTextBox.Clear();
|
||||
PasswordTextBox.Clear();
|
||||
await LoadDataAsync();
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "警告",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "删除用户失败");
|
||||
MessageBox.Show($"删除用户失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 权限管理
|
||||
|
||||
/// <summary>
|
||||
/// 添加权限
|
||||
/// </summary>
|
||||
private async void AddPermissionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_accessDataService == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(_selectedUserName))
|
||||
{
|
||||
MessageBox.Show("请先选择一个用户", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var permissionCode = PermissionCodeTextBox.Text.Trim();
|
||||
if (string.IsNullOrEmpty(permissionCode))
|
||||
{
|
||||
MessageBox.Show("请输入权限代码", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 检查权限是否已存在
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
// 插入权限
|
||||
await _accessDataService.ExecuteAsync(
|
||||
"INSERT INTO UserPermission (UserInfo_UserName, Permission_Code) VALUES (?, ?)",
|
||||
new OleDbParameter("@UserName", _selectedUserName),
|
||||
new OleDbParameter("@PermissionCode", permissionCode));
|
||||
|
||||
_logger?.LogInformation($"成功为用户 {_selectedUserName} 添加权限: {permissionCode}");
|
||||
MessageBox.Show($"成功添加权限 '{permissionCode}'!", "成功",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
PermissionCodeTextBox.Clear();
|
||||
LoadUserPermissions(_selectedUserName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "添加权限失败");
|
||||
MessageBox.Show($"添加权限失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除权限
|
||||
/// </summary>
|
||||
private async void DeletePermissionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_accessDataService == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(_selectedUserName))
|
||||
{
|
||||
MessageBox.Show("请先选择一个用户", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PermissionsDataGrid.SelectedItem is not UserPermissionDto selectedPermission)
|
||||
{
|
||||
MessageBox.Show("请从列表中选择要删除的权限", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MessageBox.Show(
|
||||
$"确定要删除用户 '{_selectedUserName}' 的权限 '{selectedPermission.Permission_Code}' 吗?",
|
||||
"确认删除",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await _accessDataService.ExecuteAsync(
|
||||
"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}");
|
||||
MessageBox.Show($"成功删除权限 '{selectedPermission.Permission_Code}'!", "成功",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
LoadUserPermissions(_selectedUserName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "删除权限失败");
|
||||
MessageBox.Show($"删除权限失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据加载
|
||||
|
||||
private async System.Threading.Tasks.Task LoadDataAsync()
|
||||
{
|
||||
if (_accessDataService == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation("开始加载 Access 数据库数据...");
|
||||
|
||||
// 查询所有用户(使用泛型映射)
|
||||
var users = await _accessDataService.QueryAsync<UserInfoDto>(
|
||||
"SELECT [UserName], [Password] AS [Password] FROM [UserInfo]");
|
||||
|
||||
var userDetails = string.Join(" | ",
|
||||
users.Select(u => $"UserName='{u.UserName}', Password='{u.Password}'"));
|
||||
_logger?.LogInformation("用户列表: {UserDetails}", userDetails);
|
||||
|
||||
// 为每个用户查询权限数量
|
||||
foreach (var user in users)
|
||||
{
|
||||
var count = await _accessDataService.ExecuteScalarAsync(
|
||||
"SELECT COUNT(*) FROM UserPermission WHERE UserInfo_UserName = ?",
|
||||
new OleDbParameter("@UserName", user.UserName));
|
||||
user.PermissionCount = Convert.ToInt32(count);
|
||||
}
|
||||
|
||||
_logger?.LogInformation($"加载了 {users.Count} 个用户");
|
||||
|
||||
// 更新 UI
|
||||
UsersDataGrid.ItemsSource = users;
|
||||
PermissionsDataGrid.ItemsSource = null;
|
||||
_selectedUserName = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "加载数据失败");
|
||||
MessageBox.Show($"加载数据失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void UsersDataGrid_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||
{
|
||||
if (UsersDataGrid.SelectedItem is UserInfoDto selectedUser)
|
||||
{
|
||||
_selectedUserName = selectedUser.UserName;
|
||||
UserNameTextBox.Text = selectedUser.UserName;
|
||||
PasswordTextBox.Text = selectedUser.Password;
|
||||
LoadUserPermissions(selectedUser.UserName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedUserName = null;
|
||||
PermissionsDataGrid.ItemsSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async void LoadUserPermissions(string userName)
|
||||
{
|
||||
if (_accessDataService == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
// 查询用户权限(使用泛型映射)
|
||||
var permissions = await _accessDataService.QueryAsync<UserPermissionDto>(
|
||||
"SELECT Permission_Code FROM UserPermission WHERE UserInfo_UserName = ?",
|
||||
new OleDbParameter("@UserName", userName));
|
||||
|
||||
PermissionsDataGrid.ItemsSource = permissions;
|
||||
_logger?.LogInformation($"用户 {userName} 有 {permissions.Count} 个权限");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "加载权限失败");
|
||||
MessageBox.Show($"加载权限失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户信息 DTO
|
||||
/// </summary>
|
||||
public class UserInfoDto
|
||||
{
|
||||
public string UserName { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
public int PermissionCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户权限 DTO
|
||||
/// </summary>
|
||||
public class UserPermissionDto
|
||||
{
|
||||
public string Permission_Code { get; set; } = "";
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
{"Timestamp":"2025-11-19T10:17:05.3241502+08:00","Level":"Information","MessageTemplate":"正在初始化数据库...","RenderedMessage":"正在初始化数据库...","Properties":{"SourceContext":"Server.App","MachineName":"WEIYP","ProcessId":51312,"ThreadId":1,"EnvironmentName":"Production","Application":"SharpServer","Environment":"Production"}}
|
||||
{"Timestamp":"2025-11-19T10:17:06.0810621+08:00","Level":"Information","MessageTemplate":"数据库初始化成功","RenderedMessage":"数据库初始化成功","Properties":{"SourceContext":"Server.App","MachineName":"WEIYP","ProcessId":51312,"ThreadId":1,"EnvironmentName":"Production","Application":"SharpServer","Environment":"Production"}}
|
||||
{"Timestamp":"2025-11-19T10:17:06.0815557+08:00","Level":"Information","MessageTemplate":"应用程序启动成功","RenderedMessage":"应用程序启动成功","Properties":{"SourceContext":"Server.App","MachineName":"WEIYP","ProcessId":51312,"ThreadId":1,"EnvironmentName":"Production","Application":"SharpServer","Environment":"Production"}}
|
||||
{"Timestamp":"2025-11-19T10:21:42.6459518+08:00","Level":"Information","MessageTemplate":"应用程序正常退出","RenderedMessage":"应用程序正常退出","Properties":{"SourceContext":"Server.App","MachineName":"WEIYP","ProcessId":51312,"ThreadId":1,"EnvironmentName":"Production","Application":"SharpServer","Environment":"Production"}}
|
||||
@@ -1,10 +1,9 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||
// 重新生成代码,这些更改将会丢失。
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -14,7 +13,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Sharp.Wpf")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f7660c49e81d5fe2747f6514545ceb961c2066d3")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+e334e65c2d8abbef0063f92b88e6672859b4ea19")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Sharp.Wpf")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Sharp.Wpf")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
7ce8133edb934c064ba59400ce54843707360487a12d558ab17d6af09c0febb5
|
||||
b6ad12dd3a3fcd3e5222848b29876246dac97d6908271610ecbcd090928adcb7
|
||||
@@ -24,4 +24,10 @@
|
||||
<ProjectReference Include="..\..\src\Sharp\Common\Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="dbDwDmsSetup.accdb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,44 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Data.Access.Test.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 真实 Access 数据库的 DbContext
|
||||
/// </summary>
|
||||
public class AccessDbContext : DbContext
|
||||
{
|
||||
public AccessDbContext(DbContextOptions<AccessDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<UserInfoEntity> UserInfos => Set<UserInfoEntity>();
|
||||
public DbSet<UserPermissionEntity> UserPermissions => Set<UserPermissionEntity>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// 配置 UserInfo
|
||||
modelBuilder.Entity<UserInfoEntity>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.UserName);
|
||||
entity.Property(e => e.UserName).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Password).IsRequired().HasMaxLength(255);
|
||||
});
|
||||
|
||||
// 配置 UserPermission
|
||||
modelBuilder.Entity<UserPermissionEntity>(entity =>
|
||||
{
|
||||
// 复合主键
|
||||
entity.HasKey(e => new { e.UserInfo_UserName, e.Permission_Code });
|
||||
entity.Property(e => e.UserInfo_UserName).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.Permission_Code).IsRequired().HasMaxLength(255);
|
||||
|
||||
// 配置关系
|
||||
entity.HasOne(e => e.UserInfo)
|
||||
.WithMany(u => u.Permissions)
|
||||
.HasForeignKey(e => e.UserInfo_UserName)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.Access.Test.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// UserInfo 表的实体映射
|
||||
/// </summary>
|
||||
[Table("UserInfo")]
|
||||
public class UserInfoEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户名 (主键)
|
||||
/// </summary>
|
||||
[Key]
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 用户权限 (导航属性)
|
||||
/// </summary>
|
||||
public ICollection<UserPermissionEntity> Permissions { get; set; } = new List<UserPermissionEntity>();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.Access.Test.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// UserPermission 表的实体映射
|
||||
/// </summary>
|
||||
[Table("UserPermission")]
|
||||
public class UserPermissionEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户名 (外键)
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string UserInfo_UserName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 权限代码
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MaxLength(255)]
|
||||
public string Permission_Code { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 用户信息 (导航属性)
|
||||
/// </summary>
|
||||
[ForeignKey(nameof(UserInfo_UserName))]
|
||||
public UserInfoEntity? UserInfo { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Sharp.Data;
|
||||
using Sharp.Data.Access.DependencyInjection;
|
||||
using Sharp.Data.Access.Extensions;
|
||||
using Sharp.Data.Context;
|
||||
using Data.Access.Test.Entities;
|
||||
|
||||
namespace Data.Access.Test;
|
||||
|
||||
/// <summary>
|
||||
/// 使用真实 Access 数据库进行 CRUD 操作的完整测试
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class RealAccessDatabaseIntegrationTests
|
||||
{
|
||||
private const string DatabaseFile = "dbDwDmsSetup.accdb";
|
||||
|
||||
[TestMethod]
|
||||
public async Task CanQueryUsersUsingDataContextBuilder()
|
||||
{
|
||||
// 方式1: 使用 DataContextBuilder (无 DI)
|
||||
var builder = new DataContextBuilder<AccessDbContext>();
|
||||
builder.UseAccessDatabase(options =>
|
||||
{
|
||||
options.DatabaseFile = DatabaseFile;
|
||||
options.CopyTemplateWhenMissing = false;
|
||||
options.ThrowIfMissing = true;
|
||||
}, configure: jet =>
|
||||
{
|
||||
// 禁用自动迁移以避免冲突
|
||||
});
|
||||
|
||||
builder.UseAutoMigration(false); // 禁用自动迁移
|
||||
|
||||
var dataContext = builder.Build();
|
||||
|
||||
// 查询所有用户
|
||||
var users = await dataContext.Repository<UserInfoEntity>().GetAllAsync();
|
||||
|
||||
Console.WriteLine($"找到 {users.Count} 个用户:");
|
||||
foreach (var user in users)
|
||||
{
|
||||
Console.WriteLine($" - {user.UserName}");
|
||||
}
|
||||
|
||||
Assert.IsTrue(users.Count >= 0, "应该能够查询用户表");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task CanQueryUsersUsingDependencyInjection()
|
||||
{
|
||||
// 方式2: 使用依赖注入
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddAccessDataContext<AccessDbContext>(options =>
|
||||
{
|
||||
options.Database.DatabaseFile = DatabaseFile;
|
||||
options.Database.CopyTemplateWhenMissing = false;
|
||||
options.Database.ThrowIfMissing = true;
|
||||
});
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var dataContext = provider.GetRequiredService<IDataContext>();
|
||||
|
||||
// 查询所有用户
|
||||
var users = await dataContext.Repository<UserInfoEntity>().GetAllAsync();
|
||||
|
||||
Console.WriteLine($"\n通过 DI 找到 {users.Count} 个用户:");
|
||||
foreach (var user in users)
|
||||
{
|
||||
Console.WriteLine($" - {user.UserName}");
|
||||
}
|
||||
|
||||
Assert.IsTrue(users.Count >= 0, "应该能够通过 DI 查询用户表");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task CanQueryUserPermissions()
|
||||
{
|
||||
var builder = new DataContextBuilder<AccessDbContext>();
|
||||
builder.UseAccessDatabase(options =>
|
||||
{
|
||||
options.DatabaseFile = DatabaseFile;
|
||||
});
|
||||
|
||||
var dataContext = builder.Build();
|
||||
|
||||
// 查询所有权限
|
||||
var permissions = await dataContext.Repository<UserPermissionEntity>().GetAllAsync();
|
||||
|
||||
Console.WriteLine($"\n找到 {permissions.Count} 个权限记录:");
|
||||
foreach (var perm in permissions.Take(10)) // 只显示前 10 条
|
||||
{
|
||||
Console.WriteLine($" - 用户: {perm.UserInfo_UserName}, 权限: {perm.Permission_Code}");
|
||||
}
|
||||
|
||||
Assert.IsTrue(permissions.Count >= 0, "应该能够查询权限表");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task CanQueryWithFilters()
|
||||
{
|
||||
var builder = new DataContextBuilder<AccessDbContext>();
|
||||
builder.UseAccessDatabase(options =>
|
||||
{
|
||||
options.DatabaseFile = DatabaseFile;
|
||||
});
|
||||
|
||||
var dataContext = builder.Build();
|
||||
|
||||
// 使用 LINQ 查询
|
||||
var dbContext = dataContext.Context;
|
||||
var adminUsers = await dbContext.Set<UserInfoEntity>()
|
||||
.Where(u => u.UserName.Contains("admin") || u.UserName.Contains("Admin"))
|
||||
.ToListAsync();
|
||||
|
||||
Console.WriteLine($"\n找到 {adminUsers.Count} 个管理员用户:");
|
||||
foreach (var user in adminUsers)
|
||||
{
|
||||
Console.WriteLine($" - {user.UserName}");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task CanQueryUserWithPermissions()
|
||||
{
|
||||
var builder = new DataContextBuilder<AccessDbContext>();
|
||||
builder.UseAccessDatabase(options =>
|
||||
{
|
||||
options.DatabaseFile = DatabaseFile;
|
||||
});
|
||||
|
||||
var dataContext = builder.Build();
|
||||
var dbContext = dataContext.Context;
|
||||
|
||||
// 查询用户及其权限
|
||||
var usersWithPermissions = await dbContext.Set<UserInfoEntity>()
|
||||
.Include(u => u.Permissions)
|
||||
.Take(5) // 只取前 5 个用户
|
||||
.ToListAsync();
|
||||
|
||||
Console.WriteLine($"\n查询到 {usersWithPermissions.Count} 个用户及其权限:");
|
||||
foreach (var user in usersWithPermissions)
|
||||
{
|
||||
Console.WriteLine($" 用户: {user.UserName}");
|
||||
Console.WriteLine($" 权限数: {user.Permissions.Count}");
|
||||
foreach (var perm in user.Permissions.Take(3)) // 每个用户只显示前 3 个权限
|
||||
{
|
||||
Console.WriteLine($" - {perm.Permission_Code}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task CanUseRepositoryPattern()
|
||||
{
|
||||
var builder = new DataContextBuilder<AccessDbContext>();
|
||||
builder.UseAccessDatabase(options =>
|
||||
{
|
||||
options.DatabaseFile = DatabaseFile;
|
||||
});
|
||||
|
||||
var dataContext = builder.Build();
|
||||
|
||||
// 使用 Repository 模式
|
||||
var userRepo = dataContext.Repository<UserInfoEntity>();
|
||||
|
||||
// 获取总数
|
||||
var totalUsers = await userRepo.CountAsync();
|
||||
Console.WriteLine($"\n用户总数: {totalUsers}");
|
||||
|
||||
// 分页查询
|
||||
var pagedResult = await userRepo.GetPagedAsync(1, 10);
|
||||
Console.WriteLine($"第一页 (10条/页): {pagedResult.Items.Count} 条记录");
|
||||
|
||||
// 查询单个用户
|
||||
if (totalUsers > 0)
|
||||
{
|
||||
var firstUser = await userRepo.FirstOrDefaultAsync(u => true);
|
||||
if (firstUser != null)
|
||||
{
|
||||
Console.WriteLine($"第一个用户: {firstUser.UserName}");
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(totalUsers >= 0, "应该能够使用 Repository 模式");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Data.OleDb;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Sharp.Data;
|
||||
using Sharp.Data.Access.Extensions;
|
||||
using Sharp.Data.Context;
|
||||
|
||||
namespace Data.Access.Test;
|
||||
|
||||
/// <summary>
|
||||
/// 使用真实 Access 数据库的集成测试
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class RealDatabaseTests
|
||||
{
|
||||
private const string DatabaseFile = "dbDwDmsSetup.accdb";
|
||||
|
||||
[TestMethod]
|
||||
public void CanReadDatabaseSchema()
|
||||
{
|
||||
// 读取数据库表结构
|
||||
var connectionString = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={DatabaseFile}";
|
||||
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
// 获取所有表
|
||||
var tables = connection.GetSchema("Tables");
|
||||
|
||||
Assert.IsTrue(tables.Rows.Count > 0, "数据库应该包含表");
|
||||
|
||||
Console.WriteLine($"数据库包含 {tables.Rows.Count} 个对象:");
|
||||
foreach (System.Data.DataRow row in tables.Rows)
|
||||
{
|
||||
var tableName = row["TABLE_NAME"]?.ToString();
|
||||
var tableType = row["TABLE_TYPE"]?.ToString();
|
||||
if (tableType == "TABLE")
|
||||
{
|
||||
Console.WriteLine($" - {tableName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanListAllTables()
|
||||
{
|
||||
var connectionString = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={DatabaseFile}";
|
||||
|
||||
using var connection = new OleDbConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
var restrictions = new string?[4];
|
||||
restrictions[3] = "TABLE"; // 只获取表,不包括视图和系统表
|
||||
|
||||
var tables = connection.GetSchema("Tables", restrictions);
|
||||
|
||||
Console.WriteLine("\n=== 用户表列表 ===");
|
||||
foreach (System.Data.DataRow row in tables.Rows)
|
||||
{
|
||||
var tableName = row["TABLE_NAME"]?.ToString();
|
||||
if (!string.IsNullOrEmpty(tableName) && !tableName.StartsWith("MSys"))
|
||||
{
|
||||
Console.WriteLine($"表名: {tableName}");
|
||||
|
||||
// 获取表的列信息
|
||||
var columnRestrictions = new string?[4];
|
||||
columnRestrictions[2] = tableName;
|
||||
var columns = connection.GetSchema("Columns", columnRestrictions);
|
||||
|
||||
Console.WriteLine($" 列数: {columns.Rows.Count}");
|
||||
foreach (System.Data.DataRow colRow in columns.Rows)
|
||||
{
|
||||
var columnName = colRow["COLUMN_NAME"]?.ToString();
|
||||
var dataType = colRow["DATA_TYPE"]?.ToString();
|
||||
Console.WriteLine($" - {columnName} ({GetDataTypeName(dataType)})");
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task CanQueryDatabaseUsingDataContextBuilder()
|
||||
{
|
||||
var builder = new DataContextBuilder<TestDbContext>();
|
||||
builder.UseAccessDatabase(options =>
|
||||
{
|
||||
options.DatabaseFile = DatabaseFile;
|
||||
options.CopyTemplateWhenMissing = false;
|
||||
options.ThrowIfMissing = true;
|
||||
});
|
||||
|
||||
var dataContext = builder.Build();
|
||||
|
||||
// 尝试连接数据库
|
||||
var dbContext = dataContext.Context;
|
||||
var canConnect = await dbContext.Database.CanConnectAsync();
|
||||
|
||||
Assert.IsTrue(canConnect, "应该能够连接到数据库");
|
||||
|
||||
Console.WriteLine("成功使用 DataContextBuilder 连接到 Access 数据库!");
|
||||
}
|
||||
|
||||
private static string GetDataTypeName(string? dataType)
|
||||
{
|
||||
return dataType switch
|
||||
{
|
||||
"130" => "nvarchar",
|
||||
"3" => "int",
|
||||
"7" => "datetime",
|
||||
"11" => "bit",
|
||||
"5" => "double",
|
||||
"4" => "float",
|
||||
"2" => "smallint",
|
||||
"6" => "currency",
|
||||
"128" => "binary",
|
||||
"205" => "image",
|
||||
_ => $"unknown({dataType})"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的 DbContext
|
||||
/// </summary>
|
||||
public class TestDbContext : DbContext
|
||||
{
|
||||
public TestDbContext(DbContextOptions<TestDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user