我最近将ServiceStack软件包升级到v4.0.46(从v4.0.36开始),我们的应用程序中有一些区域使用ResolveService来调用数据库事务中的其他服务。以前这一切都运行良好,但升级到v4.0.46后,我们收到此错误:
连接必须有效并且打开
来电者看起来像这样:
public class DeleteItemService: CustomerServiceBase
{
public object Post(DeleteItem request)
{
WriteDb(conn => {
using (var service = ResolveService<DeleteDocumentsService>()) {
service.Post(new DeleteDocumentsRequest {
Ids = ids.ToArray()
});
}
conn.Delete<Item>(request.Id);
});
return RespondSuccess<ResponseBase>();
}
}
DeleteDocumentsService看起来有点像这样
public class DeleteDocumentsService: CustomerServiceBase
{
public ILog Log { get; set; }
public PrivateStorage PMStorage { get; set; }
public ResponseBase Post(DeleteDocumentsRequest request)
{
WriteDb(conn => {
var link = conn.Select<DocumentLink>(l => l.DocumentStorageId == item.Id).FirstOrDefault();
conn.Delete<DocumentStorage>(item.Id);
});
return RespondSuccess<ResponseBase>();
}
WriteDb只是数据库事务的包装器,如下所示:
public void WriteDb(Action<IWriteCustomerDbConnection> action)
{
using (var connRef = ConnectionManager.Open()) {
using (var transRef = ConnectionManager.BeginTrans()) {
action(new CustomerDbConnection(Session, connRef.Conn));
transRef.Commit();
}
}
}
我阅读了ServiceStack的发行说明,找不到任何表明ResolveService工作方式发生变化的内容。那么,任何人都可以对可能发生的变化有所了解吗?
我意识到这段代码不是最好的,但是理解为什么它在升级到v4.0.46之后现在只给我们错误。
答案 0 :(得分:5)
ResolveService()
的行为没有改变,但是从错误消息看起来,数据库连接已被处理掉了。也许这是由于服务已经处置后的eager disposing of transient IOC resources。
但这是一个非常奇怪的使用模式,我不知道为什么你没有使用更清晰的执行路径或ConnectionManager
的目的是什么以及为什么它没有使用推荐的API来{{3关闭数据库连接。
但如果由于急切处理服务依赖性,您可以尝试在使用范围内移动Delete<T>
,例如:
WriteDb(conn => {
using (var service = ResolveService<DeleteDocumentsService>()) {
service.Post(new DeleteDocumentsRequest {
Ids = ids.ToArray()
});
conn.Delete<Item>(request.Id);
}
});
虽然如果您正在使用某个事务,我会亲自重写它以传入并使用与接受连接实例的显式API相同的数据库连接:
WriteDb(conn => {
using (var service = ResolveService<DeleteDocumentsService>()) {
service.DeleteDocuments(conn, ids);
conn.Delete<Item>(request.Id);
}
});
答案 1 :(得分:2)
这不是真正的答案,但我需要展示这个简单的工作示例来演示嵌套连接和嵌套服务按预期工作。
然而,这与旧版本的服务堆栈的行为不同。 DbConnection悬挂在哪里,必须手动处理。现在它自动&#34;。
与@mythz提到的一样,最新版本的服务堆栈清除了注入的依赖项。而且我想ormlite现在可以更好地与服务堆栈集成,因此底层的DbConnection会被处理掉(变为空)。
连接管理器必须在内部挂起DbConnection,这就是为什么在嵌套服务被释放后,连接管理器中的DbConnection实例也是如此。
花了5分钟考虑这个连接管理器,似乎这个ConnMan试图进行某种&#34;分布式事务&#34; ,它试图抓住1 dbconn的实例并尝试将其泄漏到多个服务中,从而提供单个事务。正如@mythz提到的那样狡猾!它的反模式!不要这样做!
using System;
using System.Data;
using ServiceStack;
using ServiceStack.Data;
using ServiceStack.OrmLite;
namespace NestedTransactionTest
{
[Route("/test/ResolveViaIoC", Verbs = "GET")]
public class Dto1 : IReturnVoid
{
}
public class ResolveViaIoC : Service
{
readonly IDbConnectionFactory _factory;
public ResolveViaIoC(IDbConnectionFactory factory)
{
_factory = factory;
}
public void Get(Dto1 request)
{
using (var conn = _factory.Open()) {
using (var tran = conn.BeginTransaction()) {
var kv = new KeyValue {
Id = Guid.NewGuid().ToString(),
TypeName = "ResolveViaIoC",
Value = "empty",
ExpireAfter = DateTime.Now
};
using (var nested = ResolveService<ResolveViaIoCNested>()) {
nested.Get(new Dto1Nested());
}
conn.Insert(kv);
tran.Commit();
}
}
}
}
[Route("/test/ResolveViaIoC/nested", Verbs = "GET")]
public class Dto1Nested : IReturnVoid
{
}
public class ResolveViaIoCNested : Service
{
readonly IDbConnectionFactory _factory;
public ResolveViaIoCNested(IDbConnectionFactory factory)
{
_factory = factory;
}
public void Get(Dto1Nested request)
{
using (var conn = _factory.Open()) {
using (var tran = conn.BeginTransaction()) {
var kv = new KeyValue {
Id = Guid.NewGuid().ToString(),
TypeName = "ResolveViaIoCNested",
Value = "empty",
ExpireAfter = DateTime.Now
};
conn.Insert(kv);
tran.Commit();
}
}
}
}
public class KeyValue
{
public string Id { get; set; }
public string TypeName { get; set; }
public string Value { get; set; }
public DateTime ExpireAfter { get; set; }
}
}