Does SQL Server CLR Integration support configuration files? - c#

I use SQL Server CLR Integration to create an ASSEMBLY.
Load Command:
CREATE ASSEMBLY TcpClr FROM 'G:\TcpClrTest.dll' WITH PERMISSION_SET = UNSAFE
Without App.Config
The dll code contains :
string ip=ConfigurationManager.AppSettings["connection"].ToString();
Also the App.config contains :
<appSettings>
<add key="connection" value="127.0.0.1"/>
</appSettings>
But when I execute the PROCEDURE,the SQL Server shows an error System.NullReferenceException
Does SQL Server CLR Integration support App.config files?

You need to place a sqlservr.exe.config file in the \Binn folder of that instance's root folder. For example:
C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Binn
If you are using SQL Server 2008 R2 (SP1) or newer, you should be able to find the exact location via the following query, which shows the full path to sqlservr.exe:
SELECT [filename] FROM sys.dm_server_services WHERE servicename LIKE N'SQL Server (%';
In your code, you need this line at the top:
using System.Configuration;
And then this will work:
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true)]
public static SqlString GetConfig(SqlString WhichOne)
{
ConfigurationManager.RefreshSection("connectionStrings");
return ConfigurationManager.ConnectionStrings[WhichOne.Value].ToString();
}
Contents of the sqlservr.exe.config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="Stuff" connectionString="Trusted_Connection=true; Enlist=false;" />
<add name="ClrTest" connectionString="boo hoo" />
</connectionStrings>
</configuration>
It is important to note that, as stated in the "Using an Application Configuration..." link, changes made to the config file are not immediately available. HOWEVER, you do not need to force a reload by doing one of the methods mentioned in that article (i.e. DBCC FREESYSTEMCACHE, and restarting SQL Server). All that is required to get current information is to reload the particular section you are using via ConfigurationManager.RefreshSection(string sectionName) as shown in the example above. Please see note below regarding usage and performance.
Resources:
Using System.Configuration.dll in .NET sprocs and UDFs
Using an Application Configuration (app.config/web.config) File in SQL Server CLR Integration
Also, unless you absolutely need to, you shouldn't create your assembly as UNSAFE. If you are just trying to make TCP connections to other machines, that should only require EXTERNAL_ACCESS.
USAGE AND PERFORMANCE
As suggested by Joe B in a comment below, there is a slight performance hit for the RefreshSection operation. If the code containing the refresh is going to be called more than once every couple of minutes, then it can have a noticeable impact (an impact that is unnecessary given the lack of frequency of a config file changing). In this case, you will want to remove the call to RefreshSection from the code that is called frequently and handle the refresh independently.
One approach would be to have a SQLCLR Stored Procedure or Scalar Function that just does the refresh and nothing else. This can be executed whenever a change it made to the config file.
Another approach would be to unload the App Domain which will reload the config file upon the next time any SQLCLR object in that database is referenced. One fairly simple method to reload all App Domains in a particular Database (but not across the entire Instance) is to flip the TRUSTWORTHY setting On and then Off again, or Off and then On again, depending on the current state of that setting. The code below will check the current state of that setting and flip it accordingly:
IF (EXISTS(
SELECT sd.*
FROM sys.databases sd
WHERE sd.[name] = DB_NAME() -- or N'name'
AND sd.[is_trustworthy_on] = 0
))
BEGIN
PRINT 'Enabling then disabling TRUSTWORTHY...';
ALTER DATABASE CURRENT SET TRUSTWORTHY ON;
ALTER DATABASE CURRENT SET TRUSTWORTHY OFF;
END;
ELSE
BEGIN
PRINT 'Disabling then enabling TRUSTWORTHY...';
ALTER DATABASE CURRENT SET TRUSTWORTHY OFF;
ALTER DATABASE CURRENT SET TRUSTWORTHY ON;
END;
Please do not use any of the more drastic methods -- DBCC FREESYSTEMCACHE, disabling then enabling the clr enabled system setting, restarting the Instance, etc -- as it is almost never necessary to do so. Especially restarting the Instance, or DBCC FREESYSTEMCACHE which drops all cached data for the entire Instance, which affects much more than just SQLCLR.
UPDATE REGARDING SQL SERVER ON LINUX
SQL Server is now, starting with version 2017, available on Linux (woo hoo!). However, it seems that reading from the app config file does not work on Linux. I have tried many combinations of sqlservr.exe.[Cc]onfig and sqlservr.[Cc]onfig, etc, and the like and have not gotten anything to work. Specifying a config file can't work as that requires EXTERNAL_ACCESS permission and only SAFE Assemblies are allowed on Linux (as of right now, at least). If I find a way to get it working I will post the details here.

