Enterprise Templates: Building an Application Construction Kit in Visual Studio .NET 2003

 

Steven Powell
Visual Studio Team
Microsoft Corporation

February 2002, revised April 2003

Summary: This paper explains one approach for using Enterprise Templates with other Visual Studio .NET features to create an "application construction kit." The accompanying sample demonstrates how to provide your development team with an initial project structure that includes appropriate architecture for a specific type of application. (32 printed pages)

Download Sample

Contents

Introduction
Sample Installation
Initial Application Structure
Creating the Humongous Insurance Project Wizard Initial Application
Database Project Wizard
Creating the Custom Wizard Script
Custom Folders in the New Project and Add New Project Dialog Boxes
Custom Project Items and Folder
Security Framework
Custom Guidance
Custom Policy
Using Custom Code Attributes for TDL Identification
Conclusion

Introduction

While Visual Studio .NET provides generic templates for distributed applications, the real strength of the Enterprise Template technology is the ability to create customized templates. This paper explains how to use Enterprise Templates and other Visual Studio .NET features to create an "application construction kit."

Application Construction Kits

An application construction kit is a starting point for building a particular type of application. In the traditional Visual Studio development process, you start with an empty solution and add projects in various programming languages until the solution contains all the components it needs; however, this is not the most intuitive process for large, complex applications.

Instead, if you provide a template based on the type of application being developed (Web storefront, business to business, issue tracking, data warehouse, and so on), your development team can start with an appropriate architecture as part of the initial project structure. Consequently, a development team can work more efficiently when it starts with a well-planned architecture that an application construction kit can provide. And since application construction kits are based on Visual Studio Enterprise Templates, a development team can also take advantage of the other features of Enterprise Templates, policy and custom Help.

Example Project: Humongous Insurance Company

While the example described in this paper is based on Enterprise Templates, it also takes advantage of other Visual Studio .NET features. This example shows you how to create a moderately complex initial application (with some optional content) that you can later use to build your own custom application construction kit. The sample is based on a multi-layered distributed application for the fictional "Humongous Insurance Company."

The example's project structure has optional user-interface (UI) and Web service layers. It lets you specify the names and programming languages of its projects and it includes a security framework that adds authorization and authentication capabilities.

The architecture and implementation described here are not intended as implementation best practices or reference architectures. Instead, they illustrate how to use your own architecture and implementation requirements to create an application construction kit using Enterprise Templates and Visual Studio IDE extensibility.

This paper discusses the questions you need to ask when creating your own application construction kit and relates them to the decisions reached for the Humongous Insurance Company example.

The tools used in the example include:

  • Visual C++ Custom Wizard project
  • Custom HTML and JScript
  • Enterprise Template projects
  • Custom code attributes for code identification and policy
  • Custom Add Project dialog box folders
  • Custom guidance
  • Template-based classes
  • Enterprise Templates policy
  • Existing security framework component
Tip   Read and work through the Enterprise Template walkthrough topics in the Visual Studio .NET documentation to become familiar with Enterprise Templates before attempting a complex project such as the one described in this article. For more information, see Enterprise Templates for Distributed Applications.

Roadmap for Creating an Application Construction Kit

To create an application construction kit, you need to perform specific design and development tasks. This white paper explains how to perform those tasks, illustrating them with a sample application called the Humongous Insurance Application. The following sections describe these tasks in the order recommended for completing them.

  1. Sample Installation. Download and install the Humongous Insurance Application sample application.
  2. Initial Application Structure. Choose an approach for generating the application structure that you want your application construction kit to provide. This section describes the three main approaches and explains why this white paper uses the most complex one, that of a custom project wizard.
  3. Creating the Humongous Insurance Project Wizard. Developers can use this wizard to instantiate a project based on the Humongous Insurance application. You make it available to them by adding it to the New Project and Add New Project dialog boxes of Visual Studio after you create it. The wizard-creation steps include the following tasks:
    1. Starting a Wizard Project. This procedure walks you through the creation of the custom wizard project. You use a custom wizard project to code the wizard that can later be used to instantiate application projects.
    2. Database Project Wizard. You can add subprojects of various kinds to an Enterprise Template project. Some kinds of subprojects support policy and others do not. This section describes how to add a non-policied subproject — in this case, a database project — to your Enterprise Template project.
    3. Creating the Custom Wizard Script. You can copy the script of an existing wizard included in Visual Studio to use as a starting point for a new custom wizard. This section describes how to do that and to begin editing the default.js file, which contains the essential wizard functions.
    4. Custom Folders in the New Project and Add New Project Dialog Boxes. This section describes how to add a folder for the Humongous Insurance sample to the New Project and Add New Project dialog boxes.
    5. Custom Project Items and Folder. This section provides information about adding custom project items to the Humongous Insurance application project. 
  4. Security Framework. When instantiating a solution from the Humongous Insurance application template, you can include an optional security framework project.
  5. Custom Guidance. You can add custom guidance (Help topics) that will become available to developers working on a solution instantiated from the Enterprise Template of this Application Construction Kit. This section summarizes the way to add custom guidance and provides links to more detailed information.
  6. Custom Policy. This section uses the example of the Humongous Insurance application to describe how to customize a general policy file (DAP.tdl) so that it applies the specific policy required by a specific application.
  7. Using Custom Code Attributes for TDL Identification. You can attach a custom code attribute to a class and then use that custom attribute to refer to the class in your project's policy file. This section shows how to define a custom attribute, associate it with a class, and then use that attribute to refer to the class in a policy file. The purpose is to apply policy explicitly to that class; for example, to exclude the class from use within a certain project.

Sample Installation

Install the sample files for the Humongous Insurance Application Construction Kit so that you can browse its parts while you study the details of the kit and examine the process used to create it.

