TableAdapter Connection Strings

I don’t know how a lot of people handle it, but one thing that always bugged me about table adapters and datasets is the way it handles it’s connection strings. It starts out innocent enough. A new dataset is created with it’s associated table adapter. The connection string is saved to the application settings file and that property is saved in the dataset. Running it on the development machine (or on the same network) is no big deal and just works. But what if you send that application to someone else that has their own SQL Server?

Did anyone at Microsoft actually use this scenario in a production environment? What were they thinking?

In order to run that in a production environment you have to set the connection string in the app.config file. While this may be fine for some people, what about the people like me that do not want my users to access that database? I don’t want them to have that username and password… While the chances of a normal user loading SQL Server Management Studio and logging in are slim, it’s still possible and it’s definitely possible if a user purposely wants to get out of having to do work that day.

What are the options? Well, one option is to use the encryption to encrypt the settings in app.config. For me, this option is not ideal. Reports of those settings getting corrupted are quite high, plus you have to deal with the loading and saving of those settings, which isn’t all that easy to do.

The other option is to take the route I was taking for a while… I had a function that would build me a connection string. Then I could: TableAdapter.Connection.ConnectionString = myLibrary.ConnectionStringFunction()

This was great, until that day came where I was in a hurry and added a few more tables to a form but forgot to set the ConnectionStrings. Whoops.

So I needed a solution that would stop me from having to set those ConnectionString properties, keep my connection string out of the app.config, and be easy to use (i.e. Just Works).

I started out by just giving all my datasets the same connection string. Then on application startup, I tried to change that one application setting. Hmm.. It seems those ConnectionString properties are set to friend and are read only.

Upon further investigation, it seems that there are some events that fire, such as SettingsLoaded. This event fires when the app.config is read and all the settings are loaded. When this event fires, it fires inside the MySettings class. This should allow that property to be changed.

    Private Sub MySettings_SettingsLoaded(ByVal sender As Object, ByVal e As System.Configuration.SettingsLoadedEventArgs) Handles Me.SettingsLoaded
      Me.Item("MyAppConnectionString") = MyLibrary.BuildConnectionString()
    End Sub

This will set the MyAppConnectionString setting to the proper connection string. Now, all table adapters will have an up to date connection string.

So what happens if you want to change the connection string later while the application is still running? Well, there is no way to do that. So it’s time to come up with a way to trick it into updating that property.

In looking at the MySettings class, there is another event called PropertyChanged. We can use this event if we create another setting that can be updated anywhere in the application. First, we create a new string setting that has a User scope (I called mine ConnectionString). This will allow the application to update the setting at any time.

Next, we need to create a function that will update that property with our connection string.

  Public Shared Sub ChangeConnectionString()
    My.Settings.ConnectionString = BuildConnectionString()
  End Sub

Now we can change the events in the MySettings class to look like this.

    Private Sub MySettings_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Handles Me.PropertyChanged
      If e.PropertyName = "ConnectionString" Then
        Me.Item("MyAppConnectionString") = My.Settings.ConnectionString
      End If
    End Sub

    Private Sub MySettings_SettingsLoaded(ByVal sender As Object, ByVal e As System.Configuration.SettingsLoadedEventArgs) Handles Me.SettingsLoaded
      MyLibrary.ChangeConnectionString()
    End Sub
  End Class

Now, every time that ChangeConnectionString() is called, the MyAppConnectionString will be updated. The ChangeConnectionString procedure can be changed so that it can accept a string parameter that is the actual connection string. Then you can build a Connection String anywhere and just pass it to that procedure.

XML Parsing in .Net

While working on wxDesktop, which was my first attempt at parsing data from an XML source, I tried to find the best way to read and parse out certain data from an XML document. Before I even really got to the meat of it, I had just had it in my head that I was going to use a DataSet. Why would I do that, you ask? Well, when that’s all you know how to do, that’s usually what you go with. I have never dealt with the XML namespace, mostly because I haven’t had to. Now that I have, I know I will never use the crappy DataSet class for this ever again.

Take the following XML document.

<test>
    <data>
      <name>Sample set name</name>
      <value>5</value>
      <value>4</value>
      <value>6</value>
      <value>1</value>
      <value>20</value>
      <value>54</value>
      <value>10</value>
      <value>19</value>
      <value>17</value>
      <value>15</value>
   </data>
   <otherdata>
      <name>Second Sample set name</name>
      <value>10</value>
      <value>12</value>
      <value>14</value>
      <value>2</value>
      <value>4</value>
      <value>6</value>
      <value>8</value>
      <value>16</value>
      <value>18</value>
      <value>20</value>
   </otherdata>
</test>

When you read this document in using the DataSet.ReadXML function like this:

private void readXMLDataSet(string FileName)
{
    DataSet ds = new DataSet();
    ds.ReadXml("test.xml");
}

The DataSet will contain 3 tables. It will contain the tables called data, value, and otherdata. Not really what you would think would be in there… It sort of makes sense, except for the fact that all value columns are then lumped into one table and then linked via an ID. While this isn’t too bad, and it would probably be usable, it’s not ideal, nor fun to use.

Now take this piece of code:

private void readXMLNodes(string FileName)
{
    System.Xml.XmlDocument xd = new System.Xml.XmlDocument();
    xd.Load("test.xml");
 
    System.Xml.XmlNode node = xd.SelectSingleNode("/test/data");
    System.Xml.XmlNode nameNode = node.SelectSingleNode("name");
    System.Xml.XmlNodeList valueList = node.SelectNodes("value");
}

This uses the XmlDocument, XmlNode, and XmlNodeList classes and allows you to easily parse out certain parts of the XML document to find exactly what you need. nameNode.InnerText will show “Sample set name” and the valueList will contain every value in the data section. So valueList[0].InnerText will be 5.

Now, you could shove this information into a correctly laid out DataSet, but why bother? Now I see the value in creating a class just to parse certain XML documents. You can set the class up so that it will match the structure as it should be logically laid out and use the class to just access the nodes as needed through functions.