A:Access数据库功能
This commit is contained in:
1 parent
f7660c49e8
commit
1d412bba33
37 files changed
+990
-23
No files matched your search
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Sharp.Data.Access;
|
||||
using Sharp.Data.Access.DependencyInjection;
|
||||
using Sharp.Data.Access.Options;
|
||||
using Sharp.Data.Access.Security;
|
||||
using Sharp.Data.Access.Connections;
|
||||
|
||||
namespace Data.Access.Test;
|
||||
|
||||
[TestClass]
|
||||
public sealed class AccessConnectionStringFactoryTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Create_UsesDecryptedPasswordAndAdditionalParameters()
|
||||
{
|
||||
var tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(tempDirectory);
|
||||
|
||||
var options = new AccessDatabaseOptions
|
||||
{
|
||||
DatabaseFile = "data/app.accdb",
|
||||
DataDirectory = tempDirectory,
|
||||
EncryptedPassword = "encrypted-secret",
|
||||
ProviderName = AccessProviderNames.AceOleDb12
|
||||
};
|
||||
options.AdditionalParameters["Jet OLEDB:Database Locking Mode"] = "1";
|
||||
|
||||
var decryptor = new FakeDecryptor("plain-text");
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IAccessPasswordDecryptor>(decryptor);
|
||||
services.AddAccessDataContext<FakeContext>(ctx =>
|
||||
{
|
||||
ctx.Database.ConnectionString = $"Provider={AccessProviderNames.AceOleDb16};Data Source=fake;";
|
||||
}, registerDataContext: false);
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
var factory = provider.GetRequiredService<IAccessConnectionStringFactory>();
|
||||
|
||||
var connectionString = factory.Create(options, provider);
|
||||
var expectedPath = Path.GetFullPath(Path.Combine(tempDirectory, "data", "app.accdb"));
|
||||
|
||||
StringAssert.Contains(connectionString, $"Provider={AccessProviderNames.AceOleDb12}", "Provider should be set.");
|
||||
StringAssert.Contains(connectionString, expectedPath, "Database path should be resolved to absolute path.");
|
||||
StringAssert.Contains(connectionString, "Jet OLEDB:Database Password=plain-text", "Decrypted password should be used.");
|
||||
StringAssert.Contains(connectionString, "Jet OLEDB:Database Locking Mode=1", "Additional parameters should be appended.");
|
||||
Assert.AreEqual("encrypted-secret", decryptor.LastValue, "Decryptor should receive encrypted value.");
|
||||
}
|
||||
|
||||
private sealed class FakeDecryptor : IAccessPasswordDecryptor
|
||||
{
|
||||
private readonly string? _result;
|
||||
|
||||
public FakeDecryptor(string? result) => _result = result;
|
||||
|
||||
public string? LastValue { get; private set; }
|
||||
|
||||
public string? Decrypt(string? encryptedPassword)
|
||||
{
|
||||
LastValue = encryptedPassword;
|
||||
return _result;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class FakeContext : Microsoft.EntityFrameworkCore.DbContext
|
||||
{
|
||||
public FakeContext(Microsoft.EntityFrameworkCore.DbContextOptions<FakeContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Sharp.Data.Access;
|
||||
using Sharp.Data.Access.DependencyInjection;
|
||||
using Sharp.Data.Access.Initialization;
|
||||
using Sharp.Data.Access.Options;
|
||||
|
||||
namespace Data.Access.Test;
|
||||
|
||||
[TestClass]
|
||||
public sealed class AccessDatabaseInitializerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void EnsureDatabase_CopiesTemplateWhenMissing()
|
||||
{
|
||||
var tempRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(tempRoot);
|
||||
|
||||
var templateDir = Path.Combine(tempRoot, "template");
|
||||
Directory.CreateDirectory(templateDir);
|
||||
|
||||
var templatePath = Path.Combine(templateDir, "seed.accdb");
|
||||
File.WriteAllText(templatePath, "seed-data");
|
||||
|
||||
var options = new AccessDatabaseOptions
|
||||
{
|
||||
DatabaseFile = "db/app.accdb",
|
||||
TemplateFile = "template/seed.accdb",
|
||||
DataDirectory = tempRoot,
|
||||
CopyTemplateWhenMissing = true,
|
||||
ThrowIfMissing = true
|
||||
};
|
||||
|
||||
var services = BuildServices();
|
||||
var initializer = services.GetRequiredService<IAccessDatabaseInitializer>();
|
||||
initializer.EnsureDatabase(options, services);
|
||||
|
||||
var targetPath = Path.Combine(tempRoot, "db", "app.accdb");
|
||||
Assert.IsTrue(File.Exists(targetPath), "Database file should be created from template.");
|
||||
Assert.AreEqual("seed-data", File.ReadAllText(targetPath));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EnsureDatabase_ThrowsWhenMissingAndNoTemplate()
|
||||
{
|
||||
var tempRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(tempRoot);
|
||||
|
||||
var options = new AccessDatabaseOptions
|
||||
{
|
||||
DatabaseFile = "missing/app.accdb",
|
||||
DataDirectory = tempRoot,
|
||||
CopyTemplateWhenMissing = false,
|
||||
ThrowIfMissing = true
|
||||
};
|
||||
|
||||
var services = BuildServices();
|
||||
var initializer = services.GetRequiredService<IAccessDatabaseInitializer>();
|
||||
Assert.ThrowsException<FileNotFoundException>(() =>
|
||||
initializer.EnsureDatabase(options, services));
|
||||
}
|
||||
|
||||
private static ServiceProvider BuildServices()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddAccessDataContext<FakeContext>(ctx =>
|
||||
{
|
||||
ctx.Database.ConnectionString = $"Provider={AccessProviderNames.AceOleDb16};Data Source=fake;";
|
||||
}, registerDataContext: false);
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
private sealed class FakeContext : Microsoft.EntityFrameworkCore.DbContext
|
||||
{
|
||||
public FakeContext(Microsoft.EntityFrameworkCore.DbContextOptions<FakeContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Sharp.Data;
|
||||
using Sharp.Data.Access;
|
||||
using Sharp.Data.Access.DependencyInjection;
|
||||
using Sharp.Data.Access.Options;
|
||||
|
||||
namespace Data.Access.Test;
|
||||
|
||||
[TestClass]
|
||||
public sealed class AccessServiceCollectionExtensionsTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void AddAccessDataContext_RegistersFactoryAndOptions()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddAccessDataContext<FakeContext>(options =>
|
||||
{
|
||||
options.Database.ConnectionString = $"Provider={AccessProviderNames.AceOleDb16};Data Source=fake;";
|
||||
options.ConfigureDataContext = (sp, builder) =>
|
||||
{
|
||||
builder.UseAudit(false)
|
||||
.UseSoftDelete(false)
|
||||
.UseAutoMigration(false)
|
||||
.EnableDetailedErrors(false);
|
||||
};
|
||||
}, registerDataContext: false);
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
|
||||
var options = provider.GetRequiredService<AccessContextOptions<FakeContext>>();
|
||||
Assert.AreEqual($"Provider={AccessProviderNames.AceOleDb16};Data Source=fake;", options.Database.ConnectionString);
|
||||
|
||||
var decryptor = provider.GetRequiredService<Sharp.Data.Access.Security.IAccessPasswordDecryptor>();
|
||||
Assert.IsInstanceOfType(decryptor, typeof(Sharp.Data.Access.Security.PassthroughAccessPasswordDecryptor));
|
||||
|
||||
var factory = provider.GetRequiredService<IAccessDataContextFactory<FakeContext>>();
|
||||
Assert.IsNotNull(factory);
|
||||
}
|
||||
|
||||
private sealed class FakeContext : DbContext
|
||||
{
|
||||
public FakeContext(DbContextOptions<FakeContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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.Usage;
|
||||
|
||||
[TestClass]
|
||||
public sealed class AccessUsageSampleTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 演示如何在无 DI 场景下使用 DataContextBuilder 配置 Access。
|
||||
/// 这里只验证 API 能够被调用(不真正连接数据库)。
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void ManualBuilder_AllowsAccessConfigurationWithoutExceptions()
|
||||
{
|
||||
var builder = new DataContextBuilder<FakeContext>();
|
||||
|
||||
builder.UseAccessDatabase(options =>
|
||||
{
|
||||
options.ConnectionString = $"Provider=Microsoft.ACE.OLEDB.16.0;Data Source=fake.accdb;";
|
||||
options.CopyTemplateWhenMissing = false;
|
||||
options.ThrowIfMissing = false;
|
||||
});
|
||||
|
||||
// 不调用 Build(),避免实际访问文件;只要不抛出异常即为通过。
|
||||
Assert.IsTrue(true);
|
||||
}
|
||||
|
||||
private sealed class FakeContext : DbContext
|
||||
{
|
||||
public FakeContext(DbContextOptions<FakeContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in new issue
Block a user