You'll obviously be getting a NullReferenceException because ConfigurationManager.AppSettings["connection"] is returning null and then you are calling ToString() on a null.
Until you posted your question the top ranking page for a Google search on CLR Integration app.config was this
Using an Application Configuration (app.config/web.config) File in SQL Server CLR Integration.
In the associated article by Jonathan Kehayias, he states
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.
He then goes on to detail a workaround which involves editing the project file to inject a new key into an ItemGroup that links your app.config to the project. From there you can do some basica wrangling from within your code to create a UserDefinedFunction to return the connection string or appSetting you require.
You are best to read the article posted by him at the aforementioned Url as it's not my area and I would just end up blatantly plagiarizing his content to provide a step by step of how to do.

Are you trying to access the same SQL Server that the CLR is running on? If so, you can use a special type of connection string called a "context connection".
https://msdn.microsoft.com/en-us/library/ms131053.aspx
using(SqlConnection connection = new SqlConnection("context connection=true"))
{
connection.Open();
// Use the connection
}
Basically you tell the code that it should use the underlying SQL Server (in that context) and you won't even need to specify connection info. If you're trying to run arbitrary reusable code as a CLR, you're probably committing some sort of software crime and should just not do that.

This is how I did it. It's working perfectly for me, and I can install my assembly on any instance now and it will work perfectly.
SqlConnectionStringBuilder sqlb = new SqlConnectionStringBuilder("");
sqlb.ContextConnection = true;
string newConnStr = sqlb.ToString();
using (SqlConnection c = new SqlConnection(newConnStr))
{
c.Open();
//This is the database name
DatabaseName = c.Database;
//We need to use some simple SQL to get the server and instance name.
//Returned in the format of SERVERNAME\INSTANCENAME
SqlCommand cmd = new SqlCommand("SELECT ##SERVERNAME [Server]", c);
SqlDataReader rdr = cmd.ExecuteReader();
if (rdr.Read())
{
ServerName = rdr["Server"].ToString();
}
}

Related

How to create dynamic database connection string C#

