试图找到一种方法来为我的项目实现嵌套事务

时间:2016-02-23 04:03:47

标签: c# ado.net

我最近在C#中创建了一个轻量级ORM工具,并将其发布在Github上。我的ORM所做的一件事就是为你打开和关闭连接以及打开和处理sql事务。我这样做的方法是打开连接并在我的上下文类的构造函数中开始sql事务并关闭连接并将事务处理在我的上下文类的dispose方法上。下面的代码是我工具中的一些方法:

public class ADOCRUDContext : IDisposable
{
    internal IDbConnection sqlConnection;
    internal IDbTransaction sqlTransaction;

    internal string suffix = "_";

    public ADOCRUDContext(string connectionString)
    {            
        sqlConnection = new SqlConnection(connectionString);
        sqlConnection.Open();
        sqlTransaction = sqlConnection.BeginTransaction();
    }

    public void Commit()
    {
        sqlTransaction.Commit();
    }

    /// <summary>
    /// Disposes transaction and closes sql connection
    /// </summary>
    public void Dispose()
    {
        sqlTransaction.Dispose();
        sqlConnection.Close();
    }

    public void Insert<T>(T item)
    {
        string tableName = ObjectTypeHelper.GetTableName<T>(item);

        // Properties of object that are not primary keys
        PropertyInfo[] modelProperties = ObjectTypeHelper.GetModelProperties<T>(item, false);

        // Properties of object that are also primary keys
        PropertyInfo[] primaryKeyProperties = ObjectTypeHelper.GetPrimaryKeyProperties<T>(item);

        if (modelProperties == null || modelProperties.Count() == 0)
        {
            // Moves composite keys to model properties if they are the only properties of the table/object
            if (primaryKeyProperties.Count() > 0)
                modelProperties = primaryKeyProperties;
            else
                throw new Exception("Class has no properties or are missing Member attribute");
        }

        // Generates insert statement
        StringBuilder query = new StringBuilder();
        query.Append("insert into " + tableName + "(" + String.Join(",", modelProperties.Select(x => x.Name)) + ") ");
        query.Append("values (" + String.Join(", ", modelProperties.Select(x => "@" + x.Name+suffix)) + ") ");
        query.Append("select @primaryKey = @@identity");

        try
        { 
            // Generates and executes sql command 
            SqlCommand cmd = GenerateSqlCommand<T>(item, query.ToString(), modelProperties, null, ADOCRUDEnums.Action.Insert, suffix);
            cmd.ExecuteNonQuery();

            // Loads db generated identity value into property with primary key attribute
            // if object only has 1 primary key
            // Multiple primary keys = crosswalk table which means property already contains the values
            if (primaryKeyProperties != null && primaryKeyProperties.Count() == 1)
                primaryKeyProperties[0].SetValue(item, cmd.Parameters["primaryKey"].Value as object);
        }
        catch (Exception ex)
        {
            sqlTransaction.Rollback();
            throw new Exception(ex.Message);
        }
    }

以下是您使用我的工具的示例:

public void AddProduct(Product p)
{
  using (ADOCRUDContext context = new ADOCRUDContext(connectionString))
  {
    context.Insert<Product>(p);
    context.Commit();
  }
}

public void UpdateProduct(int productId)
{
  using (ADOCRUDContext context = new ADOCRUDContext(connectionString))
  {
    Product p = this.GetProductById(productId);
    p.Name = "Basketball";
    context.Update<Product>(p);
    context.Commit();
  }
}

目前,如果我做了类似的事情:

public void DoSomething(Product p)
{
    using (ADOCRUDContext context = new ADOCRUDContext(connectionString))
    {
        this.AddProduct(p);
        p.Name = "Test";
        throw new Exception("Blah");
    }
}

即使我在DoSomething上抛出了异常,但由于内部事务已提交,因此产品仍然被添加到数据库中。如果外部事务失败,有没有办法让内部事务(AddProduct)失败?

0 个答案:

没有答案