To install the Humongous Insurance Application Construction Kit sample files

  1. Navigate to the folder C:\Public. (Create this folder if necessary.)
  2. Create a folder under C:\Public and name the new folder ACKDemo.
  3. To download the sample files, click the link at the top of this article.
    Note   This link is called either "Download Sample" or "Load Sample Solution."
  4. You may be asked to read and accept a license agreement. If so, the sample files will be downloaded only if you click Yes on the license agreement Web Page dialog box.
  5. At the prompt, download the sample files to the C:\Public\ACKDemo folder.
    Note   If you did not install Visual Studio .NET 2003 to the default location, you need to change settings in the installation script file InstallACK.cmd, as described in the ReadMe.txt file that is downloaded along with the sample.
  6. If the file ApplicationConstructionKit.exe is present in the C:\Public\ACKDemo folder, double-click the ApplicationConstructionKit.exe file to extract its contents. (If this file is not present, go to the next step.) In the WinZip Self-Extractor - ApplicationConstructionKit.exe dialog box, click Unzip, and then click OK in the dialog box that states the number of files that unzipped successfully. Finally, click Close in the WinZip Self-Extractor - ApplicationConstructionKit.exe dialog box.
  7. Open the Readme.txt file in the C:\Public\ACKDemo\ApplicationConstructionKit folder and follow its instructions for installing the Humongous Insurance Application sample.
  8. Start Visual Studio .NET 2003. (If Visual Studio is already running, close and restart it.)

Now you can explore the sample solution. In the Visual Studio New Project dialog box, notice the Framework Project Wizard in the Humongous Insurance Application Templates custom folder. If you want to view or edit the wizard's implementation, open the HIFrameworkProjectWizard.sln file located in the EnterpriseFrameworks\EFWizards subdirectory under the directory into which you installed Visual Studio .NET 2003.

Instantiating the Humongous Insurance Sample Application

The following steps describe how to instantiate a project from the Humongous Insurance template.

To instantiate the Humongous Insurance sample application

  1. Start Visual Studio.
  2. On the File menu, click New, and then click Project.
    The New Project dialog box appears.
  3. Select the Humongous Insurance Application Templates folder in the Project Types pane.
  4. In the Templates pane, select Humongous Insurance Framework Project Wizard. Optionally change the name or location of the project you are about to create. Click OK. The Humongous Insurance Framework Project Wizard - <project name> dialog box appears.
  5. The Humongous Insurance Framework Project Wizard has three pages: Required Architectural Projects, Optional Architectural Projects, and Implementation Elements & Settings. For more information on completing these pages, see Project Options.
    After you have finished filling out the information on all three pages, click Finish.
  6. The Data Link Properties dialog box appears and requires you to supply the name of a server on which the solution can access a database. When you are finished, click OK. Visual Studio now creates a solution from the Humongous Insurance template.

Project Options

The Optional Architectural Projects and Implementation Elements & Settings pages of the Humongous Insurance Framework Project Wizard let you add optional Windows UI, Web UI, Web Services, and Application Security projects to the application you are instantiating. On each page, you can set preferences for the various new projects, including the project name and language. For project options that create components accessible over the Web, you need to provide URLs.

One optional component is the security framework, which is available on the Implementation Elements & Settings page. (For more information, see Security Framework.) The following procedure describes how to add the security framework project to your new application.

To add the security framework

  1. Complete Steps 1 through 4 in the procedure To instantiate the Humongous Insurance sample application.
  2. In the Humongous Insurance Framework Project Wizard, click Implementation Elements & Settings in the wizard's navigation pane.
  3. Select the Application Security check box. Optionally, change the name of the 'AppSecurity' project. You can also select the project's language.
  4. Return to the procedure To instantiate the Humongous Insurance sample application at Step 5. After you click Finish, your new solution will include a security framework project.

Initial Application Structure

The core of an application construction kit is the initial application structure you provide. This section includes considerations for creating the initial structure and describes the approach used in the Humongous Insurance application construction kit sample.

Picking an Approach for Generating Initial Application Structure

There are several options for creating the initial application structure, but this example focuses specifically on applications consisting of multiple projects. The simplest approach is to have a top-level Enterprise Template project (.etp) that instantiates a fixed set of child projects using static prototypes at creation time.

A slightly more complex and powerful approach uses a top-level Enterprise Template project that creates a fixed set of child projects using subproject wizards (which have no user interface) at creation time.

The third option is to use a custom project wizard that can generate a project whose design is customized through user input. The following table lists the approaches and the advantages and disadvantages of each.

Approach Advantages Disadvantages
.etp with static prototypes
  • Is the simplest template type. You can create your template in Solution Explorer as an application, then convert it to a template with a few manual steps.
  • Offers the quickest and easiest approach for simple templates where the languages for the individual projects are fixed.
  • Does not update namespaces within the projects at project creation time, so some editing is required.
  • Requires separate versions for different languages.
  • Cannot add optional items or projects until after a project is created from the template.
.etp with subproject wizards
  • Allows you to update the class and namespace names at project creation time.
  • Provides additional flexibility in what you can add to the initial project structure.
  • Still limited to a fixed set of projects/languages in the initial structure.
  • Requires creating the subproject wizards (JScript) and manually connecting them to the Enterprise Template project with some simple editing of the .etp file.
Custom Project Wizard
  • Provides maximum flexibility. Can prompt user for any desired information and use that information to generate a customized initial project structure.
  • Allows optional projects and project items to be added.
  • Provides a wizard that generates a project in the user's choice of language.
  • Requires the most work to create. If you do not need all the flexibility that this approach offers, the above types might be a better fit.

The approaches using static prototypes and subproject wizards are explained in Creating a Custom Template Walkthroughs in the Visual Studio Enterprise Templates documentation. The remainder of this white paper focuses on the third approach, using a custom project wizard to generate the initial structure of your application.

In the Humongous Insurance application construction kit sample, the goal is to present developers with a new top-level folder in the New Project and Add New Project dialog boxes and to add an icon for the Humongous Insurance Project Wizard to that folder. When a developer double-clicks the icon, he or she can choose which projects and project items to include, what languages to use, what names to give the projects, and so on. The following section shows how to create this wizard.

Creating the Humongous Insurance Project Wizard

The Humongous Insurance Project Wizard has three parts: the wizard UI (HTML pages), the underlying script, and project item templates used by the script. Each of these parts is described in more detail below.

The initial implementation of the Humongous Insurance Project Wizard is based on the Visual C++ Custom Wizard.

Note   Although this example wizard project is based on the Visual C++ Custom Wizard, you need not code it in Visual C++. In fact, the behavior of the wizard is coded in JScript, and its user interface is presented using HTML. Solutions you create using a wizard project based on the Visual C++ Custom Wizard need not contain project items coded in C++.

Starting a Wizard Project

When you code anything in Visual Studio, you work in a project. Wizards are no exception to this rule. Therefore, to start work on a wizard, your first step is to create a wizard project. The following procedure explains how to do this.

Creating a wizard using the Visual C++ Custom Wizard

