Okay, I'm finally ready to start talking about the code needed to open a connection. A big part of this discussion involves protecting the logon credentials and other ConnectionString details you use to gain access to the server. Microsoft seems very concerned with this aspect of security, but I'm not so frightened. That's because you must trust your developers and those that can review your codeif you can't, you're in trouble. In any respect, your DBA can protect your data and servers by setting up appropriate and specifically restricted Login accounts that can't do anything but what the application is intended to do. Visual Studio also has several new features that let you move your ConnectionString to the XML configuration file and encrypt it, if necessary. On the other hand, Peter has a good point:
What Is a ConnectionString?Okay, let's stop a second. I noticed there are a few newbies out there who might need a few terms defined before I go on. First, if you've worked with ADO classic before (or ADO.NET), you know that in order to open a connection, you need to point to a specific server instance and that SQL Server needs to know who you are (or who you're pretending to be). ADO.NET uses a ConnectionString to pass the answers to these questions to SQL Server. It's a bit early to talk about the specific contents of the ConnectionString, but I'll get to that later in this chapter. The ConnectionString is easy to code, but if you make a mistake coding it, your code can crash at runtimenot good. Consider using a class like the SqlConnectionStringBuilder to build the ConnectionString at design time. I'll show you how next.
Generally, your ConnectionString should not change during the life of your application. Each time it does, ADO.NET creates a new connection pool to manage it and all other connections with the same ConnectionStringthis can be very expensive. However, creating a different ConnectionString can help performance if you need a separate connection pool for some operations. Building a ConnectionString with the SqlConnectionStringBuilder ClassIf you need to create a ConnectionString programmatically, you can use the SqlConnectionStringBuilder class. It's certainly a better choice than concatenating key/value pair strings together and hoping the resulting string is a valid ConnectionString. That's because as you address the SqlConnectionStringBuilder object's strongly typed properties (or their synonyms), the class validates the entry on-the-flywell, for the most part. For example, (as shown in Figure 9.13), you can build up a valid ConnectionString to access SQL Server by selecting from a list of valid ConnectionString arguments. Figure 9.13. Choose valid ConnectionString arguments from the SqlConnectionStringBuilder properties.
I'm not using "late binding" to address the properties of the SqlConnectionStringBuilder object in the example shown in Figure 9.13. Although the documentation suggests this approach, it's problematic, as it delays resolution of the selected property until runtime and makes it easy to specify invalid keywords. When you work with other .NET Data Providers (like System.Data.Odbc), you'll discover that several of the more important OdbcConnectionStringBuilder properties are not listed as properties. This might be an oversight or an intentional omission. In any case, you'll need to refer to these ConnectionString keywords using late binding. I illustrate that a bit later when discussing how to connect to flat files. Tip Yes, there are "ConnectionStringBuilder" classes for each ADO.NET provider. Each has its own properties that map to the provider-specific ConnectionString they build. Figure 9.14 shows how to populate the SqlConnectionStringBuilder with property settings. As you enter each property, Visual Studio's Intellisense makes sure you choose from valid entries (at least, for most settings). Unlike other providers, SqlClient does not permit any "extraneous" entries in the ConnectionString, so be careful as you type late-bound entries that refer to keywords using Item("String of keyword") referencing. Figure 9.14. Populating the SqlConnectionStringBuilder object with values.Protecting the ConnectionStringWhen you include sensitive credential information in a ConnectionString, it's important to protect those credentials from prying eyes. I'm of the opinion that it makes more sense to use credentials that permit access to just those parts of the database that the application is programmed to seeand no more. Sure, there are applications that need access to more sensitive information, and those will need to be granted access to those areas. In virtually all cases, it's important to protect the credentials. Fortunately, there are several strategies implemented in Visual Studio to help. First, you can set the BrowsableConnectionString property of the SqlConnectionStringBuilder object to false. This prevents the Visual Studio IDE from making the ConnectionString visible. Persisting the ConnectionStringVisual Studio makes it easier than ever to save application or user "settings", which is a useful place to store a ConnectionString or any other value used to configure an application that you used to store in .INI files or the Windows Registry. These name/value pairs are stored behind the scenes in XML files, but fortunately, you won't have to manipulate these files directly. In a Windows Forms application, settings are placed in the app.config file. ASP.NET (and ASP Web Service) applications use the web.config file. There are two basic types of "settings":
It's easy to address either type of settingbut don't bother trying to set application settings in code. Using the My.Settings.Save() method seems to work, but the values are not changed. For example, to fetch a saved ConnectionString from the app.config settings, you can use strongly typed referencing that exposes each of the settings as My.Settings propertiesthis means they'll show up in the Intellisense list of valid properties, as shown in Figure 9.16. Figure 9.16. Getting and setting application and user settings in code using My.Settings.Building a Connection ObjectI'm now ready to build an ADO.NET Connection object. For those of you new to .NET, remember that I'll use class New "constructors", which are a special class function whose job it is to initialize or "construct" an instance of a class. When the class is built using Visual Basic .NET or C#, you can include one or more "New" functions that help developers set up the instances of the class in the Visual Studio IDE. Data is passed to the New constructor as arguments, and these are used however the New function sees fit, but generally to set properties of the new class instance. The SqlConnection object has two constructors, one that takes no arguments and simply returns an uninitialized SqlConnection object, and a second that accepts a properly constructed ConnectionString. That's the trickbuilding a string that has all of the required name/value pairs associated with a ConnectionString correctly spelled and with correctly scaled values. That is, if you provide a value for a named ConnectionString argument, it must conform to one of the valid keyword names, along with the correct datatype and range of permissible values for that keyword. That's where the table of acceptable values comes inunless you've used the SqlConnectionStringBuilder class to construct the ConnectionString (as shown in Figure 9.14). Table 9.1 lists the valid names and aliases for keyword values within the ConnectionString. I've organized the keywords by function. Note that acceptable keywords must be spelled as shown (including the spaces) but are not case-sensitive. Do not confuse the SqlConnectionStringBuilder properties from the keywords they setthe properties are shown in (parenthesis).
Before I move on, I need to pause a moment and more fully discuss a few of these keywords, as they're pretty important to your connection strategy. First, ADO.NET has tightened up their parser, so leading or trailing spaces and case are now ignored. This means "Server = Fred; Database = George" is the same as "Server=fred; Database=GeorGe". I don't recommend using values that contain a semicolon (;), but if that's necessary, you can frame the value in double quotes. Next, most Boolean values can be set to a number of alternative values. For example, true = yes and false=no in most cases. I suggest sticking with true or false. The only important exception is where Microsoft encourages us to set the Integrated Security keyword to "SSPI". While in parts of Texas and in the White House some versions of ADO.NET still accept "yup" in place of "yes", sadly, Microsoft dropped support of "you betcha" as a value setting some time ago. Using AttachDBFilenameThis keyword appeared in earlier versions of ADO.NET, but it's gained a great deal of flexibility with the innovations associated with (just the) SQL Server Express edition. Used alone, AttachDBFilename points to a SQL Server .MDF database file that's ready[11] to be attached to the master database. The AttachDBFilename value is set to the name of the primary database file, including the full path name of an attachable database. The path may be absolute or relative by using the DataDirectory substitution string. If DataDirectory is used, the database file must exist within a subdirectory of the directory pointed to by the substitution string. The DataDirectory is set by the application installer and is visible via My.Application.Deployment.DataDirectorybut not during debug. No, you can't put the .MDF on a remote server or provide an HTTP or UNC pathname to the .MDF file.
When you use the AttachDBFilename keyword, ADO.NET opens the connection and checks to see if the named target database (as determined by the Database or Initial Catalog keyword value settings) has already been attached. If it's there, ADO.NET is done and you're granted access to the database (assuming you have permission). However, if the database has not been previously attached, ADO.NET permanently attaches the file to the target SQL Server instance. In this case, the "Database" or "Initial Catalog" keywords must name the database being attached. If it does not match, ADO.NET throws an exception and falls to the floor kicking and screaming. Using AttachDBFilename with SQLExpress InstancesIn the 2.0 Framework, ADO.NET dramatically expands the AttachDBFilename functionality. If you use AttachDBFilename, the keyword "User Instance" is set to true, and you're connecting to a SQL Server Express instance; the MDF database file and the SQL Server Express system database files (master, model, and tempdb) are copied to the user disk area under "Documents and Settings". Subsequently, this new private instance of SQL Server is started and a new process is spun up as the user who opened the connection and the MDF file is attached to the copy of master. Simple? Well, this approach means the user (based on the current Windows credentials) can have full use of the user database as well as the entire set of system databases without fear of impacting the "root" system database or any other user databases on the system. Because the user has full access to a User Instance .MDF, it's possible to "safely" open the database connection as "SA" with full rightsbut since the user has full rights, there is no need to enable mixed-mode security. This AttachDBFilename approach was implemented because Microsoft wanted users to be able to run applications as usersand not have to have elevated administrator accounts simply because they were attaching a database deployed with their application. Sure, the same rules apply to the application itself as with other applicationsthey'll still have to be installed by the administrator. Does this make sense for ASP.NET applications? Hardly. Remember, all ASP.NET applications run under the same process account by default; all of these applications would attach their local databases to the same shared instance of SQL Express. This also means the assurance that your data is safe because you're the only one using it is no longer possible. And no, you can't create a User Instance using any other version of SQL Serverit's a SQL Server Express feature. Setting the Server Name KeywordOne of the challenges you'll face if you're unfamiliar with SQL Server instance addressing is how to code the "Server" ("Data Source" or "Address") ConnectionString keyword value. Consider that there are a number of ways to point to the SQL Server you wantjust remember to include the instance name to address. Yes, you can install SQL Server as the "default" instance, which is how preSQL Server 2000 installations were installed (there was only one instance). As discussed in Chapter 2, SQL Server 2000 introduced the concept of multiple instances, so when you install SQL Server 2000 on a system with an existing SQL Server 7.0, the 7.0 instance became the "default" instance (so existing code would not break). While it's possible to install SQL Server 2000 or 2005 as the default instance, it's not a particularly good ideaI recommend that you give each instance a unique name. Peter is a bit more flexible in this regard. He suggests that there are situations where installing default instances make sense. I'm not fully convinced, but he has more customers than I do. To help visualize the addressing requirements, take a look at Figure 9.17. Here, I show two servers: machine "George" with a single "default" instance of SQL Server installed, and machine "Fred" with two SQL Server instances installed: "Test" and "Production". All of these machines are visible on the "Acme" domain. When creating a ConnectionString that must address machines across domains, include the domain name in the address you use in the "Server" keyword value. Figure 9.17. Addressing servers and instances using the Data Source ConnectionString keyword.Let's review some sample Server/Data Source keyword value settings:
Yes, you can also use the other aliases for the "Server" keyword (there are several), but I recommend use of the "Data Source" keyword and "DataSource" SqlConnectionStringBuilder property. Establishing an Alias to Your InstanceAll of the example applications included on the book's DVD reference the target SQL Server via an Alias of "DemoServer". An Alias permits you to address any named instance of SQL Server in the ConnectionString by using another namethus, the term "Alias". Creating an alias is very simplejust follow along:
Revisiting Other KeywordsLet's take a closer look at some of the ConnectionString keywords to get a better understanding of how they interact with each other and tune how ADO.NET manages the Connection.
Configuring the Connection Pool Keyword ValuesThe next set of keywords, as shown in Table 9.2, is used to set up and manage the connection pool. Note that there is only one change in these arguments since ADO.NET 1.1: the "Connection Lifetime" keyword has a new alias, "Load Balance Timeout" (that Microsoft wants developers to use), that does the same thingwhich is nothing for ordinary (non-clustered) connection pools. These settings have corresponding properties in the SqlConnectionStringBuilder, as shown in (parenthesis).
Don't place unvalidated strings into the ConnectionString (or any SQL), as this can lead to SQL injection attacks. |