I just created a desktop Winforms application with localhost database.
The connect string I am using is this:
SqlConnection connect = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\Administrator\Desktop\learningsystem\LearningSystem\LearningSystem\LearningSystem.mdf;Integrated Security=True");
If I want to run my application on other computers, how should I make it work?
EDIT:SOLUTION
Thank for all the help! I tried the following steps. I think it is working now. But please correct me if I did something tricky.
1. add a new setting item in project property setting. App.config will automatically update:
<connectionStrings>
<add name="LearningSystem.Properties.Settings.LearningConn" connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\LearningSystem.mdf;Integrated Security=True;Connect Timeout=30"
providerName="System.Data.SqlClient" />
</connectionStrings>
2. In my program, just add the following statement to connect to the sql server
SqlConnection connect = new SqlConnection(#"Data Source = (LocalDB)\MSSQLLocalDB; AttachDbFilename=|DataDirectory|\LearningSystem.mdf;Integrated Security = True; Connect Timeout = 30");
Further question
If others will run this application on their computer(not in the same network), they just go into the project setting and change the value by selecting the database file I provide to them,the connectionString will automatically change, right?
Thanks!
It's generally a bad idea to hard code such stuff in your application. Normally, application settings and connection strings are placed in the application's configuration file (in the ConnectionStrings section).
Just like with all strings, you could build your connectionstring from dynamic parts (variables, settings, etc.) and then pass that generated connectionstring to the SqlConnection constructor. Again, to make those separate parts configurable without hard coding them in your application, you might want to add them to your application's configuration file (in the AppSettings section). But IMHO this is an overly complex solution in most scenarios. Putting the entire connectionstring in the ConnectionStrings section is more straightforward (and more flexible).
Anyway, again, to make your application configurable, you might use your application's configuration file (App.config or Web.config), you need to add a reference to System.Configuration in your project's .NET Framework dependencies and use the AppSettings and ConnectionStrings properties of the System.Configuration.ConfigurationManager class.
(Of course, there are more ways to make your application configurable. But using the application configuration file is one of the most straightforward solutions.)
Edit:
When deploying your app to another computer, you need to copy its database over too. If you want to use the application on multiple machines and let them connect to the same database, you might want to leave LocalDB and migrate the data to a SQL Server (Express) instance and make it accessible over the (local) network.
Edit 2 (regarding the recent edits in your post):
I see in step 1 that you are using an application setting (called LearningConn) in your solution now. That's fine. However, it is important that you also use that setting in step 2, like this:
SqlConnection connect = new SqlConnection(Properties.Settings.Default.LearningConn);
If you change the setting in Visual Studio, it will update the connection string. Since the setting will probably have application scope, it will not be possible to update the setting/connection string within your application in runtime (by the user).
I'm not sure if your connection string using |DataDirectory| will always work as expected in all scenarios. I have only been using it in ASP.NET webapplications. If it does work in WinForms applications, you might read this document to learn how to set it up. But personally I am somewhat sceptical about this approach.
I personally would opt for a solution where you use a placeholder in your connection string, which you replace with the full path to the .mdf file before you pass it to your SqlConnection constructor.
When you use "{DBFILE}" as the placeholder, for example, the value of your LearningConn setting would look like this:
Data
Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename={DBFILE};Integrated
Security=True;Connect Timeout=30
(Note that this value should be a single line without any line breaks!)
You might create a separate setting in your application called DbFile (of type string) to store the actual value that should be put in place of {DBFILE} in your connection string. When you use scope "user" for that setting, the value might be changed from within the application by the user. When saved, it might not be saved directly in the application's configuration file, however, but in an additional configuration file hidden somewhere in the user's Windows user profile. You might read this document to learn more about application settings.
Your code in step 2 might eventually look something like this:
string connectString = Properties.Settings.Default.LearningConn;
string dbFile = Properties.Settings.Default.LearningSystemDb;
connectString = connectString.Replace("{DBFILE}", dbFile);
SqlConnection connect = new SqlConnection(connectString);
To let your application's users select and store the database .mdf file to use, you might include (a variation of) the following code in your application somewhere:
using (var dlg = new System.Windows.Forms.OpenFileDialog())
{
dlg.Title = "Select database file to use";
dlg.Filter = "Database Files (*.mdf)|*.mdf";
dlg.CheckFileExists = true;
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Properties.Settings.Default.DbFile = dlg.FileName;
Properties.Settings.Default.Save();
}
}
Your question is not clear!
you need work with one Database on 2 or more PC?!
OR
you need work with 2 separate programs?
if you need 2 separate programs :
you must copy .mdf file to other PC at same address or keep mdf address in app.config and read it before connect to SQL.
How to Read From app.config
if you need work with one Db you must connect to dataBase Server such as SQL Server and keep connection string in app.config in connectionStrings tag.
Get connection string from App.config
If you want to work on other PCs, rather than building it dynamically make the connection string more generic:
Server=(localdb)\\mssqllocaldb;Database=LearningSystem;Trusted_Connection=True;MultipleActiveResultSets=true
This should create the mdf file under 'mssqllocaldb' in %appdata% for each user. You might need LocalDb installed (which you tick during SQL Server installation)

Do you have to publish DefaultConnection containing the membership tables?

