Franc Stratton's .NET (TM) Web Application, OOP, and SOA Architecture & Programming Site

A site devoted to ASP.NET (TM), SilverLight (TM) and Browser-Based WPF (TM) Applications, IIS Services, and OOP Architectures

Home     Architecture Overview     WF/WCF/WPF     Data Store     Standards     .NET Security     Resources     jQuery     Silverlight     Developer Tips     Blog     Site Map      
March 01

Protecting an ASP.NET Web Application Continued
 

Application Page Code to Further Secure Forms Authentication

In the form login button click event for an ASP.NET Web application, the developer should limit the number of tries for a login to a pre-determined limit, and then lock out the user account in the database. The programmer should validate the user name and password at the client with JavaScript validators, and then validate a second time with server-side code business logic. Regular expressions are a common forms of both client-side and server-side validation methods. Limiting a user name or password textbox with the maximum limit property also will perform a type of string size validation. The login button click event might contain:

protected void btnSignin_Click(object sender, EventArgs e)

{

/// <summary>

/// Count number of login tries and could lockout if too many.

/// Clean both password and user id then compare to decrypted

/// password in login database. Stored login info in database.

/// If authenticated, then encrypt cookie for forms authentication.

/// Save cookie, and if times out, user must login again.

/// </summary>

numLoginTries = numLoginTries + 1;

if (numLoginTries >= 4)

{

    this.txtUserName.Text = "";

    this.txtPassword.Text = "";

    //put in code here to lock out user from logging into database again if needed.

    //put in code here to notify security web service of violation if applicable

    this.lblMessage.Text = "You have exceeded the number of allowable login tries. You are now locked out of this application.";

    return;

}//if (numLoginTries >= 4)

else

    {

         // Attempt to Authenticate User Credentials using encrypted passwords

         string passWord = string.Empty;

         //Clean the RacfID and Password strings before passing on

         string userRacfId = CleanStringRegex(this.txtUserName.Text.Trim());

         string UserPassword = CleanStringRegex(this.txtPassword.Text.Trim());

         //validate clean strings for racfId and password character/number formats/allowable special characters

         if (ValidateUserAndPassword(userRacfId, userPassword) == true)

         {

         //User name and password validated at Client and then Business Object

         //Now fetch encrypted password by RACFID, decrypt, and compare in

         //Business Object

         try

         {

         //Use a hash of the password with a salt value and compare hashes.

             userObject = Logon(userDS, userRacfId, userPassword);

             //If results returned, then login valid

             if (userObject.IsValid == true)

                 {

                 //Login valid, so now make audit log login entry

                     if (MakeAuditLogEntry(userRacfId.Trim()) == false)

                     {

                         this.lblMessage.Text = "Unable to log in, please try again later.";

                         return;

                     }

                    

                     //set up for cookie encryption

                     string encryptedTicket = "";

                     string userData = "WhatEverUserDataNeededHere";

                     FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(

                              1,

                              userRacfId.Trim(),

                              System.DateTime.Now,

                              System.DateTime.Now.AddMinutes(60),

                              false,

                                    userData,

                                    FormsAuthentication.FormsCookiePath);

                                encryptedTicket = FormsAuthentication.Encrypt(ticket);

                              // Use security system to set the UserID within a client-side Cookie

                              Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket));

                              //Redirect browser to MainMenu.aspx

                      FormsAuthentication.RedirectFromLoginPage(userRacfId.Trim(), false);

                            }

                        }

                        else

                        {

                           //if rows not returned, then login not valid, prompt with Login Failed

                               this.lblMessage.Text = userMessage.Trim() + "  <" + "br" + ">Login Failed!" + "<" + "br" + ">";

                        }

                    }   //try

                    catch (Exception g)

                    {

                        if (userMessage != "OK")

                        {

                            //In this application, user message inserted

                            //from business object, and real error goes to

                            //Exception handler Web or WCF Service

                            ShowMessageBox(this.Page, userMessage);

                            //ShowMessageBox is a JavaScript alert box

                        }

                        else

                        {

                            //put BLL save app error here.

                            string sError = ReturnBLLErrorDescription(g);

                            HandleBPAppErrors(1, "Client.btnSearch_Click", userRacfId.Trim(), sError);

                            userMessage = "An error has occurred and has been reported";

                            //ShowMessageBox is a JavaScript alert box

                            ShowMessageBox(this.Page, userMessage.Trim());

                            //You can also display error message on form label by uncommenting the code below

                            //this.lblError.Text = sError;

                        }

                    }

                }

            }// else if (numLoginTries >= 4)

