This .Net class library is designed to integrate into enterprise applications to help support their high performance and reliability requirements. The focus of the Enterprise Library is to provide simple and robust mechanisms that take full advantage of the amazing capabilities already in the .Net Framework and extend those capabilities to support the needs of enterprise applications. The first major component of the library is the Operations Framework. The Operations Framework provides flexible and efficient support for asynchronous operations that can fully utilize the power of computers with multiple processors and make I/O operations much faster.

Operations Framework

The library contains the Operations Framework, which is designed to make working with asynchronous tasks simple, robust, and performant. The operation classes are easy to integrate into your code. They help you avoid common mistakes in your code by releasing resources automatically at the right times (including closing database connections), synchronizing work done on multiple threads efficiently, taking advantage of I/O completion ports, and handling critical exceptions correctly (including OutOfMemory, StackOverflow, and ThreadAbort). The operation classes make the most of the .Net Framework by leveraging the power of the thread pool, ADO.Net connection pool, and timers. They help you solve common problems by including support for customizable automatic retries, reliable timeouts, efficient parallel execution, and flexible transactions. As demonstrated in the following examples, these capabilities are highly useful when communicating with a database such as SQL Server.

Code Examples

Example 1: Web Application

This example uses TimeoutOperation and RetryOperation to execute a database command with automatic retries and an overall timeout.
public partial class Default : System.Web.UI.Page
{
  protected void Page_PreRender(object sender, EventArgs e)
  {
    SqlConnection connection = new SqlConnection(@"Server=.\SQLEXPRESS;Database=master;Integrated Security=True;MultipleActiveResultSets=True;Asynchronous Processing=True");
    SqlCommand command = new SqlCommand("SELECT COUNT(*) FROM sys.schemas;", connection);

    RetryPolicy retryPolicy = new ExceptionTypeRetryPolicy(typeof(SqlException));
    TimeSpan timeout = new TimeSpan(0, 0, 30);

    Operation operation = new TimeoutOperation(new RetryOperation(new ScalarDatabaseOperation<int>(command), retryPolicy), timeout);
    this.AddOnPreRenderCompleteAsync(BeginOperation, EndOperation, operation);
  }

  private IAsyncResult BeginOperation(object sender, EventArgs e, AsyncCallback callback, object state)
  {
    Operation operation = (Operation)state;
    return operation.BeginExecute(callback, state);
  }

  private void EndOperation(IAsyncResult asyncResult)
  {
    using (Operation operation = (Operation)asyncResult.AsyncState)
    {
      operation.EndExecute(asyncResult);
      ScalarDatabaseOperation<int> databaseOperation = operation.FindOperation<ScalarDatabaseOperation<int>>();
      int schemaCount = databaseOperation.Result;
      this.Label1.Text = string.Format(CultureInfo.CurrentCulture, "Schemas: {0}", schemaCount);
    }
  }
}

Example 2: WinForms Application

This example uses RetryOperation and TransactionOperation to execute a database command in a transaction with automatic retries. It uses SynchronizationOperation to ensure the asynchronous callback gets executed on the UI thread so it can safely update UI controls.
public partial class SchemasForm : Form
{
  private struct SchemaInfo
  {
    public int SchemaId { get; set; }
    public string Name { get; set; }
  }

  private class SchemaResultReader : ResultReader<IList<SchemaInfo>>
  {
    public override IList<SchemaInfo> ReadResult(IDataReader dataReader)
    {
      List<SchemaInfo> result = new List<SchemaInfo>();
      using (dataReader)
      {
        int schemaIdOrdinal = dataReader.GetOrdinal("schema_id");
        int nameOrdinal = dataReader.GetOrdinal("name");
        while (dataReader.Read())
        {
          SchemaInfo schemaInfo = new SchemaInfo();
          schemaInfo.SchemaId = dataReader.GetInt32(schemaIdOrdinal);
          schemaInfo.Name = dataReader.GetString(nameOrdinal);
          result.Add(schemaInfo);
        }
      }
      return result;
    }
  }

  public SchemasForm()
  {
    InitializeComponent();
  }

  private void RefreshButton_Click(object sender, EventArgs e)
  {
    SqlConnection connection = new SqlConnection(@"Server=.\SQLEXPRESS;Database=master;Integrated Security=True;MultipleActiveResultSets=True;Asynchronous Processing=True");
    SqlCommand command = new SqlCommand("SELECT name, schema_id FROM sys.schemas;", connection);

    SchemaResultReader resultReader = new SchemaResultReader();
    CommandBehavior commandBehavior = CommandBehavior.SingleResult | CommandBehavior.CloseConnection;
    TimeSpan timeout = TransactionManager.DefaultTimeout;
    RetryPolicy retryPolicy = new ExceptionTypeRetryPolicy(typeof(SqlException));

    Operation operation = new SynchronizationOperation(new RetryOperation(new TransactionOperation(new ResultSetDatabaseOperation<IList<SchemaInfo>>(command, resultReader, commandBehavior), TransactionOption.Allow, timeout), retryPolicy));
    operation.BeginExecute(OperationExecutedCallback, operation);
  }