Attempting to publish a website to my host which contains my custom database and the default like so:
<add name="PortfolioContext" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Portfolio;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/>
<add name="DefaultConnection" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=TestDB;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/>
When publishing the site I can't login which I assume is down to because the DefaultConnection db isn't being created (containing the membership user tables), I might be wrong? I can generate the script for my custom database in Management studio but DefaultConnection doesn't even appear to allow me to manually create tables associated.
My error log displays this
2016-02-02 12:28:35 W3SVC30 217.194.213.135 GET /Login
ReturnUrl=%2Fdashboard 80 - 95.147.124.217
Mozilla/5.0+(Windows+NT+10.0;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/48.0.2564.97+Safari/537.36
- 500 0 0 1198 416 203
Though I'm not sure what that means. Sorry this is my first attempt at publishing a site. Thanks
Edit:
Could not find a part of the path 'C:\HostingSpaces\hannahha\hannahhamlin.co.uk\wwwroot\Assets\Common\images'.
This is the error it displays. This leads to me a problem I knew i was going to have to confront anyway. How do I include necessary directories for publishing?
First, that's not an error log. That's IIS' default request log. All it tells you is that a client using a particular browser requested /Login via GET. In other words, nothing at all useful to this issue.
You need a real error, with a stacktrace. You can either turn off custom errors temporarily (<customErrors mode="Off" /> in your Web.config), install something like Elmah to log errors for you, or use remote debugging.
However, most likely, the connection string only works locally, and the error you would see is just a DB connection error. It's rare that the connection string you'd be using in development would match exactly with what would be required for production, so you should utilize config transforms to change it when publishing.
That's assuming of course you even have a database server set up in production to connect to. Visual Studio employs a lot of database magic to allow you to focus on your code rather than menial implementation details like connecting to a database. However, when you go live, the training wheels are off, and you're responsible for making sure everything necessary is wired up. If you don't have a database server, install one. If you don't have a database, create one. Then, just alter your connection string to connect to that.
UPDATE
The error message is so far removed from this question was originally about, I think you would be best served by creating a new question and deleting this one.
That said, I'm not sure what you're trying to achieve exactly, but anything under your project directory will be published along with your site (except certain special directories like App_Data). If the files you're referencing are in some directory outside of your project directory, they will not be included, and there's really not a way to include them. You can, however, create a build action that copies the files somewhere under your project, and by virtue of that, they should then be published.

C# Windows form application - Save configuration during installation / Use external config

Before I begin, I've done good bit of research and googling. I think I didn't find the perfect answer because I'm probably a noob :)
However, I've created a POS software using Windows C# form application and it's working perfectly fine. Now, this application is using MySQL as database server.
I've been hard-coding the database server credentials on app.config file.
Code for the app.config file :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="cString" connectionString="Server=localhost;Database=adittoenterprise;port=3306;Uid=root;Pwd=root123" providerName="MySql.Data.MySqlClient"/>
</connectionStrings>
</configuration>
Now I'm trying to create a package for other users to use. Definitely I need to come up with solution where the user can provide
host
username
password
During the installation and the application should store this somewhere (that is where my knowledge is lacking) and use it on the run time.
Any alternative solution is most welcome.
Thanks everyone in advance for bearing with me and helping me :)
Your installer needs to modify the app.config file. There are many different types of installers available and some of the more fully-featured packages can modify XML files such as app.config during installation.
It's also possible to modify the connection string at runtime (though since it's an application-scope setting, not a user-scope setting, that's probably not a good idea) as long as you do it before any database objects attempt to use it:
Properties.Settings.Default["cString"] = "new connection string";
... so you could potentially distribute your app.config with a blank connection string and prompt the user to provide those values the first time the application is run. You'd have to save the new value into a user-scope setting, though, in order for Save() to work:
Properties.Settings.Default.CustomConnectionString = "new connection string";
Properties.Settings.Default.Save();
(Note that I am a computer science student and still learning)
EDIT the links are not useful for your case as they only work with IIS, but still I would not save passwords in plaintext to a file such as the other answer suggested.
It should be considered bad practice to save the password especially plain text. Usually if an application starts it would require the user to authenticate to get access to the database. Of course you can store the password, but doing that properly you should look at http://msdn.microsoft.com/en-us/library/hh8x3tas.aspx and http://msdn.microsoft.com/en-us/library/89211k9b%28v=vs.80%29.aspx
Then if your application start for the first time your application can create/edit the config file and store the values. Note that your application cannot change it's own app.config at runtime so it would make sense to create a web.config for you database connection.

