【问题标题】:WCF TransactionScope doesnt RollBackWCF TransactionScope 不回滚
【发布时间】:2012-05-03 11:38:46
【问题描述】:

我用过这个向导:http://www.c-sharpcorner.com/uploadfile/shivprasadk/wcf-faq-part-5-transactions/ 为什么不回滚??我不明白!

我有一个服务和客户端应用程序,但我不知道这段代码有什么问题。 在完美地完成这一行并将其保存在我的数据库中之后,

proxy.AddEmployee("Stav", "20");

下一行抛出异常,因为我没有向 Age 参数发送数字,但事务没有回滚第一行,所以信息:Stav,20 仍然存在于我的数据库中!

proxy.AddEmployee("Stav123", "Not a Number(-->will do Exception)") 

编辑 1: 我添加了 AddEmployee 实现。

客户:

static void Main(string[] args)
        {
            ServiceReference1.IJob proxy = new ServiceReference1.JobClient();
            using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required))
            {
                try
                {
                    proxy.AddEmployee("Stav", "20");
                    proxy.AddEmployee("Stav123", "Not a Number(-->will do Exception) ");//stop the running and show the Exception but keep the stav,20 in DB
                    ts.Complete();
                }
                catch
                {
                    ts.Dispose();
                }
            }
        }

服务:

[ServiceContract]
    public interface IJob
    {
        [OperationContract]
        [TransactionFlow(TransactionFlowOption.Allowed)]
        void AddEmployee(string Name, string Age);
    }
public class JobImplement:IJob
    {
        [OperationBehavior(TransactionScopeRequired = true)]
        public void AddEmployee(string Name, string Age)
        {
string strConnection = @"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\Users\stavalfi\Desktop\WCF2\ConsoleApplication4\WCF_DB.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
        SqlConnection objConnection = new SqlConnection(strConnection);
        objConnection.Open();
        SqlCommand objCommand = new SqlCommand("INSERT INTO Employee (Name,Age) " + "VALUES ('" + Name + "' ,'" + Age + "')", objConnection);
        objCommand.ExecuteNonQuery();
        objConnection.Close();
        }
    }
static void Main(string[] args)
        {
            WSHttpBinding Basic = new WSHttpBinding();
            Basic.TransactionFlow = true;
            ServiceHost host = new ServiceHost(typeof(JobImplement), new Uri("http://localhost:8080"));
            //
            ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
            behavior.HttpGetEnabled = true;
            host.Description.Behaviors.Add(behavior);
            //
            host.AddServiceEndpoint(typeof(IJob), new BasicHttpBinding(), "Request123");
            host.Open();
            //
            Console.WriteLine("opened");
            Console.ReadLine();
            //
            host.Close();
        }

【问题讨论】:

  • 添加 AddEmployee 方法是如何实现的?它真的兑现了交易吗?
  • 我用工具更新了话题。还有什么?
  • 您使用的是什么绑定?并非所有绑定都支持事务。
  • 好的,支持事务。您在 app.config trasactionFlow = "ture" 的 System.ServiceModel 绑定部分中有吗?又名。
  • 我在我的代码中写了它。所以不需要cofing。我会将连接添加到我的主题。

标签: c# transactions transactionscope distributed-transactions


【解决方案1】:

您的代码中可能有错字:

static void Main(string[] args)         
{             
    ...
    host.AddServiceEndpoint(typeof(IJob), new BasicHttpBinding(), "Request123");   
    ...
}

您正在添加 BasicHttpBinding 类型的端点 - 一种不支持事务的绑定协议。

我认为您实际上是打算这样做:

static void Main(string[] args)         
{             
    WSHttpBinding Basic = new WSHttpBinding();
    Basic.TransactionFlow = true;  
    ...
    host.AddServiceEndpoint(typeof(IJob), Basic, "Request123");   
    ...
}

这将为您提供一个 WSHttpBinding 端点 - 一个支持事务的绑定协议。

【讨论】:

  • 天啊,非常感谢!我不敢相信我是那么愚蠢,我有 2 个配置文件我也忘了用 BasicHttpBiding 删除。
【解决方案2】:

您应该实现自己的回滚功能。这是一个基本的方法。在您的 Service 类接口中添加[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)] 属性。然后添加这些代码:

private SqlCommand Command { get; set; }

[OperationContract]
public void BeginTransaction()
{
    this.Command = new SqlCommand();
    string strConnection = @"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\Users\stavalfi\Desktop\WCF2\ConsoleApplication4\WCF_DB.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
    SqlConnection objConnection = new SqlConnection(strConnection);
    objConnection.Open();
    this.Command.Connection = objConnection;
}

[OperationContract]
public void RollBackTransaction()
{
    this.Command.Transaction.Rollback();
}

[OperationContract]
public void CommitTransaction()
{
    this.Command.Transaction.Commit();
}

[OperationContract]
public void CloseConnection()
{
    this.Command.Connection.Close();
    this.Command = null;
}

[OperationBehavior(TransactionScopeRequired = true)]
public void AddEmployee(string Name, string Age)
{
    this.Command.CommandText = "INSERT INTO Employee (Name,Age) " + "VALUES ('" + Name + "' ,'" + Age + "')";
    this.Command.ExecuteNonQuery();
}

然后你就可以在客户端这样的代码中访问它了:

try
{
    proxy.BeginTransaction();
    proxy.AddEmployee("Stav", "20");
    proxy.AddEmployee("Stav123", "Not a Number(-->will do Exception)");
    proxy.CommitTransaction();
}
catch
{
    proxy.RollBackTransaction();
}
finally
{
    proxy.CloseConnection();
}

希望这会有所帮助。

【讨论】:

  • 在所有文章中,客户端由 transactionScope 组成(如果我有多个服务 - 我正在构建一个包含一个服务的简单示例,但在我的下一个示例中,我将添加更多服务)- 所以 TransactionScope 是从客户端管理所有这些的,所以在服务中实现我自己的事务是没用的-(如果我有一个以上的服务也没用 - 那是我的目标).btw,那是什么:[ServiceBehavior( InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)] ?
  • @StavAlfi I dont think it's possible. 你至少需要传递一个Transaction 类型来做到这一点,但它是不可序列化的。而且,您还需要在所有查询中使用单一连接来获得所需内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 2010-10-04
相关资源
最近更新 更多