A:增加Access数据库支持
This commit is contained in:
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 模式");
|
||||
}
|
||||
}
|
||||
@@ -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