In the form initialization and load events, use Forms Authentication and encrypted tickets in the cookie to prevent unauthorized access to forms before they query the database and present sensitive data to the client browser. For example, in the “Page_init” event, verify that the page request is from an authenticated user with code like the following:

// Check whether the current request has been

// authenticated. If it has not, redirect the

// user to the Login.aspx page.

if (!Request.IsAuthenticated)

{

    Response.Redirect("Login.aspx", false);

}

If it has not been authenticated, the request is redirected to a login page where users can enter their user name and password credentials into the Web application.

If you load JavaScript code from the “Page_init” event of an ASP.NET form, further security can be assured by first checking to see if the user has been authenticated:

//Check to make sure that user is authenticated

//before loading the JavaScript -- this prevents

//unauthorized viewing or misuse of data on a form

if (User.Identity.IsAuthenticated)

{

    //Resets the Drop Down List in Dialog Window

    GenerateResetDDLScript();

    //Adds a function to the drop down list to display a print, Excel, and

    //Word dialog Window based on a selected index

    GenerateDisplayPrintDialogScript();

    this.ddlActions.Attributes.Add("onchange", "Print(this)");

}

else

{

    Response.Redirect("NoAccess.aspx");

}

The JavaScript should be placed under the .NET Common Language Runtime (CLR) managed code control by injecting the script with the ClientScript class into the HTML markup code sent to the client browser. 

#region Print Grid

 

//---------------------------------------------------------------------------

/// <summary>

/// Method Name: GenerateDisplayPrintDialogScript

/// Description: Loads javascript to perform printing of grid. 

/// </summary>

//---------------------------------------------------------------------------

public void GenerateDisplayPrintDialogScript()

{

    string jScript = string.Empty;

    jScript = "<script language='javascript' type='text/javascript'>function Print(ddl)" +

    "{ " +

    "  if (ddl.selectedIndex == 1) " +

    "  {" +

    "    PagingGridView1.print(); " +

    "    ResetDDL(); " +

    "  } " +

    "  else if (ddl.selectedIndex == 2)" +

    "  { " +

    "    PagingGridView1.exportToExcel('UserList', true, true, false, true);" +

    "  } " +

    "  else if (ddl.selectedIndex == 3)" +

    "  { " +

    "    PagingGridView1.exportToWord('UserList', true, true, false, true);" +

    "  } " +

    "} " +

    "</script>";

    if (!ClientScript.IsClientScriptBlockRegistered("PrintScript"))

    {

        ClientScript.RegisterClientScriptBlock(this.GetType(), "PrintScript", jScript);

    }

}

 

//---------------------------------------------------------------------------

/// <summary>

/// Method Name: GenerateResetDDLScript

/// Description: Resets the DDL back to the zero index when action completed.

/// </summary>

//---------------------------------------------------------------------------

public void GenerateResetDDLScript()

{

    string jScript = string.Empty;

    jScript = "<script language='javascript' type='text/javascript'>function ResetDDL()" +

    "{ " +

    " document.getElementById('" + this.ddlActions.ClientID +  "').selectedIndex = 0;  " +

    "}" +

    "</script>";

    if (!ClientScript.IsClientScriptBlockRegistered("ResetDDLScript"))

    {

        ClientScript.RegisterClientScriptBlock(this.GetType(), "ResetDDLScript", jScript);

    }

}

 

#endregion Print Grid

The Client Script Manager Class (ClientScript) can also be utilized to register JavaScript files in the same manner as the script strings above. The advantage of utilizing a JavaScript file is that breakpoints can be set in it to allow code debugging stop and restart breakpoints.



5:23 AM GMT  |  Read comments(0)

Protecting an ASP.NET Web Application

