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

@@ -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)
{
}
}