Marco Bellinaso's Blog

 Tuesday, May 09, 2006


You can create page-level resources by creating resource files just as you do for global resources, but placing them under a folder named App_LocalResources (as opposed to App_GlobalResources used for global resources) located at the same level of the page to localize. For example, if the page is in the root folder, then you’ll create the App_LocalResources under the root folder, but if the page is under /Test, then you’ll create a /Test/App_LocalResources folder. This means you can have multiple App_LocalResources folders, whereas you can only have one App_GlobalResources folder for the whole site. The name of the resource filename is also fundamental, as it must be named after the page or control file for which it contains the localized resources, plus the part with the culture name: For example, a culture-neutral resource file for Localization.aspx would be named Localization.aspx.resx, whereas it would be named Localization.aspx.it-IT.resx for the Italian resources. In Figure 11-5, you can see the organization of files in the Solution Explorer, and the resource file being edited in the grid editor.

 

Figure 11-5

You can still use the Expressions dialog shown in Figure 11-4 to bind a control’s property to an expression pointing to a localized resource: When you point to a page-specific resource you just leave the ClassKey textbox empty. The code below shows the generated expression:

<asp:Label ID="lblCopyright" runat="server"

   Text="<%$ Resources:CopyrightMessage %>" / >

It differs from the expression shown in the previous section, as it doesn’t include the class name; it just specifies the resource key. If you want to access local resources programmatically, you use the page’s GetLocalResourceObject method, which takes the resource key name and returns an Object that you must cast to string or to the proper destination type (such as Bitmap if you stored an image):

string copyrightMsg = (string)this.GetLocalResourceObject("CopyrightMessage");

Even with localization expressions and local resources, localizing full pages will be a slow task if you need to create the expressions for dozens of controls, and things get worse if you need to localize multiple properties for the same control, such as Text, ToolTip, ImageUrl, NavigateUrl, and so on, which is often the case. To speed things up, Visual Studio offers the Tools @@> Generate Local Resource command, which generates a local resource file for the current page, and creates entries for all localizable properties of all controls on the page, following the ControlName.PropertyName naming convention for the names of the resources. Resource items are also automatically set with the value extracted from the page’s markup; if a property is not used in the control’s declaration, a resource item for it is generated anyway and left empty.

Localizable properties are those that are decorated with a [Localizable(true)] attribute, which you can add to the properties of your custom controls. However, even when you add it to properties of user controls, resources for those properties will not be automatically generated by the Generate Local Resource command. You can create the local resources for those properties yourself, and write the localization expressions to make the association: Everything will work perfectly at runtime, so this is only a design-time limitation. In addition, you’ll have to write the localization expressions manually, because the (Expressions) item is not available from the Properties window when a user control is selected.

Figure 11-6 shows the resource editor for the Localization.aspx.resx local resource file after executing this Generate Local Resource command on the test page.

Figure 11-6

Besides the automatic generation of the resource file (or the addition of the resource items, if a resource file for that page was already present in the proper folder), what’s even more interesting is that each control’s declaration is modified as follows (note the code in bold):

<asp:Label ID="lblTitle" runat="server" Font-Size="X-Large" ForeColor="#C00000"

   meta:resourcekey="lblTitleResource1" Text="Localization Demo"

   Text="This page provides a nice demo of new ASP.NET 2.0 localization features"

/>

A meta:resourcekey attribute is added to the declaration, and is set to the prefix used in the local resource file to identify all localized properties of that control, such as lblTitleResource1.Text and lblTitleResource1.ToolTip. These are called implicit localization expressions (expressions used earlier are considered explicit). At runtime, the framework parses all resources and applies them to the properties of the corresponding controls, making the mapping of the first part of the key with the value of meta:resourcename. This means that the control’s declaration is decorated with just a single new attribute, but may make multiple properties localizable. Later, if you want to localize a property that you didn’t take into account originally, you just need to go to the resource editor and add an item following the naming schema described above, or edit its value if it already exists.

Note that the controls retain their original property declarations after running the Generate Local Resource command. These declarations are no longer necessary, though, as the property’s value will be replaced at runtime with the values saved in the resource file; therefore, you can completely remove the definition of the Text, ToolTip, and the other localized properties from the .aspx files to avoid confusion.

This behavior works with the page’s title as well, originally defined in the @Page directive, which is modified as follows:

