【问题标题】:Is it possible to return dynamic objects or Dataset from a Sqlite Query?是否可以从 Sqlite 查询返回动态对象或数据集?
【发布时间】:2016-12-30 14:02:08
【问题描述】:

我在我的Xamarin.Forms 应用程序中使用Sqlite.Net。到目前为止,如果我的对象是这样的类,它在返回对象列表方面表现出色:

SqliteDatabase.Connection.Query<Customer>("Select * from Customers");

我现在想从我的查询中动态返回 DataSet 的等效项

SqliteDatabase.Connection.Query("Select * from Customers inner join Calls on Customers.Id=Calls.CustomerId")

现在从第二个查询我想返回一个DataSet 而不是一个对象列表。我知道我可以创建一个新对象,它结合了CustomersCalls 的列,但我不想每次查询数据库时都必须创建对象。

是否可以只动态返回DatasetObject

【问题讨论】:

  • 您在返回DataSet or a DataTable 时遇到了什么问题,这不是很困难.. 提示在.Fill() 方法上进行谷歌搜索。目前在 stackoverflow 上有很多关于如何执行此操作的示例..
  • @MethodMan 你确定我们在谈论同一个 Sqlite 包装器吗?我正在使用的Sqlite.Net-PCL 上不存在Fill 方法

标签: c# sqlite xamarin sqlite.net


【解决方案1】:

最后,我实际上设法想出了一个方法,该方法将运行任何查询并将行作为列表中的项目返回,并将列作为数组中的对象返回:

    public List<object[]> RunSql(string sqlString, bool includeColumnNamesAsFirstRow)
    {
        var lstRes = new List<object[]>();
        SQLitePCL.sqlite3_stmt stQuery = null;
        try
        {
            stQuery = SQLite3.Prepare2(fieldStrikeDatabase.Connection.Handle, sqlString);
            var colLenght = SQLite3.ColumnCount(stQuery);

            if (includeColumnNamesAsFirstRow)
            {
                var obj = new object[colLenght];
                lstRes.Add(obj);
                for (int i = 0; i < colLenght; i++)
                {
                    obj[i] = SQLite3.ColumnName(stQuery, i);
                }
            }

            while (SQLite3.Step(stQuery) == SQLite3.Result.Row)
            {
                var obj = new object[colLenght];
                lstRes.Add(obj);
                for (int i = 0; i < colLenght; i++)
                {
                     var columnType = SQLitePCL.raw.sqlite3_column_decltype(stQuery, i);

                     switch (columnType)
                     {
                         case "text":
                              obj[i] = SQLite3.ColumnString(stQuery, i);
                              break;
                         case "int":
                               obj[i] = SQLite3.ColumnInt(stQuery, i);
                               break;
                         case "real":
                               obj[i] = SQLite3.ColumnDouble(stQuery, i);
                               break;
                         case "blob":
                               obj[i] = SQLite3.ColumnBlob(stQuery, i);
                               break;
                         case "null":
                               obj[i] = null;
                               break;
                      }
                }
            }
            return lstRes;
        }
        catch (Exception)
        {
            return null;
        }
        finally
        {
            if (stQuery != null)
            {
                SQLite3.Finalize(stQuery); 
            }
        }
    }

【讨论】:

  • startColumnIndex 的值是多少?它似乎没有在您的代码中定义。
  • 这实际上是我的代码中的一个错误,我已经删除了该属性。在我的生产应用程序中,出于不同的原因,我使用 StartColumnIndex 来抵消我的列数
  • 干得好。这正是我也在寻找的。由于旧代码库都是通过 SQL 调用完成的,因此 sql-net-pcl 在不能轻松显示任意查询结果的情况下并不是很有用。
【解决方案2】:

SQLite.NET PCL 是一个围绕 sqlite 的 .NET 包装器。

因此,您可以在 LINQ 或 Lambda 中使用联接而不是在 Query 中使用类似于 EF 的查询。包装器将为您处理转换为 sqlite 查询。

然后您可以返回连接类型或动态类型的新数据类型。

注意:在 sqlite (more info) 中不直接支持连接,并且提到了解决方法 here

示例代码:

