0

I am working on Asp.Net MVC and ServiceStack. I am trying to connect to the sql server database using servicestack ormlite. like

 var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
 container.Register<IDbConnectionFactory>(
 new OrmLiteConnectionFactory(connectionString,
                    SqlServerOrmLiteDialectProvider.Instance)
                {
                    ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
                });

I am able to connect to database, But in my scenario i need to change the connection string dynamically.. That means i need to read the content from Request body and prepare a connection string. In servicestack we configure sql server connection string in AppHost class that means at app start. But i need to set the connection string in my controller. I have tried like place it in session and use that session in ClassLibrary SeviceBase class. But I am unable to use asp.Net sessions in class libraries.How to change sql server connection string dynamically in service stack. so please guide me.

Scott
  • 21,211
  • 8
  • 65
  • 72
Karthik Bammidi
  • 1,851
  • 9
  • 31
  • 65

1 Answers1

3

I would change the IDbConnectionFactory to be reused in the scope of the request, instead of the current default, which shares it among all requests. I have also created a static method (GetDatabaseConnectionFactory()) which returns the instance of OrmLiteConnectionFactory to the IoC container with the custom connection string.

To determine the connection string, I have used a request filter, which simply reads the parameter connectionstring. If it is not set it will use a default value. This value is then set in the RequestContext.Items collection, which can be accessed by the GetDatabaseConnectionFactory() method.

Remember exposing connection strings this way is dangerous, always check any connection string values thoroughly to ensure they don't contain malicious values. i.e. Ensure they don't try to connect to administrative databases, or a different server, or change default setting overrides etc.

In your AppHost:

ServiceStack V3:

public override void Configure(Container container)
{
    container.Register<IDbConnectionFactory>(c => GetDatabaseConnectionFactory()).ReusedWithin(ReuseScope.Request);

    RequestFilters.Add((req,res,obj) => {
        // Default value
        var defaultConnectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;

        // Get the connection string from the connectionstring parameter, or use default
        var dbConnectionString = req.GetParam("connectionstring") ?? defaultConnectionString;

        // You should perform some checks here to make sure the connectionstring isn't something it shouldn't be
        // ...

        // Save the connection string to the HostContext.Instance.Items collection, so we can read it later
        HostContext.Instance.Items.Add("ConnectionString", dbConnectionString);
    });
}

public static IDbConnectionFactory GetDatabaseConnectionFactory()
{
    // Read the connection string from our HostContext Items
    var dbConnectionString = HostContext.Instance.Items["ConnectionString"];

    if(dbConnectionString == null)
        throw new Exception("Connection string has not been set");

    // Return the connection factory for the given connection string
    return new OrmLiteConnectionFactory(dbConnectionString, SqlServerOrmLiteDialectProvider.Instance) {
        ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
    });
}

Usings:

using System;
using Funq;
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceHost;
using ServiceStack.WebHost.Endpoints;
using ServiceStack.OrmLite;
using ServiceStack.Common;

ServiceStack V4:

public override void Configure(Container container)
{
    container.Register<IDbConnectionFactory>(c => GetDatabaseConnectionFactory()).ReusedWithin(ReuseScope.Request);

    GlobalRequestFilters.Add((req,res,obj) => {
        // Default value
        var defaultConnectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;

        // Get the connection string from the connectionstring parameter, or use default
        var dbConnectionString = req.GetParam("connectionstring") ?? defaultConnectionString;

        // You should perform some checks here to make sure the connectionstring isn't something it shouldn't be
        // ...

        // Save the connection string to the RequestContext.Items collection, so we can read it later
        HostContext.RequestContext.Items.Add("ConnectionString", dbConnectionString);
    });
}

public static IDbConnectionFactory GetDatabaseConnectionFactory()
{
    // Read the connection string from our Items
    var dbConnectionString = HostContext.RequestContext.Items["ConnectionString"];

    if(dbConnectionString == null)
        throw new Exception("Connection string has not been set");

    // Return the connection factory for the given connection string
    return new OrmLiteConnectionFactory(dbConnectionString, SqlServerOrmLiteDialectProvider.Instance) {
        ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
    });
}

Usings:

using System;
using Funq;
using ServiceStack;
using ServiceStack.Data;
using ServiceStack.OrmLite;
using ServiceStack.OrmLite.Sqlite;
Scott
  • 21,211
  • 8
  • 65
  • 72
  • I am using ServiceStack version 3.9.43.0 dll, I am unable to compile above code, some types are not founding. I need to read request body i.e. InputStream of Request object. – Karthik Bammidi Mar 29 '14 at 10:16
  • @KarthikBammidi I wish you had mentioned that in your question somewhere, I'll try and update. What is the content-type of the data you are sending? Is it a form POST, you shouldn't need to access the InputStream directly. – Scott Mar 29 '14 at 10:25
  • An external wpf application calls my MVC application with some data like, `WebBrowser objBrowser = new WebBrowser(); objBrowser.Navigate(url, "_blank", Encoding.Default.GetBytes(queryString), "");` – Karthik Bammidi Mar 29 '14 at 10:29
  • The data that wpf application will send of type is pure text i.e. string content. – Karthik Bammidi Mar 29 '14 at 10:32
  • @KarthikBammidi So that's a [`byte[]` of post data](http://msdn.microsoft.com/en-us/library/ms161355(v=vs.110).aspx). Which is formatted as a query string, which will be read and made available as a form parameter. So there shouldn't be any need to read the `InputStream` directly. – Scott Mar 29 '14 at 10:41
  • Yes exactly, but i am unable access Request form parameters or querystring value in AppHost.cs page. I am getting Request is null and then exception. I am able to read the post data in controller action method. – Karthik Bammidi Mar 29 '14 at 10:43
  • @KarthikBammidi OK so my method should work, except that it needs to be made v3 compatible. Give me a few moments. – Scott Mar 29 '14 at 10:45
  • @KarthikBammidi I have added the code for v3. You will need to change `req.GetParam("connectionstring")` to have the name of the parameter you posting the connection string as. But you should be able to adapt as required. – Scott Mar 29 '14 at 11:33
  • I taught it is working. In my scenario that is while calling from external application, when i debug my web app, at first time it calls my controller-action and i got posted data but it did not call apphost.cs method. and then immediate call has happened to my controller-action again and i got empty posted data, this time it called apphost.cs file so it takes default connectionstring. Is there any way of using sessions in AppHost.cs please guide us. – Karthik Bammidi Mar 29 '14 at 13:40