Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional Visual Studio 2005 (2006) [eng]

.pdf
Скачиваний:
117
Добавлен:
16.08.2013
Размер:
21.9 Mб
Скачать

Chapter 9

</smtp>

</mailSettings>

</system.net>

</configuration>

Section: cryptographySettings

Although the framework contains base implementations for a number of cryptographic algorithms, such as the hashing function, there are times when it is necessary to override these algorithms. When this is required, the cryptographySettings section of the configuration file can be included to remap existing algorithm names, or map new names, to another implementation class.

Section: configurationSections

Configuration files can be customized to contain any structured XML data. In order to do this, a custom section has to be defined in the configurationSections block within the configuration file. This defines both the name of the configuration section as well as the class that is to be called in order to process the section.

The first configurationSections section in the machine.config file defines the handlers for each of the standard configuration sections discussed here. For example, the following code snippet defines the section handler for the appSettings configuration section (it is not recommended to override these section handlers as it may prevent your application from working properly):

<configuration>

<configSections>

<section name=”appSettings” type=”System.Configuration.AppSettingsSection,

System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a” restartOnExternalChanges=”false” requirePermission=”false” />

</configSections>

</configuration>

Where used, this section must appear as the first child element within the configuration section.

Section: system.diagnostics

Debugging an application is always the hardest part of writing an application. It is made even more difficult when the application is in production and the error cannot be replicated in the debugging environment. One technique that is particularly important for debugging this type of issue is to use trace statements:

trace.writeline(“The application made it this far before crashing.....

”)

Both trace and debug statements work very similarly to events and event handlers. For the preceding writeline statement to have any effect, an object must be listening for this writeline. This is typically done by a TraceListener class. The framework supports a number of default trace listeners that can be wired up to the application via the diagnostics section of the configuration file, as shown in the following section in which an EventLog trace listener has been attached to the application:

116

Application Configuration Files

<configuration>

<system.diagnostics>

<trace autoflush=”true” indentsize=”0”> <listeners>

<add name=”MyEventListener” type=”System.Diagnostics.EventLogTraceListener, system,

version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089” initializeData=”DeveloperApplicationEventLog”/>

</listeners>

</trace>

</system.diagnostics>

</configuration>

The initializeData attribute specifies a text string to be passed into the constructor for the trace listener. In the case of the event log listener, this text corresponds to the name of the event log into which trace statements will be inserted.

Other elements can also be added to the diagnostics section of the configuration file — for example, to determine the level of trace logging to perform, which will determine how verbose the trace messages are; and to control whether the debug assertion dialog is displayed for an application or not.

Section: system.web

The system.web section of the configuration file is used to control how web applications behave. This is the section that can have quite a deep hierarchy, as configuration settings can be specified on a machine, web server, web site, web application, or even a subfolder basis. Because this section controls the security requirements for a web application, it is quite often used to restrict access to certain areas of the web application.

Section: webserver

Although web service applications use several configuration settings, such as authentication and impersonation sections, the system.web section of the configuration file contains some settings that are particular to the way that web services operate. For example, the following code snippet enables the use of Soap and Documentation protocols, but removes the Post and Get protocols for the application:

<configuration>

<system.web>

<webServices>

<protocols>

<add name=”HttpSoap”/> <remove name=”HttpPost”/>

<remove name=”HttpGet”/> <add name=”Documentation”/>

</protocols>

</webServices>

</system.web>

</configuration>

117

Chapter 9

By default, only Soap and Documentation are enabled for web services. Quite often, for debugging purposes, it is convenient to allow the Post protocol so that the web service can be tested via a web browser. This should be done on an application basis by including the appropriate section in the configuration file within the application folder.

Section: compiler

The compiler section of the configuration file is used to list the compilers installed on a computer. The following snippet shows how the VB.NET compiler is referenced in the machine.config file. Within an application, this information can be accessed via the CodeDomProvider framework class.

<configuration>

<system.codedom>

<compilers>

<compiler language=”vb;vbs;visualbasic;vbscript” extension=”.vb” type=”Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089” />

</compilers>

</system.codedom>

</configuration>

Application Settings

Applications frequently have settings that do not fit into the default configuration schema. There are three mechanisms for storing this information.

Using appSettings

The first technique is to use the appSettings section of the configuration file. This section can be used to store simple name-value pairs of application settings, which might be useful for storing the name of the server, for example:

<configuration>

<appSettings>

<add key=”ApplicationServer” value=”http://DeveloperNews.com”/> </appSettings>

</configuration>

This value can easily be accessed within code using the AppSettings property of the new

ConfigurationManager class:

Dim server As String = configurationmanager.AppSettings(“ApplicationServer”)

Dynamic Properties

The second mechanism for storing application-specific information is the use of dynamic properties. These are typically used to dynamically set designer properties. For example, the text on a Button1 could be set using the following configuration block:

118

Application Configuration Files

<configuration>

<applicationSettings>

<WindowsApplication1.My.MySettings>