  private void OperationExecutedCallback(IAsyncResult asyncResult)
  {
    using (Operation operation = (Operation)asyncResult.AsyncState)
    {
      operation.EndExecute(asyncResult);
      ResultSetDatabaseOperation<IList<SchemaInfo>> databaseOperation = operation.FindOperation<ResultSetDatabaseOperation<IList<SchemaInfo>>>();
      this.SchemasDataGridView.DataSource = databaseOperation.Result;
    }
  }
}

Example 3: Console Application

This example uses ParallelOperation and TransactionOperation to execute multiple database commands in parallel, each with its own transaction.
internal class Program
{
  private struct SchemaInfo
  {
    public int SchemaId { get; set; }
    public string Name { get; set; }
  }

  private class SchemaResultReader : ResultReader<IList<SchemaInfo>>
  {
    public override IList<SchemaInfo> ReadResult(IDataReader dataReader)
    {
      List<SchemaInfo> result = new List<SchemaInfo>();
      using (dataReader)
      {
        int schemaIdOrdinal = dataReader.GetOrdinal("schema_id");
        int nameOrdinal = dataReader.GetOrdinal("name");
        while (dataReader.Read())
        {
          SchemaInfo schemaInfo = new SchemaInfo();
          schemaInfo.SchemaId = dataReader.GetInt32(schemaIdOrdinal);
          schemaInfo.Name = dataReader.GetString(nameOrdinal);
          result.Add(schemaInfo);
        }
      }
      return result;
    }
  }

  private static EventWaitHandle _completedEvent = new ManualResetEvent(false);

  private static void Main()
  {
    SqlConnection connection1 = new SqlConnection(@"Server=.\SQLEXPRESS;Database=master;Integrated Security=True;MultipleActiveResultSets=True;Asynchronous Processing=True");
    SqlCommand command1 = new SqlCommand("SELECT name, schema_id FROM sys.schemas;", connection1);

    SqlConnection connection2 = new SqlConnection(@"Server=.\SQLEXPRESS;Database=master;Integrated Security=True;MultipleActiveResultSets=True;Asynchronous Processing=True");
    SqlCommand command2 = new SqlCommand("SELECT SERVERPROPERTY('productversion');", connection2);

    SchemaResultReader resultReader = new SchemaResultReader();
    CommandBehavior commandBehavior = CommandBehavior.SingleResult | CommandBehavior.CloseConnection;
    TimeSpan timeout = TransactionManager.DefaultTimeout;

    Operation operation = new ParallelOperation(new TransactionOperation(new ResultSetDatabaseOperation<IList<SchemaInfo>>(command1, resultReader, commandBehavior), TransactionOption.Allow, timeout), new TransactionOperation(new ScalarDatabaseOperation<string>(command2), TransactionOption.Allow, timeout));
    operation.BeginExecute(OperationExecutedCallback, operation);
    _completedEvent.WaitOne();
  }

  private static void OperationExecutedCallback(IAsyncResult asyncResult)
  {
    using (Operation operation = (Operation)asyncResult.AsyncState)
    {
      ResultSetDatabaseOperation<IList<SchemaInfo>> databaseOperation1 = operation.FindOperation<ResultSetDatabaseOperation<IList<SchemaInfo>>>();
      foreach (SchemaInfo schemaInfo in databaseOperation1.Result)
      {
        Console.WriteLine("SchemaId={0}, Name={1}", schemaInfo.SchemaId, schemaInfo.Name);
      }
      ScalarDatabaseOperation<string> databaseOperation2 = operation.FindOperation<ScalarDatabaseOperation<string>>();
      Console.WriteLine("SQL Server {0}", databaseOperation2.Result);
    }
    _completedEvent.Set();
  }
}

Releases

Version 3.0 of the framework is now available. It has been thoroughly tested with unit tests and in a large-scale real-world application. See the Releases page to download the binaries, source code, documentation, and sample web application.

Library Usage

The project's default license is a modified form the BSD permissive free software license. For non-commercial, internal business, evaluation, academic, and research purposes, you may freely use the library, its source code, and its documentation in your project. For other purposes, such as commercial applications used directly by your customers, you can obtain a commercial license by contacting the project coordinator. We encourage everyone to contribute to the project by requesting features, reporting bugs, and writing code.

Last edited Jan 30, 2012 at 9:28 PM by FishDawg, version 29