A:增加Access数据库支持

This commit is contained in:
jimmy committed 2025-11-19 14:04:25 +08:00
1 parent e334e65c2d
commit dd993e5b45
29 files changed
+2939 -8

No files matched your search

+419
View File
@@ -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();
}
+374
View File
@@ -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,让调用者可以用更少的代码完成相同的功能!
+38 -2
View File
@@ -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; }
}
+438
View File
@@ -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. 尽情测试增删改查功能!
数据库随意修改,不用担心!🎉
+186
View File
@@ -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)**。
它是最稳定、性能最好、最简单的方案。
+2
View File
@@ -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>
+20
View File
@@ -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(() =>
+4
View File
@@ -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 模式");
}
}
+134
View File
@@ -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)
{
}
}