<setting name=”Button1_Text” serializeAs=”String”> <value>Press Me Now!</value>

</setting>

</WindowsApplication1.My.MySettings>

</applicationSettings>

</configuration>

When this application is deployed, the text displayed on Button1 is dynamically loaded from the configuration file. In the following steps, for example, we set the size of a control, Button1, to be dynamically loaded from the configuration file:

1.Select Button1 on the designer surface and press F4 to display the Properties window. Locate the Data category or the ApplicationSettings item in the alphabetic list, as shown in Figure 9-1.

Figure 9-1

2.Press the Ellipses button (...) next to the PropertyBinding row. This will open a dialog that lists the available properties for Button1, along with any ApplicationSettings that have been assigned.

3.Select the drop-down next to the Size property and select New. This will open a dialog in which you can specify a default value, a name for the application setting, and the scope of the setting.

4.Specify a name for the application setting — for example, Button1_Size, and set the scope to Application. You can modify the default value or simply accept the value that has been extracted from the current properties of Button1.

5.Click OK on both dialogs. If you open the app.config file that will be available from the Solution Explorer window, you will see a section that defines this ApplicationSetting.

Custom Configuration Sections

Developers often want to include more structured information in the configuration file than can be stored in the AppSettings section. To solve this problem and eliminate any need for additional configuration files, you can create a custom configuration section. The new configuration section must be defined at the top of the configuration file, complete with a reference to a class that should be used to process that portion of the configuration file.

119

Chapter 9

In the past this process was fairly complex, as the class needed to implement the IConfiguration SectionHandler interface. This exposed a simple method, Create, which was called the first time that section was referenced in code. There was little support from the framework to process the section, and a class implementing this interface often resorted to parsing the XML block to determine settings.

Visual Studio 2005 provides much better support for creating custom configuration sections via the ConfigurationSection and ConfigurationElement classes. These provide the basis for creating classes that map to the structure of the data being stored in the configuration files. Instead of mapping a class that processes the configuration section, we can now create a much simpler class that maps to the section. When the section is referenced in code, an instance of this class is returned with the appropriate data elements set. All the XML processing that would have been done in the past is now handled by the framework.

Although this mapping makes the process of writing a custom configuration section much easier, you may sometimes want more control over how the section is read. Two options can be used to provide this control.

The first option is to go back to using a configuration section handler and manually process the XML file. This can be useful if the original XML representation is required. However, it still requires processing the XML file.

The second strategy is to create an appropriate mapping class as an in-between measure. Instead of referencing this class directly, another class can be generated that exposes the configuration information in the appropriate manner.

If either of these options has to be pursued, then it might be worth taking a step back and determining whether the configuration section structure is actually in a format suited to the data being stored.

Of course, the best way to illustrate this is by example: Our application requires a list of registered entities with which to work. One type of entity is a company, and we need to be provided with both the company name and the date that they were registered. The XML snippet that we would like to have in the configuration file might look like the following:

<RegisteredEntities>

<Companies>

<add CompanyName=”Random Inc” RegisteredDate=”31/1/2005” />

<add CompanyName=”Developer Experience Inc” RegisteredDate=”1/8/2004” /> </Companies>

</RegisteredEntities>

Once generated, the corresponding classes that would map to the preceding snippet might look like the following:

Public Class RegisteredEntities

Inherits ConfigurationSection

<ConfigurationProperty(“Companies”)> _

Public ReadOnly Property Companies() As Companies Get

Return CType(MyBase.Item(“Companies”),Companies) End Get

End Property

120

Application Configuration Files

End Class

<ConfigurationCollectionAttribute(GetType(Company))> _ Public Class Companies

Inherits ConfigurationElementCollection

Protected Overrides Function CreateNewElement() As ConfigurationElement

Return New Company

End Function

Protected Overrides Function GetElementKey _

(ByVal element As ConfigurationElement) As Object

Return CType(element, Company).CompanyName

End Function

Public Sub Add(ByVal element As Company)

Me.BaseAdd(element)

End Sub

End Class

Public Class Company

Inherits ConfigurationElement

<ConfigurationProperty(“CompanyName”, DefaultValue:=”Random Inc”, IsKey:=true,

IsRequired:=true)> _

Public Property CompanyName() As String

Get

Return CType(MyBase.Item(“CompanyName”),String)

End Get

Set

MyBase.Item(“CompanyName”) = value

End Set

End Property

< ConfigurationProperty(“RegisteredDate”, DefaultValue:=”31/1/2005”,

IsKey:=false, IsRequired:=false)> _

Public Property RegisteredDate() As String

Get

Return CType(MyBase.Item(“RegisteredDate”),String)

End Get

Set

MyBase.Item(“RegisteredDate”) = value

End Set

End Property

End Class

The code contains three classes that are required to correctly map the functionality of this section. The registered entities section corresponds to the RegisteredEntities class, which contains a single property that returns a company collection. A collection is required here because we want to be able to support the addition of multiple companies. This functionality could be extended to clear and/or remove companies, which might be useful if we had a web application for which we needed to control which companies were available to different portions of the application. Lastly, there is the Company class that maps to the individual company information being added.

