我最近在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)失败?