使用任意对象类型来解析 MySql 查询(学习 C# 反射)
Use an arbitrary object type to parse a MySql query (learning C# Reflection)
我正在尝试更好地理解 C# 上的泛型和反射。作为练习,我正在执行 MySql 查询并尝试将其结果解析为预定义对象:
//FOR TABLE A
public class ObjectType1
{
public int id { get; set; }
public String name { get; set; }
}
//FOR TABLE B
public class ObjectType2
{
public int id { get; set; }
public timestamp expirationDate { get; set; }
}
//FOR TABLE C
public class ObjectType3
{
public int id { get; set; }
public BigDecimal price { get; set; }
}
我的目标是这样的:
List<ObjectType1> listObjectsA = selectAndCast(tableNameA, ObjectType1)
List<ObjectType2> listObjectsB = selectAndCast(tableNameB, ObjectType2)
List<ObjectType3> listObjectsC = selectAndCast(tableNameC, ObjectType3)
我的问题是,如何将所需的对象类型指定为参数? (已经在 S.O 处检查了类似的问题,但出现了编译错误)。
问题/工作代码的答案:
感谢@JosephDaSilva 的更正
public List<T> selectAndCast<T>(string connStr, String query, int argTimeoutSecs) where T : new()
{
MySqlConnection conn = new MySqlConnection(connStr);
MySqlDataReader rdr = null;
List<T> listaSalida = new List<T>();
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand(query, conn);
if (argTimeoutSecs != -1)
{
cmd.CommandTimeout = argTimeoutSecs;
}
rdr = cmd.ExecuteReader();
// Create a dictionary that contains each column name and a consecutive number. That number will be later used to locate the column by its name.
Dictionary<String, int> dictionaryColumnNameVsIndex = new Dictionary<String, int>();
for (int i = 0; i < rdr.FieldCount; i++)
{
String nombreColumna = rdr.GetName(i);
dictionaryColumnNameVsIndex.Add(nombreColumna, i);
}
PropertyInfo[] properties = typeof(T).GetProperties();
T destinationObject;
while (rdr.Read())
{
// For each row obtained from the query execution, create a new instance of the Object
destinationObject = new T();
for (int i = 0; i < rdr.FieldCount; i++)
{
foreach (PropertyInfo property in properties)
{
// Check if the destination object contains a property with the same name.
if (dictionaryColumnNameVsIndex.ContainsKey(property.Name))
{
// If it does, assign the value to said property.
PropertyInfo propertyToBeChanged = destinationObject.GetType().GetProperty(property.Name);
String newValue = rdr[dictionaryColumnNameVsIndex[property.Name]].ToString();
propertyToBeChanged.SetValue(destinationObject, newValue);
}
}
}
// After all rows have been processed, return the object list
listaSalida.Add(destinationObject);
}
return listaSalida;
}
catch (Exception ex)
{
Console.WriteLine("Error when trying to SELECT: " + ex.ToString());
return null;
}
finally
{
if (rdr != null)
{
rdr.Close();
conn.Close();
}
}
}
在 selectAndCast
方法中使用类型参数 T
作为对象的类型。由于您需要创建类型的实例,因此使用 new()
约束类型参数,这要求类型具有无参数构造函数(所有对象类型都有一个,因为没有显式构造函数)。所以 selectAndCast
方法应该声明为:
public List<T> selectAndCast<T>(string tableName) where T : new() {
// ...
}
要创建 List<T>
的实例,请使用:
List<T> listaSalida = new List<T>();
要创建类型 T
的实例(由于 new()
约束而有效):
T destinationObject = new T();
获取类型 T
的属性(这也应该移到读取循环之外,因为它每次总是 return 相同的属性列表):
PropertyInfo[] properties = typeof(T).GetProperties();
泛型方法可以这样调用:
List<ObjectType1> listObjectsA = selectAndCast<ObjectType1>(tableNameA);
我正在尝试更好地理解 C# 上的泛型和反射。作为练习,我正在执行 MySql 查询并尝试将其结果解析为预定义对象:
//FOR TABLE A
public class ObjectType1
{
public int id { get; set; }
public String name { get; set; }
}
//FOR TABLE B
public class ObjectType2
{
public int id { get; set; }
public timestamp expirationDate { get; set; }
}
//FOR TABLE C
public class ObjectType3
{
public int id { get; set; }
public BigDecimal price { get; set; }
}
我的目标是这样的:
List<ObjectType1> listObjectsA = selectAndCast(tableNameA, ObjectType1)
List<ObjectType2> listObjectsB = selectAndCast(tableNameB, ObjectType2)
List<ObjectType3> listObjectsC = selectAndCast(tableNameC, ObjectType3)
我的问题是,如何将所需的对象类型指定为参数? (已经在 S.O 处检查了类似的问题,但出现了编译错误)。
问题/工作代码的答案:
感谢@JosephDaSilva 的更正
public List<T> selectAndCast<T>(string connStr, String query, int argTimeoutSecs) where T : new()
{
MySqlConnection conn = new MySqlConnection(connStr);
MySqlDataReader rdr = null;
List<T> listaSalida = new List<T>();
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand(query, conn);
if (argTimeoutSecs != -1)
{
cmd.CommandTimeout = argTimeoutSecs;
}
rdr = cmd.ExecuteReader();
// Create a dictionary that contains each column name and a consecutive number. That number will be later used to locate the column by its name.
Dictionary<String, int> dictionaryColumnNameVsIndex = new Dictionary<String, int>();
for (int i = 0; i < rdr.FieldCount; i++)
{
String nombreColumna = rdr.GetName(i);
dictionaryColumnNameVsIndex.Add(nombreColumna, i);
}
PropertyInfo[] properties = typeof(T).GetProperties();
T destinationObject;
while (rdr.Read())
{
// For each row obtained from the query execution, create a new instance of the Object
destinationObject = new T();
for (int i = 0; i < rdr.FieldCount; i++)
{
foreach (PropertyInfo property in properties)
{
// Check if the destination object contains a property with the same name.
if (dictionaryColumnNameVsIndex.ContainsKey(property.Name))
{
// If it does, assign the value to said property.
PropertyInfo propertyToBeChanged = destinationObject.GetType().GetProperty(property.Name);
String newValue = rdr[dictionaryColumnNameVsIndex[property.Name]].ToString();
propertyToBeChanged.SetValue(destinationObject, newValue);
}
}
}
// After all rows have been processed, return the object list
listaSalida.Add(destinationObject);
}
return listaSalida;
}
catch (Exception ex)
{
Console.WriteLine("Error when trying to SELECT: " + ex.ToString());
return null;
}
finally
{
if (rdr != null)
{
rdr.Close();
conn.Close();
}
}
}
在 selectAndCast
方法中使用类型参数 T
作为对象的类型。由于您需要创建类型的实例,因此使用 new()
约束类型参数,这要求类型具有无参数构造函数(所有对象类型都有一个,因为没有显式构造函数)。所以 selectAndCast
方法应该声明为:
public List<T> selectAndCast<T>(string tableName) where T : new() {
// ...
}
要创建 List<T>
的实例,请使用:
List<T> listaSalida = new List<T>();
要创建类型 T
的实例(由于 new()
约束而有效):
T destinationObject = new T();
获取类型 T
的属性(这也应该移到读取循环之外,因为它每次总是 return 相同的属性列表):
PropertyInfo[] properties = typeof(T).GetProperties();
泛型方法可以这样调用:
List<ObjectType1> listObjectsA = selectAndCast<ObjectType1>(tableNameA);