【问题标题】:ServiceStack calling ResolveService within a DB transactionServiceStack 在数据库事务中调用 ResolveService
【发布时间】:2015-11-19 14:40:58
【问题描述】:

我最近将我们的 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 只是 DB 事务的包装器,看起来像这样:

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 后才给我们错误。

【问题讨论】:

    标签: c# servicestack ormlite-servicestack


    【解决方案1】:

    ResolveService() 的行为没有改变,但从错误消息看来,数据库连接已被释放。这可能是由于服务已被处置后的eager disposing of transient IOC resources

    但这是一种非常奇怪的使用模式,我不知道您为什么不使用更清晰的执行路径,也不知道 ConnectionManager 的目的是什么,以及为什么它不使用推荐的 API 到 use OpenTransaction() to create the transaction 关闭数据库连接。

    但如果是因为急切地处理服务依赖项,您可以尝试在 using 范围内移动 Delete&lt;T&gt;,例如:

    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);
        }
    });
    

    【讨论】:

    • 谢谢。是的,我们已经重构了服务并将逻辑提取到一个方法中,以便我们可以传入相同的数据库连接。我只是想了解什么不能
    • 我只是想了解是什么导致了两个版本之间的行为不同。
    • @mythz 似乎 servicestack 现在注册了注入的内容并在服务处理结束时处理它们。这个对吗?这意味着如果连接管理器保留一个 DbConnection 实例,并且它会在嵌套服务被释放后被释放。
    • @Jeff 它总是跟踪短暂的 IOC 部门,它刚刚在 Service Dispose 上发布。
    【解决方案2】:

    这不是真正的答案,但我需要展示这个简单的工作示例来演示嵌套连接和嵌套服务按预期工作。

    但是,这与旧版本的服务堆栈的行为不同。 DbConnection 挂在哪里,必须手动处理。现在它是“自动的”。

    就像@mythz 提到的,最新版本的服务堆栈清除了注入的依赖项。而且我猜 ormlite 现在可以更好地与服务堆栈集成,因此底层 DbConnection 被释放(变为 null)。

    连接管理器必须在内部挂起 DbConnection,这就是为什么在嵌套服务被释放后,连接管理器内部的 DbConnection 实例也是如此。

    又花了 5 分钟思考这个连接管理器,似乎这个 ConnMan 正在尝试执行某种“分布式事务”,它试图抓住 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; }
        }
    }
    

    【讨论】:

    • 如果我使用“连接管理器”挂在内部创建的 DbConnection 上,那么在嵌套服务被释放后,下一次插入将失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    • 1970-01-01
    • 2015-01-10
    • 2010-10-01
    • 2013-11-11
    • 2023-03-15
    相关资源
    最近更新 更多