I recently upgraded our ServiceStack package to v4.0.46 (from v4.0.36) and there are areas of our app which uses ResolveService to call another service within a DB transaction. Previously this all worked fine, but after upgrading to v4.0.46 we are getting this error:
Connection must be valid and open
The caller looks something like this:
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>();
}
}
The DeleteDocumentsService looks a bit like this
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 is just a wrapper for the DB transaction which looks something like this:
public void WriteDb(Action<IWriteCustomerDbConnection> action)
{
using (var connRef = ConnectionManager.Open()) {
using (var transRef = ConnectionManager.BeginTrans()) {
action(new CustomerDbConnection(Session, connRef.Conn));
transRef.Commit();
}
}
}
I read through the release notes for ServiceStack and couldn't find anything that would suggest there was a change in how ResolveService works. So could anyone shed any light on what could have changed?
I realise this bit of code is not the best, but it would be good to understand why it's only giving us the error now after upgrading to v4.0.46.
Behavior of ResolveService()
hasn't changed but from the error message it looks like the DB connection was disposed. Maybe this is due to the eager disposing of transient IOC resources after a Service Has been Disposed.
But this is a really strange usage pattern and I don't know why you're not using a cleaner execution path or what the purpose of ConnectionManager
is and why its not using the recommended API to use OpenTransaction() to create the transaction off the db connection.
But if it's due to eager disposing of service dependencies you can try move Delete<T>
within the using scope, e.g:
WriteDb(conn => {
using (var service = ResolveService<DeleteDocumentsService>()) {
service.Post(new DeleteDocumentsRequest {
Ids = ids.ToArray()
});
conn.Delete<Item>(request.Id);
}
});
Although if you're using a transaction I'd personally rewrite it to pass in and use the same db connection with an explicit API that accepts the connection instance:
WriteDb(conn => {
using (var service = ResolveService<DeleteDocumentsService>()) {
service.DeleteDocuments(conn, ids);
conn.Delete<Item>(request.Id);
}
});