为什么MSMQ WCF会离开开放式数据库事务?

时间:2016-10-04 23:36:49

标签: c# wcf msmq

我们正在使用Windows服务托管的WCF服务监视具有Entity Framework的事务性MSMQ队列以进行数据库操作。在调查与数据库相关的问题时,我注意到我们的生产服务在处理各自的消息队列后离开了开放的事务。

在故障排除过程中,我将生产代码修剪成一个简单的演示应用程序。我知道连接处于打开状态并返回到池中,但感觉在处理完邮件后不应该是打开的事务。使用HTTP绑定进行托管或直接实例化和使用该类时,不会出现此行为。

以下是演示应用的所有代码:

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(() =>
            {
                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<ISomeService>(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<Product> Products { get; set; }
    }

    public class Product
    {
        [Key]
        public int Key { get; set; }
        public string Name { get; set; }
        public decimal? Price { get; set; }
    }
}

还要将连接字符串添加到App.config:

<connectionStrings>
  <add name="ProductContext" connectionString="Data Source=.;Initial Catalog=SomeDatabase;Integrated Security=True;" providerName="System.Data.SqlClient" />
</connectionStrings>

运行应用程序后,您可以使用sp_WhoIsActive等工具查看数据库上的打开事务,并使用打开的事务查看(休眠连接)。

现在更改HTTP的地址绑定:

var address = "http://localhost:8080/" + queueName;
var binding = new BasicHttpBinding(BasicHttpSecurityMode.None);

再次运行时,数据库上没有任何打开的事务(但连接仍然按预期打开)

我在这个问题上摸不着头脑,非常感谢任何帮助!

0 个答案:

没有答案