title: Why is MSMQ WCF leaving open database transactions?
body: <p>We're using Windows service-hosted WCF service monitoring transactional MSMQ queues with Entity Framework for database operations. While investigating a database-related issue, I noticed that our production services were leaving open transactions after processing their respective message queues.</p>
  
  <p>In the course of troubleshooting, I've trimmed our production code into a simple demo app. I understand that connections are left open and returned to the pool, but feel there shouldn't be open transactions after a message has been processed. This behavior is not present when hosting with an HTTP binding or when instantiating and using the class directly.</p>
  
  <p>Here's all the code for the demo app:</p>
  
  <pre><code>using System;
  using System.ComponentModel.DataAnnotations;
  using System.Data.Entity;
  using System.Linq;
  using System.Messaging;
  using System.Net.Security;
  using System.ServiceModel;
  using System.Threading;
  
  namespace MsmqTestHost
  {
      class Program
      {
          static void Main(string[] args)
          {
              // Ensure transacted queue exists
              var queueName = "testqueue";
              var queue = @".\private$\" + queueName;
              if (!MessageQueue.Exists(queue))
                  MessageQueue.Create(queue, true);
  
              // Ensure DB creation
              using (var db = new ProductContext())
              {
                  var products = db.Products;
                  if (!products.Any())
                  {
                      db.Products.Add(new Product {Name = "SomeProduct", Price = 5.00m});
                      db.SaveChanges();
                  }
              }
  
              // Configure WCF
              var address = "net.msmq://localhost/private/" + queueName;
              var binding = new NetMsmqBinding(NetMsmqSecurityMode.None);
              binding.Security.Transport.MsmqProtectionLevel = ProtectionLevel.None;
              binding.Security.Message.ClientCredentialType = MessageCredentialType.None;
  
              // Start the host
              new Thread(() =&gt;
              {
                  using (var serviceHost = new ServiceHost(typeof(SomeService)))
                  {
                      serviceHost.AddServiceEndpoint(typeof(ISomeService), binding, new Uri(address));
                      serviceHost.Open();
                      Thread.Sleep(Timeout.Infinite);
                  }
              }).Start();
  
              // Create a client to add a message to the queue
              var factory = new ChannelFactory&lt;ISomeService&gt;(binding, new EndpointAddress(address));
              var client = factory.CreateChannel();
              client.SomeOperation();
              (client as IClientChannel).Close();
          }
      }
  
      //WCF service
      [ServiceContract(Namespace = "http://schemas.test.local/someservice/", SessionMode = SessionMode.NotAllowed)]
      public interface ISomeService
      {
          [OperationContract(Name = "SomeOperation", IsOneWay = true)]
          void SomeOperation();
      }
  
      public class SomeService : ISomeService
      {
          [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
          public void SomeOperation()
          {
              using (var db = new ProductContext())
              {
                  var product = db.Products.FirstOrDefault();
                  if (product != null)
                  {
                      product.Price = 5.00m;
                      db.SaveChanges();
                  }
                  Console.WriteLine("Operation complete");
              }
          }
      }
  
      //EF objects
      public class ProductContext : DbContext
      {
          public DbSet&lt;Product&gt; Products { get; set; }
      }
  
      public class Product
      {
          [Key]
          public int Key { get; set; }
          public string Name { get; set; }
          public decimal? Price { get; set; }
      }
  }
  </code></pre>
  
  <p>Also add the connection string to the App.config:</p>
  
  <pre><code>&lt;connectionStrings&gt;
    &lt;add name="ProductContext" connectionString="Data Source=.;Initial Catalog=SomeDatabase;Integrated Security=True;" providerName="System.Data.SqlClient" /&gt;
  &lt;/connectionStrings&gt;
  </code></pre>
  
  <p>After running the application, you can look at open transactions on the database with a tool like sp_WhoIsActive and view the (sleeping connection) with an open transaction.</p>
  
  <p>Now change the address binding out for HTTP:</p>
  
  <pre><code>var address = "http://localhost:8080/" + queueName;
  var binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
  </code></pre>
  
  <p>Upon running it again, no open transactions are left on the database (but the connection is still open, as expected)</p>
  
  <p>I'm scratching my head on this issue and any assistance is highly appreciated!</p>