Thursday, July 25, 2013

How are using statements in C# like out and ref variables?

First a bit of a set up. I made an ASP.NET MVC 4 Web API application in Visual Studio 2012 and I made the default /home/index/ view look like so:

@using System.Data
@model DataSet[]
@foreach (DataSet set in Model)
{
   foreach (DataRow row in set.Tables[0].Rows)
   {
      var copy = row[2];
      <hr style="margin-top: 80px;" />
      <text>@copy</text>
   }
}

 
 

I then made HomeController look like so:

using System.Data;
using System.Web.Mvc;
using FeelSoUsed.DataIntegration;
namespace FeelSoUsed.Controllers
{
   public class HomeController : Controller
   {
      public ActionResult Index()
      {
         Repository repository = new Repository();
         DataSet[] dataSets = repository.GetStuff();
         return View(dataSets);
      }
   }
}

 
 

I suppose I could have skipped mentioning the stuff above, but here is where things get really interesting:

using System.Configuration;
using System.Data;
using System.Data.SqlClient;
namespace FeelSoUsed.DataIntegration
{
   public class Repository
   {
      public DataSet[] GetStuff()
      {
         string specs = ConfigurationManager.ConnectionStrings["x"].ConnectionString;
         using (SqlConnection connection = new SqlConnection(specs))
         {
            Query query = new Query();
            DataSet firstSet = query.GetStuff(connection);
            DataSet secondSet = query.GetStuff(connection);
            return new DataSet[] {firstSet, secondSet};
         }
      }
   }
}

 
 

The Query class looks like this:

using System.Data;
using System.Data.SqlClient;
namespace FeelSoUsed.DataIntegration
{
   public class Query
   {
      public DataSet GetStuff(SqlConnection connection)
      {
         using (SqlCommand command = new SqlCommand())
         {
            using (SqlDataAdapter adapter = new SqlDataAdapter())
            {
               DataSet dataSet = new DataSet();
               string query = "dbo.GetStuff";
               command.CommandText = query;
               command.CommandType = CommandType.StoredProcedure;
               command.Connection = connection;
               adapter.SelectCommand = command;
               adapter.Fill(dataSet);
               return dataSet;
            }
         }
      }
   }
}

 
 

The thing to see here is that from within a using statement, one may hand the star of the using statement into another method and the star's behavior goes unchanged... well, up to a point. For example if Query were changed to be like so:

using System.Data;
using System.Data.SqlClient;
namespace FeelSoUsed.DataIntegration
{
   public class Query
   {
      public DataSet GetStuff(SqlConnection connection)
      {
         using (connection)
         {
            using (SqlCommand command = new SqlCommand())
            {
               using (SqlDataAdapter adapter = new SqlDataAdapter())
               {
                  DataSet dataSet = new DataSet();
                  string query = "dbo.GetStuff";
                  command.CommandText = query;
                  command.CommandType = CommandType.StoredProcedure;
                  command.Connection = connection;
                  adapter.SelectCommand = command;
                  adapter.Fill(dataSet);
                  return dataSet;
               }
            }
         }
      }
   }
}

 
 

Well... after Repository runs the code in Query a first time, the Dispose method on the SqlConnection instance will be run doing away with it. This is so in the second scenario as another using statement is nested in the second version of Query for the variable that is handed into it. However, you shouldn't do this sort of thing if you haven't made the "using object" immediately in a given method as the "state" of the SqlConnection being disposed will bubble back out of the method to the Repository method calling it and affect the "state" of the GetStuff() method in Repository. This is how a variable that is the star of a using statement may behave akin to an out or ref variable. The second call to Query in Repository cannot run in this circumstance because the SqlConnection will have been disposed of by the first Query instance!

No comments:

Post a Comment