gpt4 book ai didi

c# - 在 C# 中将 DataTable 转换为 List

转载 作者:行者123 更新时间:2023-11-30 20:54:16 24 4
gpt4 key购买 nike

我已经在 StackOverflow(和其他网站)中搜索了有关在 C# 中使用反射将 DataTable 转换为 List 的信息。

到目前为止,我的结果非常好:我可以在 3.5 秒内反射(reflect) 20 万行(在硬编码模式下为 0.5 秒)。

但是我的实体(代表我的数据的类,但我想你已经知道了)遵循这种模式:

我的数据库有这样的列(我实际上并没有这样做,但你会明白的):

Table: Clients
Columns:
ClientID, ClientName, ClientPhone, CityID[FK]

我正在使用 SqlConnection (MySqlConnection),因此我必须对我的实体进行硬编码并将数据库结果转换为该实体的列表。喜欢:

Select *, cit.* from Clients cli
Inner join Cities cit on (cit.CityID == cli.CityID)
Inner join Countries cou on (cou.CountryID == cit.CountID)

我不知道这个 SQL 是否正确,但我想你明白了。这应该返回一些这样的字段:

ClientID, ClientName, ClientPhone, CityID, CityName, CountryID, CountryName

结果应该是 List<Client> .

问题是:我有 2 个内部联接,我在我的实体中表示这些数据是这样的(我喜欢“像这样”这样的表达方式):

public class Client
{
public int ClientID { get; set; }
public string ClientName { get; set; }
public string ClientPhone { get; set; }
public City ClientCity { get; set; }
}

public class City
{
public int CityID { get; set; }
public string CityName { get; set; }
public Country CityCountry { get; set; }
}

public class Country
{
public int ContryID { get; set; }
public string CountryName { get; set; }
}

所以,如果我有一个 Client对象,我会通过表达式 client.ClientCity.CityCountry.CountryName 得到它的国家名称.我称之为 3 级属性访问器。

我想正确地反射(reflect)它。下面是将 DataTable 转换为 List 的主要方法。我的母语是葡萄牙语,但我尝试翻译我的评论以符合我上面的描述。

这段代码的想法是:我尝试在主类中找到我必须设置的列。如果找不到,我会在作为对象的属性中搜索该属性。就像 Client 中的 ClientCity 中的 CityName。这段代码一团糟。

 public List<T> ToList<T>(DataTable dt) where T : new()
{

Type type= typeof(T);
ReflectionHelper h = new ReflectionHelper(type);
insertPropInfo(tipo); //a pre-reflection work, I cache some delegates, etc..
List<T> list = new List<T>();
DataTableReader dtr = dt.CreateDataReader();
while (dtr.Read())
{
T obj = new T();
for (int i = 0; i < dtr.FieldCount; i++)
{
GetObject(ref obj, tipo, dtr.GetName(i), dtr.GetValue(i));
}
list.Add(obj);
}

return lista;

}
//ref T obj: the object I create before calling this method
//Type classType: the type of the object (say, Client)
//string colName: this is the Database Column i'm trying to fill. Like ClientID or CityName or CountryName.
//colLineData: the data I want to put in the colName.

public void GetObject<T>(ref T obj, Type classType, string colName, object colLineData) where T : new()
{


//I do some caching to reflect just once, and after the first iteration, I think all the reflection I need is already done.
foreach (PropertyInfo info in _classPropInfos[classType])
{
//If the current PropertyInfo is a valuetype (like int, int64) or string, and so on
if (info.PropertyType.IsValueType || info.PropertyType == typeof(string))
{
//I think string.Equals is a little faster, but i had not much difference using "string" == "string"
if (info.Name.Equals(colName)) //did I found the property?

if (info.PropertyType != typeof(char)) //I have to convert the type if this is a Char. MySql returns char as string.
{
_delegateSetters[info](obj, colLineData); //if it isn't a char, just set it.
}
else
{
_delegateSetters[info](obj, Convert.ChangeType(colLineData, typeof(char)));
}
break;
}
else //BUT, if the property is a class, like ClientCity:
{
//I reflect the City class, if it isn't reflected yet:
if (!_classPropInfos.ContainsKey(info.PropertyType))
{
insertPropInfo(info.PropertyType);
}
//now I search for the property:
Boolean foundProperty = false;
object instance = _delegateGetters[info](obj); //Get the existing instance of ClientCity, so I can fill the CityID and CityName in the same object.

foreach (PropertyInfo subInfo in _classPropInfos[info.PropertyType])
{
if (subInfo.Name.Equals(colName))//did I found the property?
{
if (instance == null)
{
//This will happen if i'm trying to set the first property of the class, like CityID. I have to instanciate it, so in the next iteration it won't be null, and will have it's CityID filled.
instance = _initializers[info.PropertyType]();//A very fast object initializer. I'm worried about the Dictionary lookups, but i have no other idea about how to cache it.
}
_delegateSetters[subInfo](instance, colLineData);//set the data. This method is very fast. Search about lambda getters & setters using System.Linq.Expression.
foundProperty = true;
break;//I break the loops when I find the property, so it wont iterate anymore.
}

}
if (foundProperty)//if I found the property in the code above, I set the instance of ClientCity to the Client object.
{
_delegateSetters[info](obj, instance);
break;
}
}
}
}

此代码有一个问题:我可以访问 CityID 和 CityName,并填写它。但是 CountryID 和 CountryName 不会。因为这段代码可以进行 2 层反射,所以我需要一些递归方法来填充我需要的许多层。我尝试这样做,但我遇到了太多的堆栈溢出和空引用异常,我几乎放弃了。

此代码将使获取数据库行变得更加容易,您是否已经找到了一些库或任何可以满足我需要的东西?如果不是,我如何实现 n 级反射以从 DataTable 中创建适当的列表?

最佳答案

你的问题很常见,几乎所有流通的 ORM 都解决了这个问题。
当然,更改已编写的应用程序以利用 ORM 通常是不切实际的,但是有一些简单的 ORM 确实很容易添加到现有应用程序中,并让您逐步替换已编写的代码。

其中一个 ORM 是 DAPPER .它仅包含一个源文件,您可以将其直接包含在与您的 POCO 类和存储库方法相同的项目中(或者仅引用已编译的程序集)。它真的很容易学习,考虑到要执行的工作的复杂性,它的速度非常快。更不用说这个小 gem 的作者经常在这个网站上回答关于他们工作的问题。只需使用 #dapper 进行搜索即可标签

到目前为止,我发现的唯一麻烦是您的 POCO 属性和字段名称的一对一映射,以及当您的 key 未命名时 PK 和 FK 之间有时会逃避的规则 ID .但这就是我,我仍然没有完全理解这些规则。

关于c# - 在 C# 中将 DataTable 转换为 List<T>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19226203/

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