121

Chapter 9

To access this section from within code, we can simply call the appropriate section using the configurationManager framework class:

dim registered as RegisteredEntities= _

ctype(configurationmanager.GetSection(“RegisteredEntities”),RegisteredEntities)

Automation Using SCDL

You saw in the last section how custom configuration sections can be written and mapped to classes. Although this is a huge improvement over writing section handlers, it is still a fairly laborious process that is prone to error. Furthermore, debugging the configuration sections is nearly impossible because it is difficult to track what is going wrong.

As part of another project to support ASP.NET developers, Dmitryr, development manager for the ASP.NET team at Microsoft, recognized that the process of creating these mapping classes was rather a mundane task that could easily be automated. To this end he created a small application entitled SCDL (http://blogs.msdn.com/dmitryr/archive/2005/12/07/501365.aspx) that could take a snippet of configuration data, such as the RegisteredEntities section discussed previously, and output both the mapping classes and a schema file that represented the section supplied. Once generated, this code can be included in the application. Furthermore, if the snippet of configuration data is to be included as a noncompiled file within the solution, it is possible to automate the generation of the mapping classes via a prebuild batch command. If changes need to be made to the structure of the section, they could be made in the snippet. That way, the next time the solution is built, the mapping classes would be updated automatically.

IntelliSense

Even after you get the custom configuration sections correctly mapped, there is still no support provided by Visual Studio 2005 when adding the custom section to the configuration file. Unlike the rest of the configuration file, which has support for IntelliSense and will report validation issues, your custom section will not be able to be validated.

In order to get IntelliSense and validation for your custom configuration section, you need to indicate the structure of the configuration section to Visual Studio 2005. This can be done by placing an appropriate schema (as generated by the SCDL tool) in the XML Schemas folder, which is usually located at

C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas\. Unfortunately, this is where it gets a little bit more complex, as it is not enough to place the file in that folder; you also need to tell it that the schema should be included in the catalog used for parsing config files. To register your schema, follow these steps:

1.Generate your schema file from your configuration snippet:

Scdl.exe snippet.scdl snippet.vb snippet.xsd

2.Copy the schema file (in this case, snippet.xsd) to the schema folder.

3.Create a new text file called Config.xsd and include the following lines. Note that if your schema is called something different, update these lines appropriately. You may also add additional lines to include more than one schema. Do not remove the DotNetConfig.xsd line because that will remove validation for the standard configuration sections:

122

Application Configuration Files

<?xml version=”1.0” encoding=”utf-8” ?>

<xs:schema xmlns:xs=”http://www.w3.org/2001/XMLSchema”> <xs:include schemaLocation=”DotNetConfig.xsd”/> <xs:include schemaLocation=”snippet.xsd”/>

</xs:schema>

4.Open Catalog.xml in a text editor and replace DotNetConfig.xsd with Config.xsd. This effectively remaps the validation, and IntelliSense, for config files to use Config.xsd instead of DotNetConfig.xsd. However, because this file sources both DotNetConfig.xsd and your schema information, you will get validation for both your configuration section and the standard configuration sections.

Summar y

In this chapter you have learned how configuration files can be used not only to control how your application runs, but also to store settings that may need to be adjusted at runtime. You should now be able to store simple name-value information, as well as more structured information, within the configuration file.

Several security features of the .NET Framework can be tailored from the configuration file. The diagnostic section of the configuration file is covered in more detail in Chapter 37, in the discussion of event logs.

123

XML Resource Files

Developers quite often overlook the humble XML resource file, as it is often hidden by the IDE so as not to clutter the solution. Because their most common use is as a backing file for forms or web pages, it is possible to write large applications without interacting directly with resource files.

However, resource files are an important tool that developers need to be able to use in order to write applications that can be easily maintained and translated into other languages. The first part of this chapter explains why resource files are important and describes the new IDE features that enable developers to work with them. The remainder of the chapter explains how resource files are used to localize an application for different languages and cultures.

Resourcing Your Application

Writing an application often requires data such as images, icons, or sounds (collectively known as resources) to enhance the appearance of the application. Furthermore, best coding practices suggest that the use of constant strings throughout your application should be avoided. In any of these cases we could put together a custom solution that stores these resources in files that need to be shipped with application.

An alternative is to include these resources in a resource file that can be compiled into your application. This way, you not only have the resources in a format that you can work with, they are automatically available within your application.

In Visual Studio 2005, forms are initially represented by two files: the generated designer file (for example, form1.designer.vb) and the code-beside file (for example, form1.vb). When a control, such as a button, is first added to the form, a resource file (for example, form1.resx) is automatically created for the form. By default, this resource file contains very little data, as most properties are hard-coded into the designer file. This file becomes very important when localization is turned on for the form. When this is done, via the properties grid shown in Figure 10-1, the designer properties for the controls on the form are persisted to the resource file.