The first task in preventing unauthorized use of sensitive resources in an ASP.NET application that uses Internet Information Services (IIS) is to restrict access. ASP.NET developers restrict unauthorized access by utilizing ASP.NET and IIS authentication techniques. For Intranet Web applications, the developer could choose Windows or Forms Authentication, but for Internet ASP.NET security, Forms Authentication is the most used alternative.

Using Windows Authentication

For Intranets, the ASP.NET programmer can set up Windows Authentication in the main solution Web.config file in a project that contains the Web application user startup and processing forms in the following manner:

<configuration>

    <system.web>

        <authentication mode="Windows" />

            <authorization>

                <deny users="?"/>

            </authorization>

    </system.web>

</configuration>

Note that the <deny users=”?”/> directive within the <authorization> section above is what tells ASP.NET to deny access to the application to all “anonymous” users to the site (the “?” character means anonymous user).  This forces Windows to authenticate the user, and ensures that the username is always available from code on the server.

Clients then simply log into Active Directory eliminating the need for a login form, and unlike Forms Authentication, users need not manually enter their username/password credentials to login to the application.  Instead, ASP.NET and IIS can automatically retrieve and validate the Windows username of the end-user visiting the site in a secure manner.

If the programmer wants to access the logged-in username and role/group mappings for the authenticated user within ASP.NET form code-behind, the follow C# code will accomplish this task:

string userName;

userName = User.Identity.Name;

To gain access to user information from within a business or data access layer class, a developer need only write C# code like the following to achieve the same result as above:

System.Security.Principal.IPrincipal user;

user = System.Web.HttpContext.Current.User;

 

string userName;

userName = user.Identity.Name;

An ASP.NET Intranet application can access the current Windows authenticated user role/group mappings with the following C# code:

if(user.IsInRole(@"DOMAIN\managers"))

{

    this.lblRole.Text = user.Identity.Name + " is in the manager role.";

}

else

{

    this.lblRole.Text = user.Identity.Name + " is not in the manager role.";

}

Using Forms Authentication

Forms authentication uses an authentication ticket that is created when a user logs on to a site, and then it tracks the user throughout the site. The forms authentication ticket is usually contained inside a cookie that should be encrypted. Forms authentication should be combined with the IIS anonymous authentication option.

If IIS is configured to use anonymous authentication, IIS authenticates the user and creates a Windows token to represent the user. IIS determines the authentication mode that it should use for a particular application by looking at IIS metabase settings. A token for the IUSR_MACHINE account is generated and then used to represent the anonymous user. IIS-then passes the token to ASP.NET running on IIS, and its runtime determines the Forms Authentication setup from the application Web.config file. Typically, a Web.config file setup for Forms Authentication would contain:

<!--

    The <authentication> section enables configuration

    of the security authentication mode used by

    ASP.NET to identify an incoming user.

-->

<machineKey validationKey="123FFF8CF7F466FB253AF78C5AFC96A8CC874B2A9F3DF662F0DE3A57A62486FDF253397E3BE61A3098EA7A6543E8407661DD2A5DA009840A7EE2DA5EA99B7F010" decryptionKey="B715956BF9F134BF9E6771E610C237DD1DA49CBE345B2BB1F0413C630D1B5822" validation="SHA1" decryption="AES"/>

<authentication mode="Forms">

<forms

    cookieless="UseCookies"

    loginUrl="http://YourServer/Login/Login.aspx"

    name=".YourWebApp001"

    timeout="20"

    path="/"

    protection="All"

    slidingExpiration="false"

    enableCrossAppRedirects="false">

</forms>

</authentication>

<authorization>

      <deny users="?"/>

</authorization>

<customErrors mode="On" defaultRedirect="~/Errors/DefaultError.aspx">

<!-- Set httpCookies to true to make cookies accessible only by the server.

This is one of the defenses against Cross-Site Scripting attacks (CSS/XSS).

-->

<httpCookies httpOnlyCookies="true"/>