To open an application wizard, use the New Project dialog box to specify your project properties, including the project name and the directory and solution where your project will reside.

To open a Visual C++ application wizard

  1. On the File menu, click New, and then click Project.
    The New Project dialog box appears.
  2. In the Project Types pane, select the Visual C++ Projects folder. An icon for every type of C++ project appears in the Templates pane.
  3. Select Custom Wizard in the Templates pane.
  4. Specify your project name and other properties, such as its location on disk. For more information, see Projects as Containers in the Visual Studio documentation.
  5. Click OK.
    The wizard for your project type opens.
  6. In the left pane, select Application Settings. In the right pane, define your application settings.
    Wizard friendly name
    Sets the name of the wizard that you are designing. This name is displayed in the ReadMe.txt file and as the title in the HTML file.
    User interface
    Select to specify that the Custom Wizard displays a user interface. If you want the user to select options specifying what code your wizard generates, the wizard must have a user interface. The wizard does not need a user interface if its purpose is to generate template code with no user options. To produce a wizard similar to the Humongous Insurance example, leave this option selected.
    Number of pages
    Sets the number of pages your Custom Wizard requires. This option applies only to a wizard that has a user interface. The Humongous Insurance example sets this value to 3, but you should set it to the number of pages your wizard will require to contain the options you need.
  7. Click Finish to close the wizard and open your new project.

After you have created your wizard project, you can use Solution Explorer to view the newly created files.

Your wizard uses the name that you type in the Name box in the New Project dialog box to derive names for some files and classes. The Custom Wizard adds comments to the files it creates for your project.

Files Created by the Custom Wizard

The following table describes the files created by the Custom Wizard. For more information on how the key elements interact to create a wizard, see Designing a Wizard in the Visual Studio documentation.

File Description
ProjectName.vsz A text file that identifies the wizard engine and provides context and optional custom parameters.
ProjectName.vsdir A text file that provides a routing service between the Visual Studio shell and the items in the wizard project.
HTML Files (optional) A wizard can contain a user interface (UI), which is an HTML interface. A file without a UI contains no HTML files.

Default.htm is the file that specifies features in the UI. If you specify more than one page in application settings of the Custom Wizard, the additional files are each named Page_n.htm, where n is the page number.

Script Files A wizard accesses the script engine and creates a JScript file, default.js, for each project. It also includes Common.js. These files contain JScript functions that access the Visual C++ Wizard, Code, and Environment Object Models to customize a wizard. You can customize and add functions in the wizard project's default.js file.
The file Common.js, which contains commonly-used JScript functions, is shared among all wizards including the wizards used by Visual C++ to create other project types. For more information, see Customizing C++ Wizards with Common JScript Functions in the Visual Studio .NET documentation.
Template Files A wizard's templates are a collection of text files that contain directives, which are parsed and inserted into the symbol table, depending on the wizard user's selections. The template text files are rendered according to the user input and added to the project. The appropriate information is obtained by directly accessing the wizard control's symbol table.
ReadMe.txt A template file that explains the contents and uses of the other new files created by the Custom Wizard.
Sample.txt A template file that shows how your wizard directives are used.
Image Files (optional) You can provide any images, such as icons, GIFs, BMPs, and other HTML-supported image formats, to enhance the UI for your wizard. A wizard that has no UI does not need images.
Templates.inf A text file that lists all templates associated with the project.
Default.vcproj An .xml file that contains the information on the project type.
Styles.css (optional) A file that defines the styles for the UI. If your wizard does not have a user interface, the Custom Wizard does not create a .css file.
Note   If you delete your wizard files and directories, you must also delete the following files from the vc7\vcprojects directory. Until you delete these files, icons for your wizard continue to appear in the New Project dialog box.
  • ProjectName.vsz
  • ProjectName.ico
  • ProjectName.vsdir

Wizard UI

The Custom Wizard creates an HTML page for each of the pages available in your wizard. The first page is named "Default.htm," and each additional page is named "Page_n" where n is the page number in the wizard's tab order sequence.

Each HTML page contains identical HTML for the title, navigation, and button frames. If you want to customize the appearance of any of these frames, you must repeat your customizations on each page.

The Humongous Insurance sample shows a three page wizard UI.

Modifying the Content Section

The body of each HTML page contains two sections where you do most of your customizations: an introductory information section and a content section. Place overall instructions for each page in the introductory information section and any controls and other interactive user interface elements in the content section. Each section is clearly commented in the generated HTML pages. The Humongous Insurance sample files include additional comments to point out specific areas in each file with modifications in addition to what the wizard produced.

It is important that you assign an id attribute, which is unique across all pages, to each control you add to the content section of each page of your wizard. You use these control ids when you define symbols for the supporting wizard.

Defining Wizard Symbols

The supporting wizard engine manages the state of your controls as the user moves between pages in your wizard. You define these symbols in a series of SYMBOL tags within the HEAD tag in the default.htm page of your wizard.

Each SYMBOL tag contains three attributes as illustrated below:

<SYMBOL NAME="control id" TYPE="control type" VALUE="default value">
</SYMBOL>

The following table describes each attribute of the SYMBOL tag.

Attribute Description
NAME The unique id of the control for which the wizard manages state.
TYPE The type of control: text, list box, check box, and so on.
VALUE The default value for the control.

Adding Page-Level Functions

Pages two and three of the Humongous Insurance sample each contain examples of custom JScript functions to handle the user's interaction with the controls on each page. If you use functions with the same name in more than one page, be sure they pass the same number and type of parameters.

Debugging Your Wizard

If you need to insert debugging messages in your code, you can use the JScript "alert()" function within the HTML pages. If you need to insert debugging messages in script code that the wizard executes (default.js), you can use the "wizard.ReportError('message')" function.

Templates, Projects, and Wizards

In the Humongous Insurance application construction kit, the final application is built out of a number of "building blocks" (projects, project items, and so on). Before the script was written to create the application structure based on the choices made in the Wizard UI, it was necessary to have the building blocks available. These consist of the following:

  • Initial Humongous Insurance Application Structure
  • Language project building block wizards
  • Database project wizard

Initial Humongous Insurance Application Structure

The initial Humongous Insurance application structure is set up as follows:

   HIApplication.etp

      Application Models (folder)

         HI Architecture.vsd (Visio Architecture Model)

         HI Data Model.vsd (Visio Database Model)

      BusinessFacadeProjects.etp

      BusinessLogicProjects.etp

      DataAccessProjects.etp

      Database project (see details below)

      SystemServicesProjects.etp