var conn = new SQLiteConnection(sqlitePlatform, "foofoo");
var query = from customer in conn.Table<Customers>().ToList()
            join call in conn.Table<Calls>().ToList()
                         on customer.ID equals call.CustomerId                
            select new { Customer = customer , Calls = call };

Lambda 版本:

conn.Table<Customer>().ToList().Join
(conn.Table<Call>().ToList(),
customer => customer.Id,
call => call.CustomerId, 
(customer, call) => new { Customer = customer, Calls = call });

【讨论】:

  • 你有一个例子说明这是怎么做的,我可以试试吗?
  • 感谢您的示例,但不幸的是它不起作用。你得到一个System.NotSupportedException: Joins are not supported. 有没有我可以尝试的替代库或代码?
  • 这在 Xamarin.iOS 中不起作用,因为 ios 不支持 Dynamic 对象
【解决方案3】:

非常感谢用户1!完美运行。 这里只是一个例子如何使用你的方法:

var objects = mySQLiteConnection.RunSql("SELECT * FROM Persons", true);

// ColumnNames
List<string> ColumnNames = new List<string>();
for (int column = 0; column < objects[0].Length; column++)
{
    if (objects[0][column] != null) spaltennamen.Add((string)objects[0][column]);
}

// RowValues
for (int row = 1; row < objects.Count; row++)
{
    for (int column = 0; column < objects[row].Length; column++)
    {
        if (objects[row][column] != null) System.Diagnostics.Debug.WriteLine(spaltennamen[column] + " : " + objects[row][column]);
    }
}

【讨论】:

    【解决方案4】:

    听起来您想要做的实际上是重新创建 ADO.NET。当您说“DataSet”时,我猜您是在谈论 ADO.NET。这可能意味着您不想使用 SQLite.Net 库中内置的 ORM 功能。

    我创建了这个版本的库,它允许您从 SQLite 数据库执行平面表读取。这意味着您可以根据需要将数据读入 ADO.NET 数据集。

    https://github.com/MelbourneDeveloper/SQLite.Net.Standard

    【讨论】:

      【解决方案5】:

      与@Fabian Monkemoller 不同,我无法让@User1 的代码立即工作。这是一个修改版本,它利用可空引用类型和方法嵌套将主代码与 try-catch 块分开:

           public static object?[][]? ToDataSet(this SQLiteConnection sqlConnection, string query , bool includeColumnNamesAsFirstRow = true)
          {
              var stQuery = SQLite3.Prepare2(sqlConnection.Handle, query );
              var colLength = SQLite3.ColumnCount(stQuery);
              try
              {
                  return SelectRows().ToArray();
              }
              catch (Exception e)
              {
                  return null;
              }
              finally
              {
                  if (stQuery != null)
                  {
                      SQLite3.Finalize(stQuery);
                  }
              }
      
              IEnumerable<object?[]> SelectRows()
              {
                  if (includeColumnNamesAsFirstRow)
                  {
                      yield return SelectColumnNames(stQuery, colLength).ToArray();
                  }
      
                  while (SQLite3.Step(stQuery) == SQLite3.Result.Row)
                  {
                      yield return SelectColumns(stQuery, colLength).ToArray();
                  }
      
                  static IEnumerable<object> SelectColumnNames(SQLitePCL.sqlite3_stmt stQuery, int colLength)
                  {
                      for (int i = 0; i < colLength; i++)
                      {
                          yield return SQLite3.ColumnName(stQuery, i);
                      }
                  }
      
                  static IEnumerable<object?> SelectColumns(SQLitePCL.sqlite3_stmt stQuery, int colLength)
                  {
                      for (int i = 0; i < colLength; i++)
                      {
                          var x = SQLitePCL.raw.sqlite3_column_decltype(stQuery, i);
                          yield return x switch
                          {
                              "text" => SQLite3.ColumnString(stQuery, i),
                              "integer" => SQLite3.ColumnInt(stQuery, i),
                              "bigint" => SQLite3.ColumnInt64(stQuery, i),
                              "real" => SQLite3.ColumnDouble(stQuery, i),
                              "blob" => SQLite3.ColumnBlob(stQuery, i),
                              "null" => null,
                              _ => throw new Exception($"Unexpected type encountered in for query {stQuery}")
                          };
                      }
                  }
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-10
        • 2021-09-25
        • 2021-03-29
        相关资源
        最近更新 更多