【问题标题】:C# Assigning fields from unknown list lengthC#从未知列表长度分配字段
【发布时间】:2016-01-24 17:53:46
【问题描述】:

我正在 Unity 中创建一个游戏,允许用户输入 SQL 命令。

假设我的 SQL 数据库中有一个名为 Terminals 的表,其中包含 Id、Name、Type 和 Hacked 列。

如果用户输入一个 SELECT 命令,例如SELECT Id, Name FROM Terminals;,那么 lineList 会添加返回的数据字段的数量,例如仅限 ID 和名称。

我的问题是,如何将 Terminals 类中的属性分配给从 SQL 查询返回的正确值?

这是我的代码:

public class SQLConnect : MonoBehaviour {
private void Query(string sqlCommand)
{
    using (dbCon = new SqlConnection(connectionString))
    {
        using (dbcmd = dbCon.CreateCommand())
        {
            dbcmd.CommandText = sqlCommand;
            dbCon.Open();

            using (reader = dbcmd.ExecuteReader())
            {
                var readList = new List<List<object>>();

                while (reader.Read())
                {
                    var lineList = new List<object>();

                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        lineList.Add(reader.GetValue(i)); // This reads the entries in a row
                    }

                    readList.Add(lineList);
                }
            }
        }
    }
}
}

public static class SQLDynamicData
{
    public static List<Terminals> TerminalList;

    public class Terminals
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Type { get; set; }
        public bool Hacked { get; set; }

        public Terminals(int id, string name, string type, bool hacked)
        {
            Id = id;
            Name = name;
            Type = type;
            Hacked = hacked;
        }
    }
}

这是我尝试过的:

using (reader = dbcmd.ExecuteReader())
            {
                var readList = new List<List<object>>();

                while (reader.Read())
                {
                    var lineList = new List<object>();

                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        lineList.Add(reader.GetValue(i)); // This reads the entries in a row
                    }
                 readList.Add(lineList);
                }
                foreach (var item in readList)
                {
                    SQLDynamicData.TerminalList.Add(new SQLDynamicData.Terminals(Convert.ToInt32(item[0]), item[1].ToString(), item[2].ToString(), Convert.ToBoolean(item[3])));
                }
            }

这样做的问题是,很明显如果返回的数据只是 Id 和 Name,那么 item[2] 和 item[3] 会抛出异常。此外,如果用户只选择 Name 而不是其他,则可能首先返回 Name 而不是 Id,在这种情况下,将 item[0] 转换为 int 是不正确的

我该怎么做?我需要这样做,因为我想根据 SQL 数据更新我的游戏内对象。

【问题讨论】:

    标签: c# mysql list unity3d


    【解决方案1】:

    如果您想知道查询创建的 DataReader 中存在哪些列,您可以使用 GetSchemaTable 方法,该方法返回包含列描述的表。在这种情况下,您只对列名感兴趣,因此您可以使用列名创建一个简单的List&lt;string&gt;。 这为使用List&lt;Terminals&gt; 而不是复杂的List&lt;List&lt;object&gt;&gt; 开辟了道路

    当然,我们还需要一种方法,让您的 Query 方法尽可能通用,如果您为另一个类传递不完整的查询文本,则可以使用相同的代码。
    达到此目标的一个简单方法是将打开连接的常用内容、创建命令、执行命令并循环读取阅读器内容的任务中的记录。如果您将接收 DataReader 和 datareader 中存在的列列表的 Action 委托传递给 Query 方法,则可以实现这一点。

    因此,您的 Query 方法更改为

    private void Query(string sqlCommand, Action<SqlDataReader, List<string> loader)
    {
        using (dbCon = new SqlConnection(connectionString))
        using (dbcmd = dbCon.CreateCommand())
        {
            dbcmd.CommandText = sqlCommand;
            dbCon.Open();
            DataTable dt = reader.GetSchemaTable();
            List<string> columns = dt.AsEnumerable().Select(x => x.Field<string>("COLUMNNAME")).ToList();
            using (reader = dbcmd.ExecuteReader())
            {
                while (reader.Read())
                  if(loader != null) 
                    loader(reader, columns);
            }
        }
    }
    

    如您所见,Query 方法不知道如何从查询中检索字段,而是将此任务留给作为其第二个参数接收的特殊方法。此方法需要两个参数,一个 DataReader 和列名列表。它使用它们创建一个终端实例,提取列值(检查是否存在预期的列)并将终端实例添加到终端的全局列表中。

    那么,调用Query方法的人应该提供有效加载数据的方法,像这样的方法

    private void LoadTerminalsData(SqlDataReader reader, List<string> cols)
    {
        Terminals t = new Terminals();
        if(cols.IndexOf("id") != -1)
            t.id = reader.GetInt32(reader.GetOrdinal("id"));
        if(cols.IndexOf("Name") != -1)
            t.Name = reader.GetString(reader.GetOrdinal("Name"));
        if(cols.IndexOf("Type") != -1)
            t.Type = reader.GetString(reader.GetOrdinal("Type"));
        if(cols.IndexOf("Hacked") != -1)
            t.Hacked = reader.GetBoolean(reader.GetOrdinal("Hacked"));
        listOfTerminals.Add(t);
    }
    

    像这样调用所有传递给查询方法的东西

    ....Query("select id, Name from Terminals", LoadTerminalData);
    

    【讨论】:

    • 效果很好,谢谢。我如何获得当前正在操作的表名? dt.TableName 给了我“SchemaTable”,而不是“终端”。
    • 我不知道有任何方法可以直接从 DataReader 获取此信息。连接对象有一个 GetSchema("TABLES") ,它返回数据库中存在的所有表,但 DataReader 使用的表没有以任何特定方式标识。但是,如果您的 LoadTerminalsData 是专用于 Terminals 类(和表)的方法,而不同的类将有自己的方法和自己的表,那么您总是可以知道您的表名。如果您确实需要在 Query 方法中使用此名称,您可以随时添加第三个参数和表名。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 2012-11-01
    • 1970-01-01
    • 2015-07-22
    • 1970-01-01
    • 1970-01-01
    • 2018-05-05
    相关资源
    最近更新 更多