In addition, there are three optional .etp projects that will be added dynamically by the script described above, depending on what selections the user makes. These are:

      WebServiceProjects.etp

      WebUIProjects.etp

      WinUIProjects.etp

This initial application structure contains no language projects (they are added by the script described above). This initial structure was created in the Visual Studio .NET integrated development environment (IDE) by composing the structure out of the Enterprise Template Project building blocks (found under Other Projects \ Enterprise Template Projects in the New Project dialog box). The Application Models folder was added similarly, by selecting the top-level Enterprise Template project in Solution Explorer and choosing Add Folder. The models were then added via the Add Existing Item dialog box. For the optional projects, they were added initially to the parent .etp project, then the parent .etp project was hand-edited to remove the entries that reference the optional Enterprise Template subprojects.

The Policy File property specifies that a customized version of DAP.tdl, named Demo.tdl, is associated with this structure. For details on the customizations and policy in Demo.tdl, see Custom Policy.

You cannot directly add a database project to a template, but must instead create it using a subproject wizard (For more information on creating this wizard, see Database Project Wizard). To associate the UI-less wizard used to create the database project with the application structure, it was necessary to manually edit the top-level .etp file (after closing the solution and copying the files to the appropriate folder). The following addition appears between the <REFERENCES> and <VIEWS> sections:

        <SUBPROJECTWIZARDS>
            <WIZARD>
                <FILE>..\..\proxyprojects\Visual Basic Building Blocks
                            \Database1.vsz
                </FILE>
            </WIZARD>
        </SUBPROJECTWIZARDS>

This links the small wizard to the creation of the initial application structure. Lastly, a second project global is added, following the TDLFILE global, to use for identification:

        <GLOBALENTRY>
            <NAME>TDLELEMENTTYPE</NAME>
            <VALUE>HIApplication</VALUE>
        </GLOBALENTRY>

A global of this name, with unique values, appears in each of the .etp files contained in the initial application structure. These project globals provide a way to apply unique policy to each of the layers of the application structure. For more information, see Custom Policy.

Database Project Wizard

