|
A common part of programming in .NET is to use an configuration file to store configuration information in an easily modifiable location. The app.config or web.config file is an invaluable inclusion in most .NET projects and developers may need to maintain this functionality as a part of logic sharing between objects in the database and the application as well. The problem is that SQL CLR doesn't allow use of the System.Configuration class in SQLCLR projects.
However, you can get around this little problem fairly easily. First create your SQLCLR project in the language of choice, and save it. Once you have save the project, close the project in Visual Studio. Next open the .csproj or .vbproj file as appropriate in your project in Notepad, and add the either one of the following as appropriate to your project type:
C# - <Reference Include="System.Configuration" />
VB.NET = <Import Include="System.Configuration" />
You will find an <ItemGroup> section in the file that already has subkeys if <Reference Include> or <Import Include> in it, you simply add the new key for System.Configuration under the existing keys. Once you have completed this, you can reload the project and you will see the System.Configuration assembly listed in the SQLCLR Project References.
From here, you can create a CLR UDF as well as a configuration file that holds a key to be used by the configuration file. To create the configuration file, you first need to know the root path to the SQL Server Instance. To find this, you can view the Server Properties dialog, by right clicking on the Server in the Object Browser, and selecting the Properties, or by running the following TSQL Code:
declare @SQLRoot varchar(1024)
exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
N'SOFTWARE\Microsoft\MSSQLServer\Setup', N'SQLPath', @SQLRoot OUTPUT
select @SQLRoot
You must save the config file as sqlservr.exe.config in the \Binn folder under the root path of the SQL Instance. An example configuration file would be as follows:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<appSettings>
</appSettings>
<connectionStrings>
<add name="DemoString"
connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=SQLCLR_NET;User ID=SQLCLRUser;password=SQLCLRPassword;" />
</connectionStrings>
</configuration>
For the purposes of demonstration, the following function will return the DemoString connectionString from the configuration file.
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Configuration;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString fn_GetConfigurationConnectionString()
{
return new SqlString(ConfigurationManager.ConnectionStrings["DemoString"].ToString());
}
};
In order to deploy changes to the configuration file, it is necessary to force a reload of the assembly manually. There are two ways to do this. The first is to restart the SQL Instance. The second is the free the system memory caches which will unload the CLR memory clerk.
DBCC FREESYSTEMCACHE('ALL')
This is very unlike their usage in normal .NET applications where changes in the configuration file take place immediately.
A question that may be asked is why would one want to do this in the first place? A few scenarios that come to mind are:
- The ability to share an assembly between SQL Server and an application which uses configuration information.
- Storing connection strings for connections to remote servers that require specific usernames and passwords to authenticate.
- Storing Impersonation Usernames and Passwords to other external resources to include file paths to allow for explicit impersonation of credentials.
Arguably, all of this could be stored in a SQL Database table. However, to do so has certain expense related with this when compared to including a private static readonly implementation of the data contained inside the Configuration file. For heavily used systems, there would be a measurable savings from not having to issue a select statement against the context connection to get basic configuration information for each execution.
Hopefully you find this information helpful.
|