我在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]会抛出异常。此外,如果用户只选择名称而没有其他内容,则可能首先返回名称而不是ID,在这种情况下,将项目[0]转换为int是不正确的
我该怎么做?我需要这样做,因为我想根据SQL数据更新我的游戏对象。
答案 0 :(得分:1)
如果您想知道查询创建的DataReader中存在哪些列,您可以使用GetSchemaTable方法返回一个包含列描述的表。在此上下文中,您只对列的名称感兴趣,因此您可以使用列的名称创建一个简单的List<string>
。
这开启了使用List<Terminals>
代替复杂List<List<object>>
当然,我们还需要一种方法,以这种方式使您的Query方法尽可能通用,如果您为另一个类传递不完整的查询文本,则可以使用相同的代码。
实现此目标的一种简单方法是分离打开连接,创建命令,执行命令以及从读取读取器内容任务的记录循环的常用内容。如果您向Query方法传递一个Action委托,该委托接收DataReader和datareader中存在的列列表,则可以实现这一点。
因此,您的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和列名列表。使用它们,它创建一个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);