In many cases, you might want to include a project type that does not directly support policy as part of an Enterprise Template project. (Projects that support policy in Visual Studio .NET are: Visual Basic projects, Visual C++ projects, Visual C# projects, and Enterprise Template Projects.) A good example of this is a Visual Studio Database project (.dbp). Including a database project as part of an Enterprise Template project requires more work than including standard language projects (Microsoft® Visual Basic®, Visual C#™, or Visual C++®).

To add a database project to an Enterprise Template, you must use a custom project wizard to invoke the database project, rather than attach it directly to the parent Enterprise Template project (.etp) as a static project prototype. The work breaks down into the following steps:

  • Create the custom wizard script to launch the database project.
  • Make Visual Studio aware of the custom wizard.
  • Attach the custom wizard to an Enterprise Template project.

The Humongous Insurance sample provides a simple wizard, without a user interface, that completes these steps for the user. Under the EFWizards folder resides a very simple wizard named Database. It performs an AddFromTemplate() method on the .dbp file at the specified location, which results in the creation of the database project. You can create a similar custom wizard. This is described in the following section.

Creating the Custom Wizard Script

As a starting point for creating a custom wizard (in this example, a custom database wizard), you can copy existing code/script from a standard wizard that is included with Visual Studio. Beneath the Visual Studio installation folder, you find the following hierarchy:

C:\Program Files

    Microsoft Visual Studio .NET 2003

        EnterpriseFrameworks

            EFWizards

                    csharpdllwiz

                            scripts

                                    1033

                            templates

                                    1033

To create your own custom wizard, copy the csharpdllwiz folder, paste it in the EFWizards folder, and rename the copy "databasewiz".

Note   Because you will be changing the wizard's script, it doesn't matter which Visual Studio wizard you choose to base your new wizard on. You could also have chosen, for example, a Visual Basic wizard to customize.

Delete the files in databasewiz\templates\1033, as you will not be cloning any template files with this wizard. Within the databasewiz\scripts\1033 folder is the file you need to modify, default.js.

Initially, this contains the following functions:

  • function OnFinish(selProj, selObj)
  • function GetCSharpTargetName(strName, strProjectName)
  • function DoOpenFile(strName)
  • function SetFileProperties(oFileItem, strFileName)
  • function CollapseProjectNode( oProj )

Since you will not clone any template files as part of this wizard, you can delete all the functions except OnFinish() and CollapseProjectNode(). Leave the CollapseProjectNode() function unchanged. You need to update the OnFinish() function as follows. The bold lines are critical, as they create the project using the specified template.

function OnFinish(selProj, selObj)
{
    var oldSuppressUIValue = true;
    try
    {
        oldSuppressUIValue = dte.SuppressUI;
        var bSilent = wizard.FindSymbol("SILENT_WIZARD");
        dte.SuppressUI = bSilent;

        var strProjectName = wizard.FindSymbol("PROJECT_NAME");
        var strProjectPath = wizard.FindSymbol("PROJECT_PATH");
        var strTemplatePath = wizard.FindSymbol("TEMPLATES_PATH");
        var strTemplateFile = strTemplatePath + 
           "\\..\\..\\..\\..\\projects\\visual basic building blocks\\"
           + wizard.FindSymbol("EFT_PROTOTYPE"); 

        var oTarget = wizard.FindSymbol("TARGET");

        var prjItem = oTarget.AddFromTemplate(strTemplateFile,
            strProjectPath+"\\"+strProjectName +".dbp");
        
        return 0;
    }
    catch(e)
    {   
        switch(e.number)
        {
        case -2147024816 /* FILE_ALREADY_EXISTS */ :
            return -2147213313;

        default:
            SetErrorInfo(e);
            return e.number;
        }
    }
    finally
    {
        dte.SuppressUI = oldSuppressUIValue;
    }
}

The wizard script determines where the Enterprise Templates reside (TEMPLATES_PATH) and points to the folder where you placed the copy of the standard .dbp file. The AddFromTemplate() method then launches the database project UI, prompting for the required information.

Custom Wizard Script

In addition to the HTML that provides the UI functionality of the wizard, the custom project wizard uses the default.js JScript file. This file contains the script that creates the application after the user selects "Finish" in the project wizard. This custom JScript can be as simple or as complex as necessary to generate the desired application.

The script for the Humongous Insurance project wizard is in the HIFrameworkProjectWizard\Scripts\1033 folder. It contains the following functions:

  • OnFinish() – This function is called when Finish is clicked in the wizard UI. The subsequent functions are called by OnFinish as helper functions. OnFinish is a long function but it is straightforward in that it gets strings from the wizard UI and uses them to set template paths, wizard names, programming languages, options, and project names.
  • CreateTopLevelETP() – This helper function checks the "CLOSE_SOLUTION" symbol to see whether the new project should be added to the existing solution or whether the existing solution should be closed and the project should be created in a new solution. It then uses AddFromTemplate to create the top-level Enterprise Template project. AddFromTemplate is used in several cases and is the primary means of creating new projects and project items. This actually creates not only the top-level Enterprise Template project, but also anything that is part of that top-level project (the folder, the standard subprojects, and the database project).
  • CreateSubETP() – This helper function creates the specified Enterprise Template subproject beneath the parent Enterprise Template project. It uses essentially the same mechanism as the prior function, except that it does not create a new solution, but simply adds the project (using AddFromTemplate). Note that this function is only used for the optional child Enterprise Template projects as the standard ones get created with the initial application structure.
  • CreateSubProject() – Given a parent Enterprise Template project, this function creates the specified subproject as a child of that parent. It obtains the appropriate template path and name and invokes AddFromTemplate on the parent project to create the subproject.
  • CreateSystemServicesProject() – This is a customized version of CreateSubProject that creates a top-level Visual C# or Visual Basic project (without references or project items) and then adds the specified optional project items and references. This extends to allow a number of custom options including project references, project items, and so on. AddFromTemplate is used to create the subproject, using the .csproj or .vbproj file, rather than a project wizard.
  • AddOptionalSystemServicesItems() – This function handles the addition of the optional project items. The project item is "rendered" into a temporary file. The rendering consists of substituting for any replaceable parameters in the project item. The rendering process looks for text of the format "[!output Name]" and replaces that string with the value of the symbol "Name". This can be a symbol defined by the wizard or a user-defined symbol. To define a custom symbol, you simply call wizard.AddSymbol(symbolname, symbolvalue) in your script. For example, the Humongous Insurance project wizard script uses this approach to replace SAFE_CLASS_NAME within the project item. You can define a number of custom tags within your code templates, prompt the user for the values as part of the wizard UI, and then perform the substitution at project or project item creation time.
  • GetTargetName() – This function is used when project items need to have their names replaced at project creation time. The Humongous Insurance project wizard script does not use this function, but it is included for completeness.

The Humongous Insurance project wizard script is custom script, significantly different from the original script generated by the VC++ Custom Wizard. The script is well documented with code comments, so refer to it for specific details.

Custom Folders in the New Project and Add New Project Dialog Boxes

The next step is to add a new top-level folder to the New Project and Add New Project dialog boxes to house the Humongous Insurance sample and its custom project wizard. This makes it easier for consumers of the template to find it. To add these folders, you need to add registry keys. For your convenience, the Humongous Insurance sample supplies .reg files that update the registry with the necessary keys for this sample.

Note   The installation file for the Humongous Insurance sample, InstallACK.cmd, runs these .reg files. For that reason, the information given in this section is purely instructional, since the task of updating the registry has already been accomplished if you have run InstallACK.cmd to install the sample.

Modifying the registry for the New Project dialog box

To understand how to create a new top-level folder and how to have custom templates or projects reside in that folder, follow the steps in this section.

There are two parts to the process:

  1. Generate a unique GUID.
  2. Update the registry using the GUID to point to your custom template directory.

For the first part, you can use a tool such as GUIDGEN to generate a unique GUID in registry format. For example, assume you run GUIDGEN and receive the unique GUID {499D9F32-1BA6-4a5e-A781-188CAF8085FE}.

For the next part, you need to modify the registry.

To modify the registry for the New Project dialog box

  1. Run RegEdit and navigate to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1\NewProjectTemplates\TemplateDirs.
  2. In the TemplateDirs node, add a key where the key name is the GUID generated above.
  3. Beneath the GUID-named node, add a node named "/1", with a (Default) value equal to the name you want to appear in the New Project and Add New Project dialog boxes for this folder. For example, name the folder "TopLevelFolder".
  4. Add two more sub-keys to the "/1" node.
    1. Make the first key a DWORD key named "SortPriority". The value determines in what order the folders appear in the dialog boxes. A smaller number corresponds to a higher rank. Assume you want your custom folder to always appear first, so give it a SortPriority of "0".
    2. The second key is a string key named "TemplatesDir" and the value is the path to your custom projects folder. For this example, your custom projects appear in the folder "D:\Public\Demo\EnterpriseFrameworks\ProxyProjects". If you have a set of templates that reside on a network share, you can put the UNC path here.

The registry tree should look similar to this:

Figure 1. Registry tree

The keys and values for the "/1" node should appear similar to this:

Figure 2. Registry keys and values

Note   There are a few limitations. If your custom template launches subproject wizards, those wizards must reside in the [Drive letter where Visual Studio .NET is installed]:\Program Files\Microsoft Visual Studio .NET 2003\EnterpriseFrameworks\EFWizards folder, regardless of where the template itself is located. Similarly, if your template only specifies the name of the policy file and not the full path, then that policy file needs to reside in the standard policy folder (EnterpriseFrameworks\Policy) in order to be found. If your template uses static prototypes and contains an absolute path to the policy file, then all of your template files can reside on a network path.

Modifying the registry for the Add New Project dialog box

If a user adds a project to a parent Enterprise Template project, they use the Add New Project dialog box. To modify this dialog box, you follow the same process as described above for modifying the New Project dialog box, with one exception. In Step 1 of the procedure To modify the registry for the New Project dialog box, navigate to the following location in the Registry Editor:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1\Projects\{FE3BBBB6-72D5-11d2-9ACE-00C04F79A2A4}\AddItemTemplates\TemplateDirs

Follow the remaining steps in that procedure to add a custom folder to the Add New Project dialog box.

Custom Project Items and Folder

In addition to the custom top-level folder in the New Project and Add New Project dialog boxes, the Humongous Insurance sample also uses the following custom project items.

  • A class that defines a custom attribute. This attribute can be used to uniquely identify classes. These classes do not implement a unique interface or derive from a unique base class. Without this attribute, these classes could not otherwise be uniquely identified. The section Using Custom Code Attributes for TDL Identification describes how to take advantage of this unique identification in the policy enforced in your application.
  • A template class that uses the custom attribute.

To create these custom project items, the Humongous Insurance sample started with the C# and VB Class Wizards and customized them. The demo folder has the following directory structure as pertains to the custom project items:

   AckDemo

      VB7

         VBProjectItems

            Custom Project Items

         VBWizards

            HIPolicyClassTypeAttribute

            HIPolicyTypedClass

      VC#

         CSharpProjectItems

            CustomProjectItems

         VC#Wizards

            HICSharpPolicyClassTypeAttributeWiz

            HICSharpPolicyTypedClassWiz

These custom project items were created for the Humongous Insurance sample as follows.

  1. Create the CustomProjectItems folder beneath the ProjectItems folders.
  2. Copy VBWizards\Class (found at C:\Program Files\Microsoft Visual Studio .NET 2003\Vb7\VBWizards) to VBWizards\HIPolicyClassTypeAttribute and to VBWizards\HIPolicyTypedClass
  3. Copy VC#Wizards\CSharpAddClassWiz (found at C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\VC#Wizards) to VC#Wizards\HICSharpPolicyClassTypeAttributeWiz and VC#Wizards\HICSharpPolicyTypedClassWiz
  4. Modify the contents of the Templates\1033 folder beneath each of the wizards as desired. See the Humongous Insurance sample files for details.

Security Framework

The Humongous Insurance sample contains a security framework example that illustrates one way you might set up a commonly used service for people who build applications using your template. This section briefly describes the example security framework, but it is only an example to show how you can provide similar common services in your own application construction kits. Enterprise applications can have differing security requirements. The security framework described here is specific to the Humongous Insurance sample. Your application might have different security requirements and solutions.

The Authorization and Authentication Framework example included in this sample supports the following scenarios.

  • Set up a project for a development team with a specific security policy enforced.
  • Apply a common security infrastructure so individual developers do not have to write custom code.
  • Re-use this infrastructure for multiple applications across the enterprise.

This security framework was designed to support the following requirements.

  • Specify roles associated with user and application.
  • Specify authentication credentials for the application.
  • Specify authorization levels on components.
  • Specify the source of authentication and role information.
  • Automatically...
    • Set roles.
    • Check roles.
    • Authenticate users.
    • Generate customized login form.
    • Insert framework into project.

This ASP.NET authentication example supports four basic authentication modes.

  • Windows
    • Relies on Windows 2000/NTFS and IIS 5.0 permissions.
    • Supports Basic, NTLM, Digest, and certificates for authentication.
  • Forms
    • Customize login experience.
    • Custom authentication store.
    • Control over password formats.
    • Control over authentication "events."
  • Passport
    • Redirected to MSN passport.
  • None

ASP.NET authorization is used in this sample security framework to provide the following support.

  • Authorization based on roles and users.
    • Roles - These are application roles, not Windows NT groups or COM+ roles. The runtime attaches principal to the call context per thread.
    • Users - Can explicitly set user access to ASP.NET resources.
  • Identity and Impersonation
    • Explicitly specifies what user token to use.

The diagram below shows the various components of the security framework and illustrates how they interact.

Figure 3. Security Framework Processing in Humongous Insurance Sample

  1. Browser requests page1.
  2. ASP.NET process loaded, Web.config is read.
  3. Global.asax registers security framework event handlers and checks for authorization token.
  4. Configuration handler reads framework configuration.
  5. ASP.NET redirects browser to Login.aspx.
  6. Browser requests login.
  7. On submit, login page instantiates Authentication class and performs authorization check.
  8. Roles are looked up in the database.
  9. Authentication token is set and role context is set.
  10. ASP.NET redirects browser to page1 and issues security cookie.

The application security framework adds the following to the Humongous Insurance Enterprise Template project:

  • Alters Global.asax to call the framework by calling Application_OnAuthenticateRequest and Application_Start.
  • Alters Web.config and inserts the security framework handler and the ASP.NET security configuration section.
  • Works together with project policy to include framework components, login controls, and class templates for component role-based security.

Adding the Security Framework

Although the files that make up the security framework project are included with the Humongous Insurance application's files, they do not become part of a solution unless you specify 'Application Security' when instantiating the solution. For more information, see Instantiating the Humongous Insurance Sample Application.

Custom Guidance

Adding custom guidance requires three steps:

  • Authoring the content.
  • Associating keywords and attributes with one or more content files.
  • Associating Solution Explorer items with keywords and attributes.

You can author the content in any HTML editor, or even Notepad. To map content to keywords and attributes, use the Context.xml file located at [Drive letter where Visual Studio .NET is installed]:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\HTML\XMLLinks\1033. The last step requires changes to the policy file. For details, see Custom Policy.

The Humongous Insurance sample has two sets of guide information associated with the application structure. The first set is specific information about each of the projects in Solution Explorer. The second set is a group of HTML pages with sample code for various common services (authentication, exception catch/throw, Active Directory add/search, and so on). Each guidance page is associated with the appropriate projects and displays in the Dynamic Help window when those projects have focus. To learn how to associate Solution Explorer items (such as projects) with keywords and attributes, see Associating Projects with Custom Guidance.

For detailed information on adding custom guidance, see Providing Custom Help for Enterprise Templates in the Visual Studio .NET documentation. The same approach documented there is demonstrated in the context file for the Humongous Insurance sample, located at \Common7\IDE\HTML\XMLLinks\1033\CustomHelp.xml.

Custom Policy

The last major step is to define the policy that applies to this application structure. This includes defining what projects or items can be added to various parts of the solution, specifying any Toolbox, menu, or property constraints, and associating the projects with custom guidance. The two policy files that ship with Visual Studio .NET, DAP.tdl and VSIDE.tdl, can be used as a starting point for a custom policy file. These files are general because the policy requirements of specific applications vary widely. For use in a specific application, you must create a customized copy that defines the rules you want your policy to implement.

The Humongous Insurance sample started with a copy of DAP.tdl, named Demo.tdl. To provide the policy that Humongous Insurance wanted to implement, Demo.tdl includes a number of changes. Some of the most significant changes are described below.

Note   These changes have already been made in the Demo.tdl file that is included in the Humongous Insurance application demo files. You do not need to change the contents of Demo.tdl.

POLICYMODE

The POLICYMODE value changed from "Permissive" to "Restrictive". This means that any project or project item that does not have a corresponding element definition will be disallowed.

Constraining the TimeToBeReceived Property

The Humongous Insurance sample needed a property constraint on the MessageQueue component's TimeToBeReceived property (a subproperty of DefaultPropertiesToSend) that limits the allowed range of values to 1-120 seconds. The Humongous Insurance sample added this constraint in the DEFAULTSETTINGS section of the policy file so that it applies anywhere in the solution that a MessageQueue component might be used.

Preserving the Project Structure

To maintain the initial project structure, the top-level Enterprise Template project does not allow anything to be added to it. In the ELEMENTSET definition, only the existing Enterprise Template subprojects are allowed; all other projects are excluded.

Each of the Enterprise Template subprojects only allows the projects for that application layer or the empty project types. If you want the application to be only composed of predefined building blocks, you could also disallow the empty project types. For example, the following ELEMENTSET definition for BusinessFacade, permits only the specified projects:

      <INCLUDE>projBusinessFacade</INCLUDE>
      <INCLUDE>projCSharpEmptyProject</INCLUDE>
      <INCLUDE>projVBEmptyProject</INCLUDE>
      <INCLUDE>projCSharpProject</INCLUDE>
      <INCLUDE>projVBProject</INCLUDE>
      <INCLUDE>projVCManagedCppEmptyProject</INCLUDE>
      <INCLUDE>projVCProject</INCLUDE>

The ELEMENTSET allows the BusinessFacade building block, the empty project types for Visual C#, Visual Basic, and Visual C++, and three other projects. The element definitions for projCSharpProject, projVBProject, and projVCProject, have no prototype defined (this means they do not correspond to entries in the New Project or Add New Project dialog boxes). These do not correspond to projects that can be added, but rather are here to show that you could restrict a particular layer to only be written in Visual Basic, by disallowing all projCSharpProject and projVCProject types. This would cause a policy reminder to be generated if the user added an existing Visual C# or Visual C++ project to the BusinessFacadeProjects layer.

Controlling Project Items and Project References

Each of the language projects has a specific set of allowed project items and project references. This allows control of both references to system assemblies and cross-project references. For example, in the DataAccess project (projDataAccess) the ELEMENTSET definition is as follows (reordered for clarity):

      <INCLUDE>projItemAssemblyInformationFile</INCLUDE>
      <INCLUDE>projItemAssemblyResourceFile</INCLUDE>
      <INCLUDE>codeComponent</INCLUDE>
      <INCLUDE>projItemClass</INCLUDE>
      <INCLUDE>projItemVBModule</INCLUDE>
      <INCLUDE>projItemVBCodeFile</INCLUDE>
      <INCLUDE>projItemAssemblyInfoFile</INCLUDE>
      <INCLUDE>projItemCSharpFile</INCLUDE>
      <INCLUDE>projItemLicenseFile</INCLUDE>

      <INCLUDE>catComponentCodeItems</INCLUDE>
      <INCLUDE>catDataCodeItems</INCLUDE>

      <EXCLUDE>codeDataSQLCommand</EXCLUDE>
      <EXCLUDE>codeDataSQLConnection</EXCLUDE>
      <EXCLUDE>codeDataSQLDataAdapter</EXCLUDE>

      <INCLUDE>refSystemFrameworksDll</INCLUDE>
      <INCLUDE>refSystem.Windows.Forms</INCLUDE>
      <EXCLUDE>refSystem.Web</EXCLUDE>
      <EXCLUDE>refSystem.Web.Services</EXCLUDE>

      <INCLUDE>projItemPolicyTypedClass</INCLUDE>

The first nine INCLUDE nodes define the types of project items that can be added to the DataAccess project(s). The Humongous Insurance sample allows a number of the standard project items; however, you could disallow all the standard project items and require a project to be composed entirely out of predefined building blocks.

The next two INCLUDE statements include categories of code objects that correspond to the Toolbox items in the Component and Data sections of the Toolbox. Notice that all of the data items are initially included, and then three specific data Toolbox items — the SQL-specific items — are excluded. In cases where you are expressing an exception, use a category to make what is intended clear. This is especially helpful when you revisit the policy file in the future. In this example, it is easy to understand that all Data Toolbox items are included except those three specified. If you add more custom data Toolbox items to the catDataCodeItems at a later time, this ELEMENTSET definition does not need updating.

The next set of statements covers references. A reference to the SystemServices project is allowed, but not references to other projects. The refSystemFrameworksDll element is defined to map to the output of the SystemServices section of the application, SystemFrameworks.dll. The Humongous Insurance sample allows a reference to System.Windows.Forms because the MessageQueue component requires System.Windows.Forms for design-time support, but prevents developers from referencing System.Web or System.Web.Services by excluding those.

Finally, the DataAccess project allows the custom PolicyTypedClass item as a project item.

Constraining the Background Property

The ELEMENTSET definition for the WebUI project contains a Property constraint that sets the background property for any children of the project to use the specified company logo bitmap. This is an example of a property constraint that applies to all children (project items, such as Web Forms or HTML pages, or even controls on those forms, if they support the named property).

Allowing Only Custom Policy-Typed Class Items in the SystemServices Project

Here are a few lines from the ELEMENTSET definition for the SystemServices project:

      <INCLUDE>projItemPolicyClassTypeAttributeClass</INCLUDE>
      <INCLUDE>projItemPolicyTypedClass</INCLUDE>
      <EXCLUDE>projItemClass</EXCLUDE>

The policy file specifies that the custom project item that defined the PolicyClassTypeAttribute can be added to the SystemServices project. The policy file also allows the custom policy-typed class item to be added, but disallows the standard generic-class type. This demonstrates how you could require developers to use a specific class template you designed. In this case, the policy file uses a class-template that has the extra identification attribute. You can see the effects of this in the Add Item dialog box for the SystemServices project.

To take advantage of this filtering, the policy file defines new elements as follows:

<!-- ============================================================ -->
<!-- The following are a couple of custom project items. -->
<ELEMENT>
   <ID>projItemPolicyClassTypeAttributeClass</ID>
   <PROTOTYPES>
      <PROTOTYPE>
         [VB]\VBProjectItems\HIPolicyClassTypeAttribute.vsz
      </PROTOTYPE>
      <PROTOTYPE>
       [VC#]\CsharpProjectItems\HICSharpPolicyClassTypeAttributeWiz.vsz
      </PROTOTYPE>
   </PROTOTYPES>
</ELEMENT>
<ELEMENT>
   <ID>projItemPolicyTypedClass</ID>
   <PROTOTYPES>
      <PROTOTYPE>
         [VB]\VBProjectItems\HIPolicyTypedClass.vsz
      </PROTOTYPE>
      <PROTOTYPE>
         [VC#]\CSharpProjectItems\HICSharpPolicyTypedClassWiz.vsz
      </PROTOTYPE>
   </PROTOTYPES>
</ELEMENT>
<!-- ============================================================ -->
<!-- The following shows how to identify a class based on the value -->
<!-- of a custom code attribute "PolicyClassType" -->
<ELEMENT>
   <ID>codeTypedClassCustomClass1</ID>
   <IDENTIFIERS>
      <IDENTIFIER>
         <TYPE>CODE</TYPE>
         <IDENTIFIERDATA>
         <NAME>Attribute:SystemServices.PolicyClassType</NAME>
         <VALUE>"CustomClass1"</VALUE>
         </IDENTIFIERDATA>
      </IDENTIFIER>
   </IDENTIFIERS>
</ELEMENT>      

The first two elements define the custom project items, specifying where the prototypes are found. These allow filtering of the Add Item dialog box. The third element identifies a specific instance of a class that uses the custom attribute "PolicyClassType" with a value of "CustomClass1". For more information, see Using Custom Code Attributes for TDL Identification below.

Associating Projects with Custom Guidance

Each of the Enterprise Template projects and language projects used in the Humongous Insurance template has a specific context. The context associates keywords and attributes with each Solution Explorer selection that matches the current element definition. For example, the context section for the SystemServices project element is as follows.

   <CONTEXT>
      <CTXTKEYWORD>SystemServices</CTXTKEYWORD>
      <CTXTATTRIBUTE>
         <NAME>HIAttribute</NAME>
         <VALUE>SystemServices</VALUE>
      </CTXTATTRIBUTE>
   </CONTEXT>

This context specifies a keyword "SystemServices" and an attribute "HIAttribute" with a value "SystemServices". These correspond to entries in the CustomHelp.xml file, enabling your custom guidance topic(s) to display in the Dynamic Help window. For more information on CustomHelp.xml, see Custom Guidance above. For detailed information on adding custom guidance, see Providing Custom Help for Enterprise Templates in the Visual Studio .NET documentation.

Using Custom Code Attributes for TDL Identification

Enterprise Templates provides several methods of uniquely identifying classes and interfaces. Without making modifications to your sources, you can identify classes using base class or the interfaces implemented by that class. However, this method does not work when you need to uniquely identify a class that does not derive from a base class or that cannot be uniquely identified by base class; for example, when you must identify several classes that share a common base class. For this situation, you can use another method of identification: code attributes.

Defining a Custom Attribute

You can use either Visual C# or Visual Basic to associate a custom attribute with a class. You can then use that attribute and its value to uniquely identify any class in your application or framework. You can define the custom attribute class as follows:

[Visual C#]

    [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
    public class PolicyClassType : Attribute
    {
        public PolicyClassType(string className)
        {
            this.className = className;
        }
        public string className
        {
            get 
            {
                return(className);
            }
            set
            {
                className = value;
            }
        }
    }

[Visual Basic]

    ' This attribute is intended for use with Classes
    <AttributeUsage(AttributeTargets.Class)> _
            Public Class PolicyClassType
        Inherits System.Attribute

        ' Private fields
        Private m_typeName As String

        ' This constructor defines the required parameter
        Public Sub New(ByVal typeName As String)
            Me.m_typeName = typeName
        End Sub

        'Define Name property (reaonly attribute)
        Public Overridable ReadOnly Property TypeName() As String
            Get
                Return m_typeName
            End Get
        End Property
    End Class

If you define this attribute class within a particular namespace, you must give the fully-scoped name when referring to it in your policy file. In this example, it is defined within the myNamespace.OrderRequest namespace.

Associating a Custom Attribute with a Class

To assign the custom attribute to a class, do the following:

[Visual C#]

    /// <summary>
    /// Summary description for orderRequestTypeOne.
    /// </summary>
    [PolicyClassType("TypeOne")]
    public class orderRequestTypeOne
    {
        //
        // class contents deleted for clarity
        //
    }

[Visual Basic]

    <PolicyClassType("TypeOne")> _
    Public Class orderRequestTypeOne
        'Class contents deleted for clarity
    End Class

The bold attribute is all that is needed to associate a custom attribute called "PolicyClassType" whose value is "TypeOne" with this class, orderRequestTypeOne.

Updating the Policy File to Identify a Class by Custom Attribute

In your policy file, the element definition identifies this class as follows:

<ELEMENT>
    <ID>codeOrderRequestTypeOne</ID>
    <IDENTIFIERS>
        <IDENTIFIER>
            <TYPE>CODE</TYPE>
            <IDENTIFIERDATA>
                <NAME>Attribute:myNamespace.OrderRequest.ClassType
                </NAME>
                <VALUE>"TypeOne"</VALUE>
            </IDENTIFIERDATA>
        </IDENTIFIER>
    </IDENTIFIERS>
</ELEMENT>

Note that the <VALUE> must include the quotations and that the attribute is fully qualified with the namespace.

After you define this element, you can quite easily INCLUDE or EXCLUDE classes that match this element definition from a parent project or code element. This is particularly useful when you need to uniquely identify a class without a base class, or must distinguish between two classes that both derive from the same base class or implement the same interface.

Conclusion

The Humongous Insurance sample illustrates the various parts of Visual Studio that you can use to create an application construction kit. By using Enterprise Templates and policy, custom project wizards, and other Visual Studio .NET features, you can provide your development team with a starting point that is more sophisticated than an empty solution, while reducing the number and complexity of choices that the developers must make.

You can create a custom wizard such as the Humongous Insurance sample in just a day or two. For cases in which you anticipate that a particular type of application will be developed several times, this initial investment helps reduce the per-project overhead. Reusable code, such as the security framework pieces shown in this sample, can be part of the initial project structure, eliminating the need to show developers where to find that reusable code. In this way, Visual Studio .NET and Enterprise Templates make architectural guidance and code reuse a practical option.

posted on 2005-07-23 09:04  forrestsun  阅读(524)  评论(0编辑  收藏  举报