The attribute values used above are explained below:

  • “loginUrl” points to an ASP.NET application's login page. For Internet applications, a developer should put the login page in a folder that requires Secure Sockets Layer (SSL). Securing the login traffic between the user and the IIS server helps ensure the integrity of the client user name and password credentials when they are passed from the browser to the Web server.
  • “protection” is set to ”All” above to denote privacy and integrity for the forms authentication ticket in the cookie. This causes the authentication ticket to be encrypted using the algorithm specified on the ”machineKey” element, and to be signed using the hashing algorithm that is also specified on the ”machineKey” element.
  • ”timeout” is used above to specify a limited lifetime for the forms authentication session. The default value is 30 minutes. If a persistent forms authentication cookie is issued, the timeout attribute is also used to set the lifetime of the persistent cookie (see the Forms Authentication code in the Form_Load event below).
  • ”name” and ”path” are set to the values defined in the application's configuration file.
  • ”requireSSL” is set to ”false”. This configuration means that authentication cookies can be transmitted over channels that are not SSL-encrypted. If session hijacking is a concern, then consider setting ”requireSSL” to ”true”.
  • ”slidingExpiration” is set to ”true” to enforce a sliding session lifetime. This means that the session timeout is periodically reset as long as a user stays active on the site.
  • ”defaultUrl” is set to the Login.aspx page for the application to redirect unauthorized requests for pages with no authentication cookie or login, and to require the user to login again when a session expires.
  • ”cookieless” is set to ”UseCookies” to specify that the application use cookies for all browsers that support cookies. If a browser that does not support cookies accesses the site, then Forms Authentication can put the authentication ticket on the URL only if ”cookieless” is set to ”UseDeviceProfile”.
  • ”enableCrossAppRedirects” is set to ”false” to indicate that forms authentication does not support automatic processing of tickets that are passed between applications on the query string or as part of a form POST. If you use a portal for login of all Web applications, then this attribute must be set to ”true”.

 

IIS defaults anonymous access to “enabled” for all applications that use Forms Authentication. However, ASP.NET can utilize the UrlAuthorizationModule class to help ensure that only authenticated users can access a page. The programmer can configure the UrlAuthorizationModule class properties by using the ”authorization” element as shown above in the Web.config file XML. The above code markup denies access to all anonymous users (deny users = “?”).

The “customErrors” mode set to “on” redirects the user to the error page path setting as show above in the Web.config example XML code. Having a generic error page with user-friendly messages provides the client with helpful information, but this page should not give the unauthorized person any useful hacking tips for accessing application resources.

Internet Explorer has support since version 6 SP1 for a cookie property called “httpOnlyCookies” that can help mitigate cross-site scripting threats that result in stolen cookies. As shown above in the Web.config example, when a cookie that has “httpOnlyCookies” set to true” is received by a compliant browser, it is accessible only to the Web server and inaccessible to client-side scripts.

 

 



5:22 AM GMT  |  Read comments(0)

May 18

What's Wrong with Microsoft's continuous release strategy?
Since Orcas and the other Heros of 2008 new applications, Microsoft now releases CTP, Beta, and other versions of their integrated developement tools at a furious pace. The problem with these continuous new versions of development application wannabes is that you can't uninstall the releases for the RTM products.
 
