【问题标题】:Multiple queries on one transaction. C# winforms with SQLite一个事务的多个查询。带有 SQLite 的 C# winforms
【发布时间】: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ó 所说,上述方法没有必要。

标签: c# sqlite


【解决方案1】:

错误消息“数据库已锁定”表示有一些其他连接具有活动事务。

为确保事务不会保持活动状态,请检查 所有 SQL 命令和事务对象是否仅用于 using 块,或以其他方式清理。此外,整个程序应该只使用一个连接对象(除非它有多个线程);你不应该每次都重新打开它(这只会让一切变慢,因为页面缓存会丢失)。

【讨论】:

  • 谢谢!这个解决方案对我很有用。我检查了我拥有的所有其他方法,它们都存在你所说的那种问题。
猜你喜欢
  • 2017-05-29
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-10
  • 2011-01-08
相关资源
最近更新 更多