【发布时间】:2021-06-07 15:51:20
【问题描述】:
我正在开发一个 blazor 服务器端项目。
当我尝试为我的存储库模式服务编写集成测试时,我得到了错误:
System.PlatformNotSupportedException : This platform does not support distributed transactions.。
尝试使用第二个连接并查询是否真正保存了所需的数据会触发错误。
我的测试代码:
namespace CVUnitTests.IntegrationTests.Services
{
public class SkilServiceTest
{
[TestFixture]
public class YourFixture
{
private TransactionScope _scope;
private ISkillService _skillService;
private IConfigurationRoot _config;
[SetUp]
public void SetUp()
{
_config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
_skillService = new SkillService(_config);
_scope = new TransactionScope();
}
[TearDown]
public void TearDown()
{
_scope.Dispose();
}
[Test]
public async Task UpdateSkill_NewSkillIsSaved()
{
var skillToUpdate = new Skill()
{
Name = "TestSkill",
Type = SkillType.Hardskill,
Category = "TestCategory"
};
await _skillService.UpdateSkill(skillToUpdate);
using IDbConnection connection = new SqlConnection(DatabaseUtils.GetConnectionString(_config));
var sql = "SELECT * FROM skill WHERE Name = TestSkill";
var queriedSkill = connection.QuerySingle<Skill>(sql);
Assert.AreEqual(queriedSkill, skillToUpdate);
}
}
}
}
我发现Query 可用于实现此目的,但我无法将两个查询包装成一个使用,因为该服务正在打开自己的连接:
public Task<bool> UpdateSkill(Skill skill)
{
if (GetSkill(skill.Id) != null)
{
using IDbConnection dbConnection = new SqlConnection(_connectionString);
var parameters = new
{
Id = skill.Id,
skill.Name,
Skilltype = skill.Type == SkillType.Hardskill ? "TRUE" : "FALSE"
};
var sql = "update skill SET Name = @Name, Skilltype = @Skilltype where id = @Id";
dbConnection.Open();
var result = dbConnection.ExecuteAsync(sql, parameters).Result;
dbConnection.Close();
return Task.FromResult(true);
}
else
{
using IDbConnection dbConnection = new SqlConnection(_connectionString);
var parameters = new {skill.Name, Skilltype = skill.Type == SkillType.Hardskill ? 1 : 0};
var sql = "insert into skill (Name, Skilltype) values (@Name, @Skilltype)";
dbConnection.Open();
var result = dbConnection.ExecuteAsync(sql, parameters).Result;
dbConnection.Close();
return Task.FromResult(false);
}
即使我只在事务范围内运行一个查询,我也会收到错误 This platform does not support distributed transactions.。
我还尝试从我的服务中读出dbConnection,而不是创建一个新的,但也没有成功。
我实际上可以在我的服务函数中使用多个,然后返回技能,尽管这似乎是在我的方法中添加测试代码。
有没有办法我仍然可以实现我想要的测试行为,还是我必须完全改变我的测试逻辑?
【问题讨论】:
-
用
using(connection.BeginTransaction())代替TransactionScope不要忘记所有DB对象都应该在using块中,而不是缓存在字段中 -
我尝试在更新答案时添加
connection.BeginTransaction(),但我仍然遇到同样的错误。我是否也必须以某种方式在方法中使用它? -
你不能只改变一个方法来解决这个问题。
UpdateSkill使用自己的连接。使其受外部事务控制的唯一方法是使用分布式事务。你必须重新考虑整个Repository类。包括你为什么首先添加它。在可扩展性方面最大的进步之一是 ADO.NET 和 ORM(如 Entity Framework)支持的断开连接的操作。所有更改都被缓存,并且在代码明确要求之前没有任何内容被持久化。如果您想回滚,请不要保存更改。 -
为了从使用 Dapper 中获得任何性能优势,应该避免使用像
GetSkill这样的方法。使用 Dapper 的全部意义在于 避免 读取对象只是为了更新它,但这正是GetSkill所做的。更糟糕的是,您必须使用事务来确保结果一致。您可以编写单个“UPSERT”SQL 批处理,方法是使用MERGE命令或UPDATE和INSERT以及只允许其中一个完成的WHERE子句。在这两种情况下,您都必须在查询中使用事务(MERGE 不是原子的)但该事务将是短暂的。 -
@FelixOuttaSpace
repository用于抽象存储,而不是使测试更容易。不过这是一个好处。像 EF Core 这样的 ORM 提供了相同的好处,甚至更多。普通的 ADO.NET 也是如此。 DataTable 对将保存到的数据库一无所知。但是,一旦您开始使用事务,您必须考虑连接和事务生命周期。
标签: nunit integration-testing repository-pattern dapper blazor-server-side