gpt4 book ai didi

C# 从未知列表长度分配字段

转载 作者:行者123 更新时间:2023-11-29 11:45:47 25 4
gpt4 key购买 nike

我正在 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 数据更新游戏中的对象。

最佳答案

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

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

因此,您的查询方法更改为

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 和列名列表。使用它们,它创建一个 Terminals 实例,提取列值(检查是否存在预期的列)并将 Terminals 实例添加到 Terminals 的全局列表中。

那么,无论谁调用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 方法的所有内容

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

关于C# 从未知列表长度分配字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34979265/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com