【发布时间】:2017-03-31 16:32:10
【问题描述】:
我正在学习如何使用 SQLite,但我被这个问题困扰了一个多星期,在我的网络搜索中没有找到答案。
问题是我需要用它的相应行注册一个账单,并更新购买产品的一些信息,如果某些查询失败,所有这些信息都在同一个交易中。
当我尝试注册某些东西时,我得到 "Database is locked" 异常,但我发现奇怪的是,我在创建这个问题时保持程序运行,当我看到它时我再次发现“继续”按钮可用,然后程序终于可以运行了。
所以我想知道我必须改进什么才能没有那个例外。
啊,这是一个Winforms应用程序,而且只有一个人会使用它,所以不会有并发问题(或者我是这么认为的)。
首先,这是 connectionString(如果我需要添加一些东西的话): “数据源=D:\De disco c\Documents\Visual Studio 2010\Projects\Racion\Racion\bin\Racion.db;Version=3;”
在账单类的 Mapper 上我有这个方法:
public static void registrarFactura(Factura f)
{
SQLiteConnection conn = null;
SQLiteTransaction trn = null;
try
{
var parametros = new List<SQLiteParameter>();
var cant = new SQLiteParameter();
cant.ParameterName = "@Cliente";
cant.Value = f.cliente;
parametros.Add(cant);
cant = new SQLiteParameter();
cant.ParameterName = "@Fecha";
cant.Value = DateTime.Now;
parametros.Add(cant);
//Here is one query. I need to know the bill's ID to register the lines and make the update.
String consulta = "Insert into Factura(Cliente, Fecha) VALUES (@Cliente, @Fecha); SELECT last_insert_rowid();";
//Open the connection
conn = ObtenerConection();
//begin transaction
using (trn = conn.BeginTransaction())
{
//Here I register the bill and obtain it's id
int codigo = Convert.ToInt32(Mapper.ejecutaScalar(consulta, CommandType.Text, parametros, conn, trn));
//Now I must register the lines of the bill
String consulta2 = "";
foreach (Linea l in f.lineas)
{
parametros = new List<SQLiteParameter>();
cant = new SQLiteParameter();
cant.ParameterName = "@NLinea";
cant.Value = l.numeroLinea;
parametros.Add(cant);
cant = new SQLiteParameter();
cant.ParameterName = "@Cantidad";
cant.Value = l.cantidad;
parametros.Add(cant);
cant = new SQLiteParameter();
cant.ParameterName = "@CodigoProd";
cant.Value = l.producto.Codigo;
parametros.Add(cant);
cant = new SQLiteParameter();
cant.ParameterName = "@PTotal";
cant.Value = l.PrecioTotal;
parametros.Add(cant);
cant = new SQLiteParameter();
cant.ParameterName = "@Codigo";
cant.Value = codigo;
parametros.Add(cant);
//The query to insert the actual line of the foreach
consulta = "Insert into Linea(IdFactura, IdLinea, IdProducto, Cantidad, Total) VALUES (" + codigo + ", @NLinea, @CodigoProd, @Cantidad, @PTotal)";
Mapper.EjecutaNonQuery(consulta, CommandType.Text, parametros, conn, null);
//Update the stock of the product
if (f.cliente == "")
{
consulta2 = "Update Producto Set Cantidad=Cantidad+@Cantidad Where IdProducto=@CodigoProd;";
}
else
{
consulta2 = "Update Producto Set Cantidad=Cantidad-@Cantidad Where IdProducto=@CodigoProd;";
}
Mapper.EjecutaNonQuery(consulta2, CommandType.Text, parametros, conn, null);
}
//The transaction concludes
trn.Commit();
}
}
catch (SqlException ex)
{
//If there is a problem
trn.Rollback();
}
finally
{
//Close the connection
CerrarConexion(conn);
}
}
在映射器类上,我有这两个在前一个中使用的方法:
public static object ejecutaScalar(string sentencia, CommandType tipoComando, List<SQLiteParameter> parametros, SQLiteConnection con, SQLiteTransaction trn)
{
object retorno;
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.Connection = con;
cmd.CommandText = sentencia;
cmd.CommandType = tipoComando;
cmd.Parameters.AddRange(parametros.ToArray());
if (trn != null)
cmd.Transaction = trn;
retorno = cmd.ExecuteScalar();
}
return retorno;
}
public static int EjecutaNonQuery(string sentencia, CommandType tipoComando, List<SQLiteParameter> parametros, SQLiteConnection con, SQLiteTransaction trn)
{
int afectadas = -1;
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.Connection = con;
cmd.CommandText = sentencia;
cmd.CommandType = tipoComando;
cmd.Parameters.AddRange(parametros.ToArray());
if (trn != null)
cmd.Transaction = trn;
afectadas = cmd.ExecuteNonQuery();
}
return afectadas;
}
谢谢,如果我不能更好地解释我,我很抱歉,英语不是我的母语,我有一些困难:P
【问题讨论】:
-
尝试处理 SQLiteCommand
cmd.Dispose() -
@crimson589 不需要,在 using 块中
-
这不是你问题的答案,但它会解决这个问题和许多其他问题:使用 ORM。例如Dapper
-
感谢 cmets。我在其他方法中使用了 Dispose,它有效,但正如@Tamás Szabó 所说,上述方法没有必要。