I'm using the Db
property in a ServiceStack service to access my database but every now and then I get the following error from IIS:
Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
Stack Trace:
[InvalidOperationException: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.]
System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) +6371713
System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) +6372046
System.Data.SqlClient.SqlConnection.Open() +300
ServiceStack.OrmLite.OrmLiteConnection.Open() +44
ServiceStack.OrmLite.OrmLiteConnectionFactory.OpenDbConnection() +132
ServiceStack.ServiceInterface.Service.get_Db() +68
I have set the ReuseScope
in the Configure
method to ReuseScope.None
which should close the connection on a per request basis I believe? What am I doing wrong here?
public override void Configure(Container container)
{
JsConfig.EmitCamelCaseNames = true;
//Register all your dependencies
ConfigureDb(container);
//Set MVC to use the same Funq IOC as ServiceStack
ControllerBuilder.Current.SetControllerFactory(new FunqControllerFactory(container));
}
ConfigureDb:
private static void ConfigureDb(Container container)
{
var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider))
.ReusedWithin(ReuseScope.None);
using (var db = container.Resolve<IDbConnectionFactory>().Open())
{
// Do database seed/initialisation
}
}
Edit
After more diagnosis, it seems to happen when I refresh the page several times when I call this service method:
public Warranty Get(Warranty request)
{
var warranty = new Warranty();
if (request.Id != default(int))
{
warranty = Db.Id<Warranty>(request.Id);
warranty.WarrantyOrder = ResolveService<WarrantyOrderService>().Get(new WarrantyOrder { WarrantyId = warranty.Id });
warranty.WarrantyStatus = ResolveService<WarrantyStatusService>().Get(new WarrantyStatus { Id = warranty.StatusId });
warranty.WarrantyNotes = ResolveService<WarrantyNoteService>().Get(new WarrantyNotes { WarrantyId = warranty.Id });
warranty.WarrantyDialogues = ResolveService<WarrantyDialogueService>().Get(new WarrantyDialogues { WarrantyId = warranty.Id });
warranty.WarrantyCredit = ResolveService<WarrantyCreditService>().Get(new WarrantyCredit { WarrantyId = warranty.Id });
warranty.WarrantyPhotos = ResolveService<WarrantyPhotoService>().Get(new WarrantyPhotos { WarrantyReference = warranty.WarrantyReference });
warranty.WarrantyReport = ResolveService<WarrantyReportService>().Get(new WarrantyReport { WarrantyId = warranty.Id });
}
return warranty;
}
I have changed ConfigureDb
as per @mythz answer below:
private static void ConfigureDb(Container container)
{
var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider));
}
The service needs to call out other services to populate the other objects on my Warranty
object, I'm not sure on how to improve this?
The IDbConnectionFactory
like all connection managers is a thread-safe factory to create DB Connections, i.e. it's not a DB connection itself. It is supposed to be registered as a singleton.
By default ServiceStack's IOC (Funq) registers as a Singleton by default, so the correct registration is just:
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider));
Resolving Services
As ServiceStack Services have the potential to utilize managed resources, they should be used within a using statement whenever they're resolved from another service, so that they're appropriately disposed of, e.g:
using (var orders = ResolveService<WarrantyOrderService>())
using (var status = ResolveService<WarrantyStatusService>())
{
var warranty = new Warranty {
WarrantyOrder = orders.Get(new WarrantyOrder { WarrantyId = warranty.Id }),
WarrantyStatus = status.Get(new WarrantyStatus {
WarrantyId = warranty.StatusId }),
//etc
}
return warranty;
}
来源:https://stackoverflow.com/questions/19927765/timeout-expired-using-db-in-servicestack-service