Odbc connection string format, not finding files

This is kind of a 'double' question that might have a single answer.
I'm working with an Odbc Connection with an AS/400, with my connection string as follows:
driver={iSeries Access ODBC Driver}; system={0}; uid={1}; pwd={2}; DefaultLibraries=*USRLIBL;
I'm able to connect to the system fine.
*USRLIBL contains all the necessary libraries from the user (which is of the type 'API only' which has access to all user libraries).
However, when I try to access certain ERP libraries, it says they can't be found, while other ones can.
So as an extremely basic walkthrough:
1. Open Connection - Query File 1 from Library A: OK! - Close Connection
2. Open Connection - Query File 2 from Library A: OK! - Close Connection
3. Open Connection - Query File 1 from Library B: Exception SQL0204 - in UserName type *FILE not found
Ok, so I added in the specific library that the ERP files would be in, making the connection string as follows, just to test the program:
driver={iSeries Access ODBC Driver}; system={0}; uid={1}; pwd={2}; DefaultLibraries=*USRLIBL, LibraryB;
But then I start getting a different problem (another extremely basic walkthrough)
1. Open Connection - Query File 1 from Library A: OK! - Close Connection
2. Open Connection - Query File 2 from Library A: OK! - Close Connection
3. Open Connection - Query File 1 from Library B: OK! - Close Connection
4. Open Connection - Query File 1 from Library A again: Exception SQL0202 - in LibraryB type *FILE not found.
So my question(s) are:
Why doesn't the odbc connectionstring DefaultLibraries=*USRLIBL not return the correct libraries? (Note: I also tested this using an iDB2Connection which in fact works fine... however, the iDB2Connection can not be deployed as it literally crashes the server)
Why does the second walkthrough throw an exception, it just seems to 'skip past' *USRLIBL after reading from LibraryB even once.
Any thoughts?
Begin Edit:
There are actually two users, DEV and PROD
The *USRLIBL gets all the necessary Libraries from the Environment itself, so if when opening the connection, it detects a localhost environment, or anything that's unsecure (plus a few other caveats), it defaults to DEV log in credentials before creating the connection. This is why the system, uid, and pwd are designated as parameters in the connection (and not just stackoverflow I-dont-want-to-give-out-data placeholders)
The *USRLIBL then pulls the necessary libraries from the API user.
To Clarify, the way it's set up does work using the iDB2 Connector, but because of the limitations of our ERP system (we think), using it with an IIS 7 server causes a catastrophic failure, so we're working with the ODBC connector.
End Edit:
You can qualify your table names as library.filename and not have to deal with any library list issues.
For more information:
Client Access ODBC: Default Libraries Setting
ODBC connection string keywords
Excerpts of the relevant parts are:
With SQL naming convention, the operating system does not perform a library list search to locate an unqualified object. If a default collection is defined, the default collection is used to resolve unqualified SQL statements.
...
With the SYS naming convention, the unqualified SQL statements go to the default collection. If there is no default collection, the current library is used. If no current library is specified, the library list is used.
...
Default Collection
A job attribute set by ODBC that determines the library used when processing SQL statements that contain unqualified SQL names. When a default collection is set all unqualified objects except procedures, functions and types must reside in the default collection, regardless of naming convention.
...
How can I get ODBC to search the library list?
As explained above, edit the ODBC data source and set system naming to SYS. The default library must be empty, or on versions older than R510, the default libraries setting must start with a comma so that no default collection is defined (for example, ",MYLIB1, MYLIB2").
Try this connection string to enable system naming and to not set a default library:
driver={iSeries Access ODBC Driver}; system={0}; uid={1}; pwd={2}; naming=1; DefaultLibraries=,*USRLIBL,LibraryB;
If anyone runs into this post and is using the IBM.Data.DB2.iSeries .NET data provider as I am, the key point taken from above was using the naming=1 and not specifying a "Default Collection". I was finally successful when using the following portion in my connection string
LibraryList= MyLibrary1,MyLibrary2,MyLibrary3,MyLibrary4;naming=1;
An alternative is to set up a separate user profile for each environment. Since the *USRLIBL is set by the job description, this would entail setting up a separate job description as well. For example:
user: WEB job desc: WEB library list: CUSTPROD, ITEMPROD, UTILITY
user: WEBTEST job desc: WEBTEST library list: CUSTTEST, ITEMTEST, UTILITY
The C# code does not change except for using the test or production user ID to authenticate.

