将数据读取器中的行转换为类型化结果
我正在使用返回数据读取器的第三方库。 我想要一种简单且尽可能通用的方法将其转换为对象列表。
例如,假设我有一个类“Employee”,它有 2 个属性 EmployeeId 和 Name,我希望将数据读取器(包含员工列表)转换为 List< 员工>.
我想我别无选择,只能迭代数据读取器的行,并将它们中的每一行转换为我将添加到列表中的 Employee 对象。 有更好的解决办法吗? 我使用的是 C# 3.5,理想情况下我希望它尽可能通用,以便它可以与任何类一起使用(DataReader 中的字段名称与各个对象的属性名称相匹配)。
I'm using a third party library which returns a data reader. I would like a simple way and as generic as possible to convert it into a List of objects.
For example, say I have a class 'Employee' with 2 properties EmployeeId and Name, I would like the data reader (which contains a list of employees) to be converted into List< Employee>.
I guess I have no choice but to iterate though the rows of the data reader and for each of them convert them into an Employee object that I will add to the List. Any better solution? I'm using C# 3.5 and ideally I would like it to be as generic as possible so that it works with any classes (the field names in the DataReader match the property names of the various objects).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
您真的需要一个列表吗?或者 IEnumerable 就足够了吗?
我知道您希望它是通用的,但更常见的模式是在接受数据行(或 IDataRecord)的目标对象类型上有一个静态工厂方法。 那看起来像这样:
。
然后,如果您确实需要一个列表而不是 IEnumerable,则可以对结果调用
.ToList()
。 我想您还可以使用泛型+委托来使该模式的代码更可重用。更新:我今天再次看到这个,想编写通用代码:
Do you really need a list, or would IEnumerable be good enough?
I know you want it to be generic, but a much more common pattern is to have a static Factory method on the target object type that accepts a datarow (or IDataRecord). That would look something like this:
.
Then if you really need a list rather than an IEnumerable you can call
.ToList()
on the results. I suppose you could also use generics + a delegate to make the code for this pattern more re-usable as well.Update: I saw this again today and felt like writing the generic code:
您可以构建一个扩展方法,例如:
并使用它:
Joel 的建议是一个很好的建议。 您可以选择返回
IEnumerable
。 改造上面的代码很容易:如果你想自动将列映射到属性,代码思路是一样的。 您只需将上述代码中的
generator
函数替换为一个查询typeof(T)
并通过读取匹配列使用反射设置对象属性的函数即可。 然而,我个人更喜欢定义一个工厂方法(就像 Joel 的答案中提到的那样)并将其委托传递给这个函数:You could build an extension method like:
and use it like:
Joel's suggestion is a good one. You can choose to return
IEnumerable<T>
. It's easy to transform the above code:If you want to automatically map the columns to properties, the code idea is the same. You can just replace the
generator
function in the above code with a function that interrogatestypeof(T)
and sets the properties on the object using reflection by reading the matched column. However, I personally prefer defining a factory method (like the one mentioned in Joel's answer) and passing a delegate of it into this function:虽然我不建议在生产代码中这样做,但您可以使用反射和泛型自动执行此操作:
然后您可以使用 CreateRecord() 从数据读取器中的字段实例化任何类。
Whilst I wouldn't recommend this for production code, but you can do this automatically using reflection and generics:
You can then use
CreateRecord<T>()
to instantiate any class from the fields in a data reader.我们实施了以下解决方案,感觉效果很好。 它非常简单,并且比映射器需要更多的接线。 然而,有时手动控制是很好的,老实说,只要接线一次就可以了。
简而言之:我们的域模型实现了一个接口,该接口有一个方法,该方法接受
IDataReader
并从中填充模型属性。 然后,我们使用泛型和反射创建模型的实例并对其调用Parse
方法。我们考虑使用构造函数并将
IDataReader
传递给它,但我们所做的基本性能检查似乎表明该界面始终更快(即使只是快一点)。 此外,接口路由通过编译错误提供即时反馈。我喜欢的一件事是,您可以使用
private set
来获取以下示例中的Age
等属性,并直接从数据库中设置它们。要调用它,您只需提供类型参数
We have implemented the following solution and feel it works pretty well. It's pretty simple and requires a bit more wiring up then what a mapper would do. However, sometimes it is nice to have the manual control and honestly, you wire up once and you're done.
In a nutshell: Our domain models implement an interface that has a method that takes in an
IDataReader
and populates the model properties from it. We then use Generics and Reflection to create an instance of the model and call theParse
method on it.We considered using a constructor and passing
IDataReader
to it, but the basic performance checks we did seemed to suggest the interface was consistently faster (if only by a little). Also, the interface route provides instant feedback via compilation errors.One of the things I like, is that you can utilize
private set
for properties likeAge
in the example below and set them straight from the database.To call it, you simply provide the type parameter
一个愚蠢的性能选项,如果您不介意外部依赖项(令人惊叹的
Fast Member
nuget 包):使用:
A stupidly performant option, should you not mind an external dependency (the amazing
Fast Member
nuget package):To use:
像 Magic
我个人讨厌在构造函数中进行手动映射,我也不喜欢自己进行反射。 因此,这里有另一个由精彩的(而且相当普遍的)Newtonsoft JSON 库提供的解决方案。
仅当您的属性名称与数据读取器列名称完全匹配时它才有效,但它对我们来说效果很好。
...假设您有一个数据读取器名称“yourDataReader”...
Like Magic
I personally HATE doing manual mapping in constructors, I'm also not a fan of doing my own reflection. So here's another solution courtesy of the wonderful (and fairly ubiquitous) Newtonsoft JSON lib.
It will only work if your property names exactly match the datareader column names, but it worked well for us.
...assumes you've got a datareader name "yourDataReader"...
最新版本的 C#/.NET5 提供了一个名为“源生成器”的新功能,我强烈建议每个人都去探索一下。
简而言之,它允许您在编译时生成 C# 代码,该代码将为您进行手动映射。 它显然比任何类型的反射(即使使用缓存和类似的速度黑客)快 20-30 倍 - 因为它实际上只是将属性分配给
IDataReader
字段。我在这里没有可以分享的简短代码片段,因为源生成器仍然有点复杂(它应该驻留在单独的程序集中,并且您必须学习 Roslyn API 等),但我确实相信这是一个值得每个人探索的很棒的功能ORM 世界及周边。
我开始尝试源生成器并喜欢它: https://github.com/jitbit/MapDataReader 感觉可以自由地从我的存储库中“窃取”一些代码。
Latest versions of C#/.NET5 offer a new awesome feature called "source generators", that I urge everyone to explore.
In a nutshell it allows you to generate C# code at compilation time, the code that will do manual mapping for you. It's obviously 20-30 times faster than any kind of reflection (even with caching and similar speed hacks) - because it literally just assigns properties to
IDataReader
fields.There's no short code snippet I could share here, since source generators are still a bit complicated (it should reside in a separate assembly and you will have to learn Roslyn API's etc.) but I do believe it's an awesome feature worth exploring for everyone in the ORM world and around.
I started experimenting with source generators and loving it: https://github.com/jitbit/MapDataReader feel free to "steal" some code from my repo.
最简单的解决方案:
然后选择它们以便将它们映射到任何类型。
The simplest Solution :
Then select them in order to map them to any type.
对于.NET Core 2.0:
这是一个与.NET CORE 2.0配合使用的扩展方法,可以执行RAW SQL并将结果映射到任意类型的LIST:
用法:
For .NET Core 2.0:
Here is an extension method that works with .NET CORE 2.0 to execute RAW SQL and map results to LIST of arbitrary types:
USAGE:
我的版本
用法:
PgRoom is
Reader.GetTable 包含:
My version
Usage:
PgRoom is
Reader.GetTable contains:
我找到了这个解决方案。
I found this solution.
请检查此答案
使用反射 + lambda 表达式进行简单、快速的
IDataReader
到IEnumerable
转换https://stackoverflow.com/a/70321210/3343007
Please check this answer
Simple, Fast
IDataReader
toIEnumerable<T>
conversion using reflection + lambda expressionshttps://stackoverflow.com/a/70321210/3343007