<%@ Page Language="C#" meta:resourcekey="PageResource1" …other attributes… %>


 NOTE: This excerpt was taken from the book "ASP.NET 2.0 Website Programming". Click here to find more about it, and download the complete source code of the sample project.

5/9/2006 7:29:18 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [4]  | 
 Monday, May 01, 2006


As mentioned earlier, a Web Part can be developed as a user control (which is like a partial page, with markup code and a code-behind) or a custom control (which is a class written in C# for which you create the output 100% programmatically). The choice between the two depends on your needs and requirements, and it’s much like the choice between writing user and custom controls in general. If you want to compile everything into an assembly—so that the source code is protected and the output cannot be modified by an external developer and shared among multiple web sites by installing it in the GAC—then you’ll want to go with custom controls. If, instead, you don’t care much about those aspects, but prefer simplicity and speed of development, and the ease of changing the appearance of the Web Part by working with markup code instead of with C# code, then user controls will be your best bet. In this section, I’ll provide a quick overview of both approaches.

Building a Web Part as a User Control

For our first example we’ll build a simple online calculator, with two textboxes for the operands, a button to submit the form and do the calculation, and a label to show the result. You define the UI with the usual markup code, in the .ascx file (typical of user controls):

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Calculator.ascx.cs"

   Inherits=" Calculator" %>

Op1: <asp:TextBox ID="txtOp1" runat="server" /><br />

Op2: <asp:TextBox ID="txtOp2" runat="server" /><br />

<asp:Button ID="btnCalc" runat="server" Text="Calculate"

   OnClick="btnCalc_Click" /><br />

<asp:Label ID="lblResult" runat="server" />

The code-behind needs a property that specifies the type of operation to perform: addition, subtraction, division, or multiplication. The property is named Operation, and is of type OperationType, an enumeration that contains the values indicated above. Then, when the button is clicked, you simply retrieve the input strings, convert them to integers, perform the specified operation, and show the result (I’m not showing the type checks and other validations in order to keep this simple):

public enum OperationType : int

{

   Addition,

   Subtraction,

   Division,

   Multiplication

}

 

public partial class Calculator : System.Web.UI.UserControl

{

   private OperationType _operation = OperationType.Addition;

   public OperationType Operation

   {

      get { return _operation; }

      set { _operation = value; }

   }

 

   protected void btnCalc_Click(object sender, EventArgs e)

   {

      int op1 = Convert.ToInt32(txtOp1.Text);

      int op2 = Convert.ToInt32(txtOp2.Text);

 

      if (this.Operation == OperationType.Addition)

         lblResult.Text = (op1 + op2).ToString();

      else if (this.Operation == OperationType.Subtraction)

         lblResult.Text = (op1 - op2).ToString();

      if (this.Operation == OperationType.Division)

         lblResult.Text = ((double)op1 / (double)op2).ToString();

      else

         lblResult.Text = (op1 * op2).ToString();

   }

}

So far this is a 100% standard user control. Even so, it can be used as a Web Part on a page. Actually, any control can be used as a Web Part: ASP.NET will take care of wrapping it into a GenericWebPart at runtime. However, to enable users to personalize it by setting the properties, you need to add some attributes to the public properties that you want to make editable at runtime. In particular, the WebBrowsable attribute specifies that the property will be visible in the Web Part’s Editor box; the Personalizable attribute specifies whether the property is editable (either for the shared view or also at the user level in the personal view); the WebDisplayName specifies the property title in the editor box, so that it’s more understandable and user friendly; and the WebDescription attribute is a longer description of the property. In this example, you would decorate the Operation property with attributes as follows:

private OperationType _operation = OperationType.Addition;

[Personalizable(PersonalizationScope.User),

WebBrowsable,

WebDisplayName("Operation Type"),

WebDescription("The type of operation performed when submit is clicked.")]

public OperationType Operation

{

   get { return _operation; }

   set { _operation = value; }

}

Because this is just a normal user control that ASP.NET will wrap with GenericWebPart, it doesn’t specify Web Part–specific attributes such as the title that should be listed on the Web Parts catalog, or an icon. You can add those attributes to a user control by implementing the IWebPart interface, which defines properties with self-descriptive names such as Title, Description, TitleIconImageUrl, CatalogIconImage, and TitleUrl. You implement these properties as simple wrappers for private fields, as follows:

public partial class Controls_Calculator : System.Web.UI.UserControl, IWebPart

{

   private string _catalogIconImageUrl = "";

   public string CatalogIconImageUrl

   {

      get { return _catalogIconImageUrl; }

      set { _catalogIconImageUrl = value; }

   }

 

   private string _description = "";

   public string Description

   {

      get { return _description; }

      set { _description = value; }

   }

 

   protected string _subTitle = "";

   public string Subtitle

   {

      get { return _subTitle; }

      set { _subTitle = value; }

   }

 

   protected string _title = "Online Calculator";

   public string Title

   {

      get { return _title; }

      set { _title = value; }

   }

 

   private string _titleIconImageUrl = "";

   public string TitleIconImageUrl

   {

      get { return _titleIconImageUrl; }

      set { _titleIconImageUrl = value; }

   }

 

   private string _titleUrl = "";

   public string TitleUrl

   {

      get { return _titleUrl; }

      set { _titleUrl = value; }

   }

 

   // ...the rest of the class as shown earlier...

}

Note that the _title field is initialized with “Online Calculator”, which is the string that will be shown in the Web Part catalog to refer to this Web Part, and on the title bar of the Web Part itself when added on the page. You’ll learn how to build and use the catalog shortly.


 NOTE: This excerpt was taken from the book "ASP.NET 2.0 Website Programming". Click here to find more about it, and download the complete source code of the sample project.

5/1/2006 12:11:23 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [1]  | 
 Saturday, April 22, 2006


The System.Net.Mail namespace defined in the System.dll assembly contains all the classes used to send e-mails. The older System.Web.Mail namespace, and its related classes, that were used with ASP.NET 1.x are still there, but its use has been deprecated now in favor of these new classes in ASP.NET 2.0 that provide more features. The principal classes are MailMessage, which represents an e-mail message, and the SmtpClient class, which provides the methods used to send a MailMessage by connecting to a configured SMTP server (SMTP is the Simple Mail Transfer Protocol, which is the low-level protocol used by Microsoft Exchange and other mail servers).

MailMessage fully describes an e-mail message, with its subject, body (in plain-text, HTML, or in both formats), the To, CC, and BCC addresses, and any attachments that might be used. The simplest way to create an e-mail is using the MailMessage constructor, which takes the sender’s address, the recipient’s address, the mail’s subject, and the body, as shown below:

MailMessage mail = new MailMessage(

   "from@somewhere.com", "to@somewhere.com", "subject", "body");

However, this approach will be too limited in most cases, because you may want to specify the sender’s display name in addition to his e-mail address (the display name is what is displayed by the mail client, if present, instead of the address, and makes the mail and its sender look more professional). You may also want to send to more than one recipient, use an a HTML body (as an alternative, or in addition, to the plain-text version), include some attachments, use a different encoding, modify the mail’s priority, and so on. All these settings, and more, are specified by means of a number of instance properties of the MailMessage class. Their names should be self-explanatory, and some examples include the following: Subject, Body, IsBodyHtml, From, To, CC, Bcc, BodyEncoding, Attachments, AlternateViews, Headers, Priority, and ReplyTo. The class’ constructor enables you to specify a From property of type MailAddress, Address, and UserName properties. The To, CC, and Bcc properties are of type MailAddressCollection, and thus can accept multiple MailAddress instances (you can add them by means of the collection’s Add method). Similarly, the MailMessage’s Attachments property is of type AttachmentCollection, a collection of Attachment instances that point to files located on the server. The following example shows how to build a HTML-formatted e-mail message that will be sent to multiple recipients, with high priority, and that includes a couple of attachments:

// create the message

MailMessage mail = new MailMessage();

// set the sender's address and display name

mail.From = new MailAddress("mbellinaso@wrox.com", "Marco Bellinaso");

// add a first recipient by specifying only her address

mail.To.Add("john@wroxfans.com");

// add a second recipient by specifying her address and display name

mail.To.Add(new MailAddress("anne@wroxfans.com", "Anne Gentle"));

// add a third recipient, but to the CC field this time

mail.CC.Add("mike@wroxfans.com");

// set the mail's subject and HTML body

mail.Subject = "Sample Mail";

mail.Body = "Hello, <b>my friend</b>!<br />How are you?";

mail.IsBodyHtml = true;

// set the mail’s priority to high

mail.Priority = MailPriority.High;

// add a couple of attachments

mail.Attachments.Add(

   new Attachment(@"c:\demo.zip", MediaTypeNames.Application.Octet));

mail.Attachments.Add(

   new Attachment(@"c:\report.xls", MediaTypeNames.Application.Octet));

If you also wanted to provide a plain-text version of the body in the same mail, so that the display format (plain text or HTML) would depend on the user’s e-mail client settings, you would add the following lines:

string body = "Hello, my friend!\nHow are you?";

AlternateView plainView = new AlternateView(body, MediaTypeNames.Text.Plain);

mail.AlternateViews.Add(plainView);

Once a MailMessage object is ready, the e-mail message it describes can be sent out by means of the Send method of the SmtpClient class, as shown here:

SmtpClient smtpClient = new SmtpClient();

smtpClient.Send(mail);

Before calling the Send method, you may need to set some configuration settings, such as the SMTP server’s address (the SmtpClient’s Host property), port (the Port property) and its credentials (the Credentials property), whether the connection in encrypted with SSL (the EnableSsl property), and the timeout in milliseconds for sending the mail (the Timeout property, which defaults to 100 seconds). An important property is DeliveryMethod, which defines how the mail message is delivered. It’s of type SmtpDeliveryMethod, an enumeration with the following values:

  • Network: The e-mail is sent through a direct connection to the specified SMTP server.
  • PickupDirectoryFromIis: The e-mail message is prepared and the EML file is saved into the default directory from which IIS picks up queued e-mails to send. By default this is <drive>:\Inetpub\mailroot\Queue.
  • SpecifiedPickupDirectory: The EML file with the mail being sent is saved into the location specified by the PickupDirectoryLocation property of the smtpClient object. This is useful when you have an external custom program that picks up e-mails from that folder and processes them.

The delivery method you choose can dramatically change the performance of your site when sending many e-mails, and can produce different errors during the send operation. If you select the Network delivery method, the SmtpClient class takes care of sending the mail directly, and raises an error if the destination e-mail address is not found or if there are other transmission problems. With the other two methods, instead of sending the message directly, an EML mail file is prepared and saved to the file system, where another application (IIS or something else) will pick them up later for the actual delivery (a queue accumulates the messages, which means the web application will not have to wait for each message to be sent over the Internet). However, when using the second and third delivery methods, your web application cannot be notified of any errors that may occur during transmission of the message, and it will be up to IIS (or another mail agent that might be used) to handle them. In general, the PickupDirectoryFromIis method is the preferred one, unless your ASP.NET application is not given the right to write to IIS mail folders (check with your web hosting provider service if you don’t use your own servers).

If you set all SmtpClient properties mentioned above directly in your C# code, you’ll have to recompile the application or edit the source file every time you want to change any of these settings. This, of course, is not an option if you’re selling a packaged application, or you want to let the administrator change these settings on his own without directly involving you. As an alternative to hard-coding the delivery method, you can set it declaratively in the web.config file, which now supports a new configuration section named <mailSettings>, located under <system.net>, which allows you to specify delivery settings. The SmtpClient class automatically loads those settings from web.config to configure itself at runtime, so you should generally not set your delivery and SMTP options directly in your C# code. Following is an extract of the configuration file that shows how to select PickupDirectoryFromIis as the delivery method, set up the sender’s e-mail address and the SMTP server’s name (or IP address) and port, and specify that you want to use the default credentials to connect to the server:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

   <system.net>

      <mailSettings>

         <smtp deliveryMethod="PickupDirectoryFromIis"

            from="mbellinaso@wrox.com">

            <network defaultCredentials="true"

               host="vmwin2003" port="25"></network>

         </smtp>

      </mailSettings>

   </system.net>

   <!-- other configuration sections... -->

</configuration>

The SmtpClient’s Send method used in the preceding code snippet sends the e-mail synchronously, which means that the task must complete before the execution of your application can resume. The term synchronous means “do what I asked, and I’ll stop and wait for you to finish,” and the term asynchronous means “do what I asked, but let me continue doing other work, and you should notify me when you’re done.” The SmtpClient class also has a SendAsync method to send the mail asynchronously. It returns immediately, and the e-mail is prepared and sent out on a separate thread. When the send task is complete, the SmtpClient’s SendCompleted event is raised. This event is also raised in case of errors, and the Error and Cancelled properties of its second argument (of type AsyncCompletedEventArgs) tells you whether it was raised because the send was cancelled, because there was an error, or because the send completed successfully. Here’s a sample snippet that shows how to send the mail asynchronously, and handle the resulting completion event:

SmtpClient smtpClient = new SmtpClient();

smtpClient.SendCompleted += new SendCompletedEventHandler(MailSendCompleted);

smtpClient.SendAsync(message, null);

...

 

public static void MailSendCompleted(object sender, AsyncCompletedEventArgs e)

{

   if (e.Cancelled)

      Trace.Write("Send canceled.");

   if (e.Error != null)

      Trace.Write(e.Error.ToString());

   else

      Trace.Write("Message sent.");

}

An asynchronous send operation can be cancelled before completion by calling the SmtpClient’s SendAsyncCancel method. Note that you can’t send a second e-mail while a SmtpClient has another send in progress; if you try to do so, you’ll receive an InvalidOperationException.



NOTE: This excerpt was taken from the book "ASP.NET 2.0 Website Programming". Click here to find more about it, and download the complete source code of the sample project.

4/22/2006 9:29:13 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 


The more meaningful and short a URL can be, the better. Those URLs are easier to communicate to people, and easier for them to remember. The current URLs that allow us to browse articles for a specific category are already pretty short, but it’s not easy to remember all category IDs by heart . . . and thus to remember the URL. ASP.NET 2.0 introduces a new section in web.config that allows us to map a virtual URL to a real URL: This section’s name is urlMapping, and it’s located under <system.web>. When the user types the virtual URL, the page located at the corresponding real URL will be loaded. Here’s what you can write in web.config to make your sample categories easier to reach:

<urlMappings>

   <add url="~/articles/beer.aspx" mappedUrl="~/BrowseArticles.aspx?CatID=28" />

   <add url="~/articles/events.aspx" mappedUrl="~/BrowseArticles.aspx?CatID=41" />

   <add url="~/articles/news.aspx" mappedUrl="~/BrowseArticles.aspx?CatID=31" />

   <add url="~/articles/photos.aspx" mappedUrl="~/BrowseArticles.aspx?CatID=40" />

   <add url="~/articles/blog.aspx" mappedUrl="~/BrowseArticles.aspx?CatID=29" />

   <add url="~/articles/faq.aspx" mappedUrl="~/BrowseArticles.aspx?CatID=42" />

</urlMappings>

Note that when the server receives a request for the virtual URL it doesn’t make a normal redirect to the real URL. Before the page is loaded, ASP.NET rewrites the URL to the request’s context. From that point on, the URL will be read from the context, and thus the correct URL will be retrieved. The bottom line is that the transition is done in memory on the server side, and users do not even see the real URL in their browser. Therefore, if a user maneuvers to ~/articles/beer.aspx she’s really going to get ~/BrowseArticles.aspx?CatID=28. Furthermore, there is no physical page named ~/articles/beer.aspx.

This could have been done in ASP.NET 1.1, but you had to write your own custom HTTP module to do that, from which you would have used the current HttpContext’s RewritePath method to do the in-memory redirect.



NOTE: This excerpt was taken from the book "ASP.NET 2.0 Website Programming". Click here to find more about it, and download the complete source code of the sample project.

4/22/2006 9:25:01 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, April 13, 2006


In the ASP.NET 1.x days, if you wanted to associate a profile to a registered user, you typically added a custom table to your database, or stored them together with the user credentials, in the same table. You also had to write quite a lot of code for the business and data access layers, to store, retrieve, and update that data from your web pages. ASP.NET 2.0 provides a built-in mechanism to manage user profiles, in an easy, yet very complete and flexible, way. This new feature can save you hours or even days of work! The Profile module takes care of everything—you just need to configure what the profile will contain, i.e., define the property name, type, and default value. This configuration is done in the root web.config file, within the <profile> section. The following snippet shows how to declare two properties, FavoriteTheme of type String, and BirthDate of type DateTime:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

   <system.web>

      <profile>

         <properties>

            <add name="FavoriteTheme" type="String" />

            <add name="BirthDate" type="DateTime" />

         </properties>

      </profile>

   <!-- other settings... -->

   </system.web>

</configuration>

Amazingly, this is all you need to do to set up a profile structure! When the application is run, the ASP.NET runtime dynamically adds a Profile property to the Page class, which means you will not find such a property in the Object Browser at design time. The object returned is of type ProfileCommon (inherited from System.Web.Profile.ProfileBase); you will not find this class in the Object Browser either, or on the documentation, because this class is generated and compiled on-the-fly, according to the properties defined in the web.config file. The result is that you can just access the page’s Profile property and read/write its subproperties. The following code demonstrates how to read the values of the current user’s profile to show them on the page when it loads, and then updates them when a Submit button is clicked:

protected void Page_Load(object sender, EventArgs e)

{

   if (!this.IsPostBack)

   {

      ddlThemes.SelectedValue = this.Profile.FavoriteTheme;

      txtBirthDate.Text = this.Profile.BirthDate.ToShortDateString();

   }

}

 

protected void btnSubmit_Click(object sender, EventArgs e)

{

   this.Profile.FavoriteTheme = ddlThemes.SelectedValue;

   this.Profile.BirthDate = DateTime.Parse(txtBirthDate.Text);

}

Even though you can’t see these properties in the Object Browser, Visual Studio .NET is smart enough to compile this class in the background when the web.config file is modified, so you get full IntelliSense in the IDE, just as if the Profile properties were built-in properties of the Page class, like all the others. Figure 4-11 is a screenshot of the IDE with the IntelliSense in action.

 

Figure 4-11

Having a class dynamically generated by Visual Studio 2005 with all the custom profile properties (and the IntelliSense for them) doesn’t just speed up development, but also helps developers reduce inadvertent coding errors. In fact, this class provides strongly typed access to the user’s profile, so if you try to assign a string or an integer to a property that expects a date, you’ll get a compile-time error so you can correct the problem immediately!

When you define a profile property, you can also assign a default value to it, by means of the defaultValue attribute:

<add name="FavoriteTheme" type="String" defaultValue="Colorful" />

The default value for strings is an empty string, not null, as you may have thought. This makes it easier to read string properties, because you don’t have to check whether they are null before using the value somewhere. The other data types have the same default values that a variable of the same type would have (e.g., zero for integers).

When you declare profile properties, you can also group them into subsections, as shown below:

<profile>

   <properties>

      <add name="FavoriteTheme" type="String" />

      <add name="BirthDate" type="DateTime" />

      <group name="Address">

         <add name="Street" type="String" />

         <add name="City" type="String" />

      </group>           

   </properties>

</profile>

The Street property will be accessible as Profile.Address.Street. Note, however, that you can’t define nested groups under each other, but can only have a single level of groups. If this limitation is not acceptable to you, you can define your own custom class with subcollections and properties, and reference it in the type attribute of a new property. In fact, you are not limited to base types for profile properties; you can also reference more complex classes (such as ArrayList or Color), and your own enumerations, structures, and classes, as long as they are serializable into a binary or XML format (the format is dictated by the property’s serializeAs attribute).

The Profile system is built upon the provider model design pattern. ASP.NET 2.0 comes with a single built-in profile provider that uses a SQL Server database as a backing store. However, as usual, you can built your own providers or find them from third parties.



NOTE: This excerpt was taken from the book "ASP.NET 2.0 Website Programming". Click here to find more about it, and download the complete source code of the sample project.

4/13/2006 8:07:04 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, April 07, 2006


A very important issue you must tackle when designing a business layer is how you plan to manage transactions. Many business methods call multiple DAL methods internally to update, insert, or delete multiple records, potentially in multiple tables. You must ensure that multiple calls run within a transaction, so that if one fails, all actions performed by previous methods are rolled back. If you don’t do this, you’ll end up having inconsistent, and wrong, data. Managing transactions would be complicated if you had to do everything yourself, but fortunately there are several technologies and frameworks that can do the plumbing for you.

ADO.NET Transactions

In the simplest case you can use explicit ADO.NET client-side transactions. You should already be familiar with them, but here’s some sample code that runs a couple of commands inside a transaction to refresh your memory:

using (SqlConnection cn = new SqlConnection(connString))

{

   cn.Open();

   SqlTransaction tran = cn.BeginTransaction();

  

   try

   {

      SqlCommand cmd1 = new SqlCommand(cmdText1, cn, tran);

      cmd1.ExecuteNonQuery();

 

      SqlCommand cmd2 = new SqlCommand(cmdText2, cn, tran);

      cmd2.ExecuteNonQuery();

 

      tran.Commit();

   }

   catch(Exception e)

   {

      tran.Rollback();

   }