[英]How can I map the results of a sql query onto objects?
目前,我正在使用這樣的東西:
try
{
dr = SQL.Execute(sql);
if(dr != null) {
while(dr.Read()) {
CustomObject c = new CustomObject();
c.Key = dr[0].ToString();
c.Value = dr[1].ToString();
c.Meta = dr[2].ToString();
customerInfo.CustomerList.Add(c);
}
}
else
{
customerInfo.ErrorDetails="No records found";
}
有沒有一種方法可以直接進行此映射,而不是我手動進行分配(假設列名與字段名匹配)。
然而,一個要求是我想通過我當前使用 sql 查詢的方法而不是使用純基於 LINQ 的方法來做到這一點。 一方面,SQL 查詢足夠大,涉及復雜的 JOIN 並且已經過徹底測試,所以我現在不想引入更多錯誤。 有什么建議么?
一個簡單的解決方案是為您的CustomObject
創建一個構造函數,它接受一個DataRow
(來自示例,所以如果它是另一個類,請糾正我)。
在你的新構造函數中,按照你自己的例子做。
public CustomObject(DataRow row)
{
Key = row[0].ToString();
// And so on...
}
另一種方法是引入泛型,並在您的 SQL 類中創建一個新函數
示例(從Passing arguments to C# generic new() of templated type 獲取代碼):
// This function should reside in your SQL-class.
public IEnumerable<T> ExecuteObject<T>(string sql)
{
List<T> items = new List<T>();
var data = ExecuteDataTable(sql); // You probably need to build a ExecuteDataTable for your SQL-class.
foreach(var row in data.Rows)
{
T item = (T)Activator.CreateInstance(typeof(T), row);
items.Add(item);
}
return items;
}
用法示例:
public IEnumerable<CustomObject> GetCustomObjects()
{
return SQL.ExecuteObject<CustomObject>("SELECT * FROM CustomObject");
}
我已經在 LinqPad 中測試了這段代碼,它應該可以工作。
您可以通過為您的要求創建通用方法來實現。 您也可以將新方法作為數據表的擴展。
public static List<T> ToList<T>(this DataTable table) where T : class, new()
{
try
{
List<T> list = new List<T>();
foreach (var row in table.AsEnumerable())
{
T obj = new T();
foreach (var prop in obj.GetType().GetProperties())
{
try
{
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
propertyInfo.SetValue(obj, Convert.ChangeType(row[prop.Name], propertyInfo.PropertyType), null);
}
catch
{
continue;
}
}
list.Add(obj);
}
return list;
}
catch
{
return null;
}
}
}
用法:
DataTable dtCustomer = GetCustomers();
List<CustomObject> CustomObjectList = dtCustomer.ToList<CustomObject>();
您應該研究 MicroORM。 與提供必須使用的 SDL 的常規 ORM 不同,MicroORM 允許您使用自己的 SQL 查詢,並且僅提供從 SQL 結果集到 C# 對象以及從 C# 對象到 SQL 參數的映射。
我最喜歡的是PetaPoco ,它也提供了一個查詢構建器,它使用您自己的 SQL 但對參數編號進行了一些巧妙的操作。
@user1553525的回答很好,但是,如果您的列名與您的屬性名稱不完全匹配,則它不起作用。
所以首先你要創建一個自定義屬性。 然后使用您嘗試反序列化的類中的屬性,最后,您想要反序列化 DataTable。
我們創建了一個自定義屬性,該屬性將應用於我們類內部的屬性。 我們創建的類具有屬性Name
,稍后我們將使用該屬性從 DataTable 中獲取正確的列。
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
public class MySqlColName : Attribute
{
private string _name = "";
public string Name { get => _name; set => _name = value; }
public MySqlColName(string name)
{
_name = name;
}
}
接下來,在我們將要填充的類中,我們將使用我們剛剛創建的屬性[MySqlColName]
聲明將鏈接到類中的屬性的列名。
但是,如果屬性名稱與數據庫列相同,我們不需要在屬性中指定列名稱,因為.ToList<>()
函數將從屬性名稱中假定列的名稱。
public class EventInfo
{
[MySqlColName("ID")]
public int EventID { get; set; }
//Notice there is no attribute on this property?
public string Name { get; set; }
[MySqlColName("State")]
public string State { get; set; }
[MySqlColName("Start_Date")]
public DateTime StartDate { get; set; }
[MySqlColName("End_Date")]
public DateTime EndDate { get; set; }
}
最后,我們修改@user1553525的答案,添加一個檢查以查看是否提供了我們的自定義屬性。 如果是,那么我們將列的名稱設置為提供的名稱,否則,我們使用屬性名稱(參見 try 塊中的代碼)。
public static List<T> ToList<T>(this DataTable table) where T : class, new()
{
try
{
List<T> list = new List<T>();
foreach (var row in table.AsEnumerable())
{
T obj = new T();
foreach (var prop in obj.GetType().GetProperties())
{
try
{
//Set the column name to be the name of the property
string ColumnName = prop.Name;
//Get a list of all of the attributes on the property
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
//Check if there is a custom property name
if (attr is MySqlColName colName)
{
//If the custom column name is specified overwrite property name
if (!colName.Name.IsNullOrWhiteSpace())
ColumnName = colName.Name;
}
}
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
//GET THE COLUMN NAME OFF THE ATTRIBUTE OR THE NAME OF THE PROPERTY
propertyInfo.SetValue(obj, Convert.ChangeType(row[ColumnName], propertyInfo.PropertyType), null);
}
catch
{
continue;
}
}
list.Add(obj);
}
return list;
}
catch
{
return null;
}
}//END METHOD
最后,我們可以調用.ToList<>()
方法並獲取序列化對象的列表
List<EventInfo> CustomObjectList;
using (DataTable dtCustomer = GetDataTable("SELECT * FROM EventIndex"))
{
CustomObjectList = dtCustomer.ToList<EventInfo>();
}
旁注:我使用了一些自定義方法
public static bool IsNullOrWhiteSpace(this string x)
{
return string.IsNullOrWhiteSpace(x);
}
public static DataTable GetDataTable(string Query)
{
MySqlConnection connection = new MySqlConnection("<Connection_String>");
try
{
DataTable data = new DataTable();
connection.Open();
using (MySqlCommand command = new MySqlCommand(Query, connection))
{
data.Load(command.ExecuteReader());
}
return data;
}
catch (Exception ex)
{
// handle exception here
Console.WriteLine(ex);
throw ex;
}
finally
{
connection.Close();
}
}
假設:如果您只需要用於序列化或簡單臨時輸出的對象。
您可以像這樣使用ExpandoObject
和SqlDataReader.GetSchemaTable()
:
private IEnumerable<dynamic> ReaderToAnonymmous(SqlCommand comm) {
using (var reader = comm.ExecuteReader()) {
var schemaTable = reader.GetSchemaTable();
List<string> colnames = new List<string>();
foreach (DataRow row in schemaTable.Rows) {
colnames.Add(row["ColumnName"].ToString());
}
while (reader.Read()) {
var data = new ExpandoObject() as IDictionary<string, Object>;
foreach (string colname in colnames) {
var val = reader[colname];
data.Add(colname, Convert.IsDBNull(val) ? null : val);
}
yield return (ExpandoObject)data;
}
}
}
盡管發布了更快的解決方案(我將其發布為臨時 SQL/Reader 結果/輸出的替代懶惰方法)。
下面的函數接受一個 SQL 字符串和一個對象,它要求對象在 select 語句中的每一列都有一個屬性。 對象必須被實例化。
public object SqlToSingleObject(string sSql, object o)
{
MySql.Data.MySqlClient.MySqlDataReader oRead;
using (ConnectionHelper oDb = new ConnectionHelper())
{
oRead = oDb.Execute(sSql);
if (oRead.Read())
{
for (int i = 0; i < oRead.FieldCount; i++)
{
System.Reflection.PropertyInfo propertyInfo = o.GetType().GetProperty(oRead.GetName(i));
propertyInfo.SetValue(o, Convert.ChangeType(oRead[i], propertyInfo.PropertyType), null);
}
return o;
}
else
{
return null;
}
}
}
在搜索此答案時,我發現您可以使用 Dapper 庫: https : //dapper-tutorial.net/knowledge-base/44980945/querying-into-a-complex-object-with-dapper
你可以使用這樣的東西:
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
IList<CustomObject> result = connection.Query<CustomObject>(sql, commandType: CommandType.Text).ToList();
}
雖然這個問題一直存在,但我找不到一個干凈的解決方案。 為了我的目的,我想出了以下在我的情況下效果很好的方法。
using System.Dynamic;
private IEnumerable<ExpandoObject> GetQueryToList()
{
try
{
using (var conn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(MyQuery, conn))
{
var list = new List<ExpandoObject>();
conn.Open();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var expandoObject = new ExpandoObject();
for (var i = 0; i < reader.FieldCount; i++)
{
((IDictionary<string, object>) expandoObject).Add(
reader.GetName(i), reader[i]);
}
list.Add(expandoObject);
}
reader.Close();
return list;
}
}
catch (Exception ex)
{
var m = MethodBase.GetCurrentMethod();
Console.WriteLine(ex.Message + " " + m.Name);
}
return null;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.