如何将泛型与 SqlDataReader 一起使用
How to use Generic with SqlDataReader
我正在尝试想出一种方法,只需将 table 从 SQL 服务器加载到 class,而无需告诉它任何内容。基本上,只需创建 class 并让它知道要加载什么,基于此。这是我目前所拥有的。
我的问题是,有没有什么方法可以避免对类型进行硬编码来调用 reader.readString、reader。 readInt32 等.. 基于 FieldType?
private Int32? readInt32(SqlDataReader reader, string columnName)
{
Int32? result = null;
if (!reader.IsDBNull(reader.GetOrdinal(columnName)))
{
result = reader.GetInt32(reader.GetOrdinal(columnName));
};
return result;
}
public List<T> readTable(string table, string wherecls, string connStr)
{
List<T> result = new List<T>();
using (SqlConnection connection = new SqlConnection(connStr))
{
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "select * from " + table;
if (wherecls.Length > 0) command.CommandText += " where " + wherecls;
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Object i = Activator.CreateInstance(typeof(T));
System.Reflection.FieldInfo[] fieldInfoList = typeof(T).GetFields();
foreach (System.Reflection.FieldInfo f in fieldInfoList)
{
if (f.FieldType == typeof(string)) f.SetValue(i, readString(reader, f.Name));
if (f.FieldType == typeof(Int32)) f.SetValue(i, readInt32(reader, f.Name));
if (f.FieldType == typeof(Int16)) f.SetValue(i, readInt16(reader, f.Name));
if (f.FieldType == typeof(byte)) f.SetValue(i, readByte(reader, f.Name));
if (f.FieldType == typeof(short)) f.SetValue(i, readShort(reader, f.Name));
}
result.Add((T)i);
}
}
}
}
return result;
}
谢谢,
丹·蔡斯
您所描述的是大量工作...并且正是 "dapper" 等工具已经在做的事情 。所以我的建议是:使用 dapper:
// Dapper adds a Query<T>(this DbConnection, ...) extension method
var data = connection.Query<T>(sql, args).AsList();
我 会 ,但是,说 string wherecls
让我脊背发凉 - 这听起来像是 SQL 注射噩梦。但是...这取决于你。
试试这个。
确保类型有一个 public 默认构造函数——一个不带参数的构造函数——并且 SQL 字符串中的列名与类型的 public属性。
namespace MyNamespace {
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Reflection;
public static class MyExtensions {
public static IEnumerable<T> Query<T>(this SqlConnection cn, string sql) {
Type TypeT = typeof(T);
ConstructorInfo ctor = TypeT.GetConstructor(Type.EmptyTypes);
if (ctor == null) {
throw new InvalidOperationException($"Type {TypeT.Name} does not have a default constructor.");
}
using (SqlCommand cmd = new SqlCommand(sql, cn)) {
using (SqlDataReader reader = cmd.ExecuteReader()) {
while (reader.Read()) {
T newInst = (T)ctor.Invoke(null);
for (int i = 0; i < reader.FieldCount; i++) {
string propName = reader.GetName(i);
PropertyInfo propInfo = TypeT.GetProperty(propName);
if (propInfo != null) {
object value = reader.GetValue(i);
if (value == DBNull.Value) {
propInfo.SetValue(newInst, null);
} else {
propInfo.SetValue(newInst, value);
}
}
}
yield return newInst;
}
}
}
}
}
}
我正在尝试想出一种方法,只需将 table 从 SQL 服务器加载到 class,而无需告诉它任何内容。基本上,只需创建 class 并让它知道要加载什么,基于此。这是我目前所拥有的。
我的问题是,有没有什么方法可以避免对类型进行硬编码来调用 reader.readString、reader。 readInt32 等.. 基于 FieldType?
private Int32? readInt32(SqlDataReader reader, string columnName)
{
Int32? result = null;
if (!reader.IsDBNull(reader.GetOrdinal(columnName)))
{
result = reader.GetInt32(reader.GetOrdinal(columnName));
};
return result;
}
public List<T> readTable(string table, string wherecls, string connStr)
{
List<T> result = new List<T>();
using (SqlConnection connection = new SqlConnection(connStr))
{
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "select * from " + table;
if (wherecls.Length > 0) command.CommandText += " where " + wherecls;
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Object i = Activator.CreateInstance(typeof(T));
System.Reflection.FieldInfo[] fieldInfoList = typeof(T).GetFields();
foreach (System.Reflection.FieldInfo f in fieldInfoList)
{
if (f.FieldType == typeof(string)) f.SetValue(i, readString(reader, f.Name));
if (f.FieldType == typeof(Int32)) f.SetValue(i, readInt32(reader, f.Name));
if (f.FieldType == typeof(Int16)) f.SetValue(i, readInt16(reader, f.Name));
if (f.FieldType == typeof(byte)) f.SetValue(i, readByte(reader, f.Name));
if (f.FieldType == typeof(short)) f.SetValue(i, readShort(reader, f.Name));
}
result.Add((T)i);
}
}
}
}
return result;
}
谢谢, 丹·蔡斯
您所描述的是大量工作...并且正是 "dapper" 等工具已经在做的事情 。所以我的建议是:使用 dapper:
// Dapper adds a Query<T>(this DbConnection, ...) extension method
var data = connection.Query<T>(sql, args).AsList();
我 会 ,但是,说 string wherecls
让我脊背发凉 - 这听起来像是 SQL 注射噩梦。但是...这取决于你。
试试这个。
确保类型有一个 public 默认构造函数——一个不带参数的构造函数——并且 SQL 字符串中的列名与类型的 public属性。
namespace MyNamespace {
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Reflection;
public static class MyExtensions {
public static IEnumerable<T> Query<T>(this SqlConnection cn, string sql) {
Type TypeT = typeof(T);
ConstructorInfo ctor = TypeT.GetConstructor(Type.EmptyTypes);
if (ctor == null) {
throw new InvalidOperationException($"Type {TypeT.Name} does not have a default constructor.");
}
using (SqlCommand cmd = new SqlCommand(sql, cn)) {
using (SqlDataReader reader = cmd.ExecuteReader()) {
while (reader.Read()) {
T newInst = (T)ctor.Invoke(null);
for (int i = 0; i < reader.FieldCount; i++) {
string propName = reader.GetName(i);
PropertyInfo propInfo = TypeT.GetProperty(propName);
if (propInfo != null) {
object value = reader.GetValue(i);
if (value == DBNull.Value) {
propInfo.SetValue(newInst, null);
} else {
propInfo.SetValue(newInst, value);
}
}
}
yield return newInst;
}
}
}
}
}
}