Recently, I wanted to uninstall Microsoft's SQL Server 2005 Express Edition because I wanted to install the Developer edition. I needed to create a DTS package with the new BI Manament or SSIS tools not in Express Edition. For 2 days, I tried every forum and blog trick out in President Bush's "Internets" (Google it, if you don't catch the numor), but nothing worked. I used their uninstall tool that I found with a Google search that I didn't find with an MSDN search, but that didn't work either. Finally, I used registry cleaner that allow Management Studio and the BI tools to install, but the SSIS integration stuff still had errors trying to install. I went to the BI tools, and lo and behold, VS 2005 pops up without the DTS options per the MSDN blog instructions. I gave up, and I installed a pristine copy of SQL Server 2005 Developer Edition on a new laptop at work. Only this worked!
 
What wrong with this picture? Tools that should save developer time now waste tremendous amouts of frustrating energy better utilized implementing business processes. Microsoft forums and blogs are now fraught with opposing opinions, and only by trial and error can you separate the fool's gold from the real nuggets. Is this the result of a more open Microsoft thanks to the EU lawsuits and mandates? Or is it just poor planning and chaos coming from a company that has just gotten too large to serve developers? I think it's the latter.


8:05 AM GMT  |  Read comments(0)

April 01

Writing Code XML Comments to a File with VS 2008
If you have the following XML comment header in a class, and you wish to write it out to an XML file:
 
//---------------------------------------------------------------------------

/// Namespace: DHS.AppBase

/// Authors: FS/KW/JW

/// Date: 4/22/2006 Finished properties and methods

/// Updated: 4/23/2006 Added comments and header page

/// Updated: 12/25/2006 Added DHS.AppBase

/// Updated: 3/21/2007 Added Javascript Client Code

/// <summary>

/// Purpose: Parent Base Class for specific section page code-behinds.

/// Works in conjuction with ASP.NET pages as parent for code behind page

/// conrollers to contain common properties, methods, cascading

/// styles sheets or skins, and javascript and XML calls to

/// Business web services through the Domain bll.

/// </summary>

///

You could write the XML comments in your project classes by:

1. Go to the properties page in the document.
2. Highlight the Build choice in the left hand menu item list
3. On the very bottom of the Build page, you will see Output.
4. Underneath Output, click on the "XML documentation file" checkbox.
5. You will see a path to the bin folder like: "bin\Release\AppBase.XML"
6. Build (compile) the project.
7. Go to your Solution Explorer and click on the Show All Files button.
8. Look under the bin\Release flolder, and you will see the XML you just created.
9. Double click on it, and you can read the file.

You would end up with the following in an XML file under your bin directory:

<

member name="T:DHS.AppBase.BP">

Namespace: DHS.AppBase

Authors: FS/KW/JW

Date: 4/22/2006 Finished properties and methods

Updated: 4/23/2006 Added comments and header page

Updated: 12/25/2006 Added DHS.AppBase

Updated: 3/21/2007 Added Javascript Client Code

<summary>

Purpose: Parent Base Class for specific section page code-behinds.

Works in conjuction with ASP.NET pages as parent for code behind page

conrollers to contain common properties, methods, cascading

styles sheets or skins, and javascript and XML calls to

Business web services through the Domain bll.

</summary>

</member>

You could the use comments like above in the document to compare against your functional requirements as a User Acceptance Test.

Franc.



4:59 PM GMT  |  Read comments(0)

March 20

Some Suggested Database Standards
These are just a few of my thoughts on SQL Server Database Standards for a few areas:
 

Table Names

·         The table name must clearly identify the collection of data the table represents or contains: e.g., UserAddresses.

·         Table names are the only database objects not prefixed since they often represent objects. Most standards call for tables to be plural like a collection of row objects similar to instances of a class: e.g., Clients, Appeals. However, this guideline is not static, and the DBA can make exceptions.

·         Only the first letter of each whole word in a table name should be capitalized: e.g., LegalContacts. ProviderAddresses.

·         Functional table names (for many-to-many relationships) should be a combination of the two tables referenced. For example, cross-reference tables for the many-to many relationships between Clients and Appeals: ClientsAppeals.

·         Lookup tables should be grouped together. They can be grouped at the beginning by the prefix ‘lkup_’;  e.g., lkup_NamePrefixes.

Column Names

 

·         Each column name must be derived from the business name identified during the business/data analysis process. If the column was not identified during the analysis of business data, it must still be given a spelled-out logical name; e.g., ClientFirstName, ClientMiddleInitial, ClientPhoneAreaCode, etc.

·         Column names should be descriptive and clearly understandable by someone who looks at them for the first time.

·         Each word in a column name is capitalized, and, for the sake of clarity, but they can be separated by an underscore if it clarifies the name: e.g.,

·         Column names should contain letters, numbers, and underscore characters (“_”) only: e.g., ClientAppealHistory_2004.

·         If multiple fields in a table reference the same primary key in another table, the field names should be followed by a suffix. (Examples: fk_BankID_From and fk_BankID_To)

·         Column names should be unique throughout the database, if possible. The DBA can make exceptions to this guideline.

·         The primary key Column should always end with “ID” and begin with “pk_”. If the Column is not auto-numbered, all relationships to that Column must have referential integrity when cascading an update.

·         A Column referencing a primary key Column in another table should have exactly the same name as the Column it is referencing from the reference table plus “fk_” as a prefix to denote foreign key. E.g.; fk_ClientID.

·         Column names should not be abbreviated. (An exception can be made by the DBA if the Column name is too long to be understood)

Stored Procedures

 

·         All application level and user defined stored procedures are prefixed with the constant 'usp_' with a description of what the stored procedure does. Example: usp_GetLastModifiedDate.

·         Inserts, Updates, and Deletes that affect table rows should be named with the prefix usp_ plus the the table name, and Insert plus Update, or Delete. E.g.; usp_Appeals_Insert.

·         Actual table Deletes must be approved by the DBA since deletes are in reality only Inserts that make a table row inactive by modifying a row column.

·         Use upper case for all SQL keywords: e.g., SELECT, INSERT, UPDATE, WHERE, AND, OR, LIKE, etc.

·         Liberally comment code blocks not easily understood by non-technical persons.Use single-line comment markers (--) for comments and headers.

·         Reserve multi-line comments (/*.. ..*/) for blocking out sections of SQL code.

·         Indent the SQL code 4 spaces per line to improve readability when code blocks change: e.g., see Figure 1.

·         Use single quote characters to delimit strings.

·         Use parentheses to increase readability or to force order of execution: e.g., WHERE (WidgetColor=’red’ AND ((WidgetSize = 1) OR (Size = 2)).

·         Use BEGIN…..END blocks when multiple statements are present within a conditional code segment: e.g., see Figure 1.

·         Use one blank line to separate code sections: e.g., see Figure 1.

·         Use spaces so that expressions read like sentences;

e.g., fillfactor = 25, not fillfactor=25.

·         Place SET statements before any executing code in the procedure.

·         Use SET NOCOUNT ON at the beginning of stored procedures: e.g., see Figure 1.

·         Tables, views, and stored procedures are always fully qualified to ensure SQL Server follows a direct path to find it rather than searching through the master catalog.

·         Do not call functions repeatedly within your stored procedures, triggers, functions and batches. For example, you might need the length of a string variable in many places of your procedure, but don't call the LEN function whenever it's needed. Instead, call the LEN function once, and store the result in a variable for later use.

Code Readability

 

·         Write comments in your stored procedures, triggers and SQL batches generously; especially when the meaning of the code logic is not obvious. This helps other programmers and DBAs understand the SQL code.

·         Do not use column numbers in the ORDER BY clause. In the following examples, note that the second query is more readable than the first. 

Example 1:

SELECT OrderID, OrderDate FROM Orders ORDER BY 2

Example 2:

SELECT OrderID, OrderDate FROM Orders ORDER BY OrderDate.

·         Use the more readable ANSI-Standard Join clauses instead of the old style joins. With ANSI joins, the WHERE clause is used only for filtering data. With older style joins, the WHERE clause handles both the join condition and filtering data. The first of the following two examples shows the old style join syntax, while the second one shows the new ANSI join syntax. 

Example 1:

SELECT a.au_id, t.title FROM titles t, authors a, titleauthor ta

WHERE a.au_id = ta.au_id AND ta.title_id = t.title_id AND

t.title LIKE 'Computer%'

 

Example 2:

SELECT a.au_id, t.title FROM authors a INNER JOIN titleauthor ta ON     a.au_id = ta.au_id

INNER JOIN titles t ON ta.title_id = t.title_id

WHERE t.title LIKE 'Computer%'  

 

·         Do not use SELECT *; always list all fields by name in the SELECT statement.

·         Avoid unnecessary use of temporary tables; use 'Derived tables' or CTE (Common Table Expressions) wherever possible, as they perform better.

·         To make SQL statements more readable, start each clause on a new line and indent when needed.

·         Do not use GOTO as this is bad coding by any standard.

·         Check the global variable @@ERROR immediately after executing a data manipulation statement (like INSERT/UPDATE), so that you can rollback the transaction if an error occurs. However, make sure that you assign @@ERROR to a variable before using an IF statement since this will cause @@ERROR to reset, e.g.;

 

USE [dbo].[Test]

GO

 

SET ANSI_NULLS ON

GO

 

SET QUOTED_IDENTIFIER ON

GO

 

       -- =============================================

--  Author:             Franc Stratton

       -- Create date:     11/26/2006

       -- Description:      Adds a user login row to table

       -- =============================================

 

CREATE PROCEDURE [dbo].[Test].[usp_LoginInsert]

 

       -- parameters for the stored procedure

@UserID char(7) = 0

AS

DECLARE @Error int -- local error holder

 

       -- SET NOCOUNT ON added to prevent extra result sets from

       -- interfering with SELECT statements.

SET NOCOUNT ON;

 

BEGIN TRAN

    INSERT INTO Login( UserID,  LoginDateTime) values ( @UserID,  GetDate())

    --Assign @@ERROR to a declared @Error integer variable.

 

    SELECT @Error = @@ERROR

  

    --Check for ERROR with an IF, if no ERROR return row IDENTITY integer

           --ELSE ROLLBACK & return -1 to signify that ERROR prevented INSERT

    IF (@Error = 0)

        BEGIN

            COMMIT TRAN

            SELECT SCOPE_IDENTITY() as 'IDENTITY'

        END

    ELSE

        BEGIN

            ROLLBACK TRAN

            SELECT -1 as 'IDENTITY'

        END

            Figure 1: A Stored Procedure Example.

 

·         Always use a column list in your INSERT, UPDATE, or SELECT statements. This helps avoid problems when the table structure changes (like adding or dropping a column).

·         When executing an UPDATE or DELETE statement, use the primary key in the WHERE condition, if possible. This reduces error possibilities.

·         Avoid wild-card characters at the beginning of a word while searching using the LIKE keyword since this can result in an index scan and defeat the purpose of the index.

 

Keys, Indexes, Triggers, Views, and Constraints

·         Names of all database objects and constraints should all be prefixed with a lower case identifier

 

Object Type

Example

Views

vw_TableName  or vw_FirstLastNames (If it is combination of more than one table)

Stored Procedures

usp_ProcedureName

Primary Keys

pk_TableName

Foreign Keys

fk_ForeignKeyName

Index

ix_TableName_ColumnName (unless it’s too long, and DBA allows abbreviations for clarity)

 

·         Triggers are named to indicate the table they are for and the type of trigger. The purpose of the trigger is identified in the prefix to the name. All triggers should be prefixed with the letters 'tr_', letters designating the type, an underscore, and the table name. The type should be designated as 'Insert' = insert, 'Update' = update, 'Delete' = delete. Example tr_Products_Insert (Insert trigger for Products Table)



6:18 PM GMT  |  Read comments(0)

March 19

Passing Information Across the Web To Services
My how Microsoft changes the .NET focus on technology! With Visual Studio 2003 and 2005, the .NET MVPs, blogs, MSDN, forums, etc. touted typed and untyped datasets as the preferred container for data passed between Web applications and Web services, whether Intranet or Internet. After many bouts and bad losses in European Union decision concerning Microsoft's proprietary propensity and secrecy, all of a sudden, the software giant now embraces open standards. Thank you, EU!
 
Generally, generics, arrays, simple datatypes (e.g., strings, integers, etc.), and class objects are preferred over typed or untyped datasets when transferring data between layers, methods, and services in Windows or Web form applications. Since most .NET application development&nbsp;is in a transitional state moving from OOP MVC to an SOA architecture, the typed and untyped dataset is too bloated and proprietary for service communication objects in large applications with many users such as ARTS.
 
Datasets always serialize themselves as XML, which have bandwidth and performance implications when passed across service communication boundaries. The alternative to using datasets is to create custom object types that represent business entities, and custom collection types to contain them. Even though this route may result in more code, the bandwidth requirements are much lower since serialized objects are not nearly as verbose as datasets. Custom objects have the advantage of allowing explicit and complete control over the way the type is designed and what its internal capabilities are.
 
When designing a layered system the programmer must consider factors such as the ability to cascade changes (such as new data schemas) through layers and the amount of overhead involved when data is moved from one layer to the next. Plus the programmer will need a business tier to execute business logic, and he or she will need a DAL to provide Create/Read/Update/Delete (CRUD) functions to the rest of the system. Using custom business objects results in a far more elegant and neat solution where data is perfectly modeled on the domain of the problem. Maintenance and extensions are greatly facilitated, readability is assured, and technical documentation is easier with generics, arrays, simple datatypes, and especially class objects.
 
Be sure that classes passed as parameters, however, don't have methods that bloat them when serialized. This is one are where LINQ to SQL can help. With LINQ to SQL, you can map classes to tables either with attributes or an XML file.  Visual Studio 2008 creates mapped table classes for you automatically. More on this later.


3:55 PM GMT  |  Read comments(0)

March 17

Is Windows Workflow with Framework 3.5 Ready for Prime Time?
This .NET reviewer/architect/programmer determined that high level and low level business process diagrams produced during the requirements and functional phases of the software development life cycle respectively will visually represent work necessary to accomplish tasks imbedded in business service model layers. Microsoft Windows Workflow Foundation (WF) makes an ideal repository for this business logic since it resembles the flow diagrams.

However, the status of workflows for Microsoft is currently in transition between Framework 3.0 and 3.5. Framework 3.0 workflows are extremely complicated, and communications with a workflow host (e.g., WinForms, Webforms, or WCF services) and a client have dramatically changed with Framework 3.5. Unfortunately, even Microsoft MVPs admit that very little is known about the new methodology for local and remote communications and with the WF/WCF integrated send and receive activities.

Currently, Microsoft has no consistent model for the amazing technology among the workflow implementations in BizTalk (Framework 3.0), SharePoint 3.0 (Framework 3.0), and Visual Studio 2008 (Framework 3.5). Until there is a unified, consistent, and well-documented paradigm for incorporating workflows into applications, this reviewer will not implement workflows to a large extent in production applications, but sparingly will use workflows in a minor way to provide experience only.

At some point, business analysts should be able to understand the drag-and-drop, visual part of workflows, and  programmers will just supply the code to implement activity functionality. The business analysts (with a little technical expertise) should then be able to design a visual layout of business logic much like analysts are now providing in detailed business flow diagrams.
 
See My Website for more details at http://www.francstratton.com/


4:33 PM GMT  |  Read comments(0)

November 26

Talking about Microsoft Workflows

 

Quote

Microsoft Workflows

Software applies knowledge to the manipulation data while implementing business processes and rules. The knowledge imbedded in software by developers is generally a combination of procedural and declarative knowledge. Procedural knowledge is the step-by-step activities necessary to complete a business objective or “how to” perform a task.

 

Declarative knowledge, on the other hand, is about the relationships and rules that exist among data and objects implemented in an application to achieve a business process goal. For example, a piece of declarative knowledge would be that plane reservations made at least 21 days in advance receive a 20% discount, unless the cost of the air fare is less than $100. The date and the price share a relationship and can affect each other. Expressing this type of knowledge using a general-purpose programming language isn't difficult on a small scale, but becomes more difficult as the amount of knowledge grows. In the past, programmers transformed the knowledge into procedural code using if-then-else statements. Many software applications require an enormous amount of declarative knowledge that should be, but most often is not, imbedded in the business logic layers or workflows. Some business analysts refer to declarative knowledge as "business rules" that control the logic flow in business processes.

 

Encoding business rules into procedural code in Windows or ASP.NET form code-behind pages makes this form of declarative knowledge harder to find, understand, and modify. Over the past decade, the software industry has developed tools and models for working with declarative knowledge. The IT industry categorizes these tools as rules engines, inference engines, and logic machines. A rules engine specializes in making declarative knowledge easier to implement, process, isolate, and modify. Windows Workflow Foundation (WF) provides such a framework to model, implement, and execute business processes and logic with a built-in rules engine.Since both declarative and procedural activity types are available, Windows Workflow offers the best of both worlds. Developers can use Sequence activities to implement procedural knowledge, and Condition, Policy, or If-Then rule activities to execute declarative knowledge.

 

 



4:33 AM GMT  |  Read comments(0)