Local database, I need some examples

I'm making an app that will be installed and run on multiple computers, my target is to make an empty local database file that is installed with the app and when user uses the app his database to be filled with the data from the app .
can you provide me with the following examples :
what do I need to do so my app can connect to its local database
how to execute a query with variables from the app for example how would you add to the database the following thing
String abc = "ABC";
String BBB = "Something longer than abc";
and etc
Edit ::
I am using a "local database" created from " add > new item > Local database" so how would i connect to that ? Sorry for the dumb question .. i have never used databases in .net
Depending on the needs you could also consider Sql CE. I'm sure that if you specified the database you're thinking of using, or your requirements if you're usure you would get proper and real examples of connection strings etc.
Edit: Here's code for SqlCe / Sql Compact
public void ConnectListAndSaveSQLCompactExample()
{
// Create a connection to the file datafile.sdf in the program folder
string dbfile = new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\datafile.sdf";
SqlCeConnection connection = new SqlCeConnection("datasource=" + dbfile);
// Read all rows from the table test_table into a dataset (note, the adapter automatically opens the connection)
SqlCeDataAdapter adapter = new SqlCeDataAdapter("select * from test_table", connection);
DataSet data = new DataSet();
adapter.Fill(data);
// Add a row to the test_table (assume that table consists of a text column)
data.Tables[0].Rows.Add(new object[] { "New row added by code" });
// Save data back to the databasefile
adapter.Update(data);
// Close
connection.Close();
}
Remember to add a reference to System.Data.SqlServerCe
I'm not seeing anybody suggesting SQL Compact; it's similar to SQLite in that it doesn't require installation and tailors to the low-end database. It grew out of SQL Mobile and as such has a small footprint and limited feature-set, but if you're familiar with Microsoft's SQL offerings it should have some familiarity.
SQL Express is another option, but be aware that it requires a standalone installation and is a bit beefier than you might need for an applciation's local cache. That said it's also quite a bit more powerful than SQL Compact or SQLite.
Seems like you're:
-Making a C# app that will be installed and run on multiple
computers
-That needs a local database (I'm assuming an RDBMS)
-You need to generate a blank database at installation
-You then need to be able to connect to the database and populate it when
the app runs.
In general, it seems that you need to read up on using a small database engine for applications. I'd start by checking out SQLite, especially if you need multi-OS capability (eg., your C# program will run on Microsoft's .NET Framework and Novell's Mono). There are C# wrappers for accessing the SQLite database.
I believe this question is about the "Local Database" item template in Visual Studio:
What are you considering as a database? From what little you've provided in your question, I'd suggest SQLite.
You can get sample code from their site Sqlite.NET
Not sure I fully understand what you're asking but Sqlite is a good option for lightweight, locally deployed database persistence. Have a look here:
http://www.sqlite.org/
and here for an ADO.NET provider..
http://sqlite.phxsoftware.com/
For 1)
The easiest way to provide this functionality is through SQL Server Express User Instances. SQL Server Express is free, so your user does not have to pay additional license for SQL Server, and the User Instance is file-based, which suits your requirement.
For 2)
This is a big topic. You may want to go through some of the tutorials from Microsoft to get the feeling of how to connect to DB, etc.

Categories

Resources