ADO.NET 数据表与数据读取器

发布于 2024-08-25 04:33:38 字数 141 浏览 6 评论 0原文

如果您只需要显示数据而不需要操作数据,则 DataReader 比 DataTable 更有效。但是,要从数据访问层获取 DataReader,我是否应该将连接对象保持打开状态?我觉得这也是一个非常大的效率问题。那么是否有另一种方法可以充分利用 DataReader?

The DataReader is more efficient than a DataTable if you only need to show data but not manipulate it. However, to get a DataReader from the data access layer should I leave the connection object open? I think this is also a very big efficiency problem. So is there another approach to this in order to take full advantage of the DataReader?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

后来的我们 2024-09-01 04:33:39

是的,数据读取器绝对是最有效的 - 但您不想希望长时间保持连接打开!

  • 使用DataReader将数据读入实体对象;打开连接、读取数据、关闭连接
  • 执行您需要对业务对象执行的任何操作
  • ,将更改存储回来,例如,通过使用即席 SQL 查询、存储过程或您想要的任何其他内容;再次:打开连接,写回更改,关闭连接

这可能是您可以获得的最有效的方法 - 这是一些工作,一些无聊的代码等等,但它是尽可能快的。

如果您对开发人员的生产力比原始速度更感兴趣,为什么不使用某种 ORM 来完成所有这些无聊、烦人的来回映射呢?为您节省大量编码和维护的麻烦!

Yes, the data reader is definitely the most efficient - but you do not want to keep a connection open for a long period of time!

  • use the DataReader to read your data into an entity object; open the connection, read the data, close the connection
  • do whatever you need to do with your business object
  • store the changes back, e.g. by using an ad-hoc SQL query, a stored procedure, or whatever else you want; again: open the connection, write back the changes, close the connection

This is probably the most efficient you can get - it's a bit of work, some boring code, and all, but it's about as fast as it can be.

If you're more interested in developer productivity than raw speed, why not use some kind of an ORM to do all this boring, annoying mapping back and forth? Saves you a lot of coding and messy stuff to maintain!

眼藏柔 2024-09-01 04:33:39

我从来没有让 DataReader 暴露在外(脱离 DAL)。您在某处保持连接打开只是时间问题。另外,我几乎从未在一次调用中处理如此多的数据,而传递 DataTable 或 DataSet 会出现问题。

我们使用的是面向对象的语言,DAL 可以真正利用这一点。您的项目中应该只有一行代码来获取连接字符串。只有一个对象实际接触数据库(调用 ExecuteNonQuery、DA.Fill() 等)。

这还可以让您充分参与记录异常等工作,因为您只需执行一次。因此,在我用于所有项目中所有 DAL 对象的一个​​ DAL 基类中,我的逻辑是,如果 DAL 引发异常,则会将其记录到数据库中的表中。如果数据库日志记录失败,此日志记录将故障转移到文本文件。

So the code I see a lot looks like:
-   Start a try block
-   Make a SQLCommand
-   Get connection string.
-   Make Connection object
-   Open the connection
-   Get the data
-   Bind the data
-   Close the connection
-   Log error if exception

由于我封装了所有这些,因此获取数据的代码现在看起来像:(

GridView1.DataSource = cProgram.DB.getMyData();

或更可能是 BAL 对象位于两者之间)。 DB 是一个普通对象(非静态),但每个应用程序仅实例化一次。

I never let a DataReader out into the wild (out of the DAL). Only a matter of time before you are leaving connections open somewhere. Also, I’m almost never dealing with so much data on a single call where passing around a DataTable or DataSet presents a problem.

We are using an object oriented language and the DAL can really take advantage of this. There should only be one line of code in your project that gets the connection string. Only one object that actually touches the database (calls ExecuteNonQuery, DA.Fill(), etc.)

This also lets you get pretty involved with logging exceptions, etc. because you are only doing it once. So in the one DAL base class that I use for all of my DAL object in all of my project I have the logic that if the DAL throws an exception then it is logged to a table in my database. This logging fails-over to a text file if the database logging fails.

So the code I see a lot looks like:
-   Start a try block
-   Make a SQLCommand
-   Get connection string.
-   Make Connection object
-   Open the connection
-   Get the data
-   Bind the data
-   Close the connection
-   Log error if exception

Since I encapsulate all of this, my code to get the data now looks like:

GridView1.DataSource = cProgram.DB.getMyData();

(or more likely a BAL object sits between the 2). The DB is a normal object (not static), but it only instantiated once per application.

厌味 2024-09-01 04:33:39

让您的数据层返回对象,而不是数据表或数据读取器。我建议您使用数据读取器填充对象。

Let your data layer return objects, not datatables or data readers. I would suggest you populate your objects using a data reader.

错爱 2024-09-01 04:33:39

我通常做的是使用 CommandBehavior.CloseConnection 打开阅读器。然后我运行读取器并将数据读入我自己的对象模型或列表或内存中的任何数据,然后关闭读取器。它与数据表做了很多相同的事情,但我只是讨厌处理臃肿且松散类型的数据结构。

What I usually do is open the reader with CommandBehavior.CloseConnection. Then I run through the reader and read the data into my own object model or list or whatever in memory with the data and then close the reader. It does much of the same stuff as a data table, but I just hate dealing with bloated and loosely-typed data stuctures.

倾听心声的旋律 2024-09-01 04:33:39

让我们有一个愚蠢的基准来检查 DataReader(.Net 版本 4)的速度有多快。
我从数据库 (SQL Server 2000) 中获取一条记录并读取其所有字段。我重复这个过程1000次。 DataReader 花费了 17.3327585 秒,DataTable 花费了 18.37320156 秒,因此对于 1000 次读取,DataReader 比 DataTable 快约 1.04 秒。

因此,如果 DataReader 优于 DataTable,则性能将提高 0.00104 秒。

看看 DataSet 比 DataReader 慢是因为……吗?

Let us have a silly benchmark to check how much faster the DataReader (.Net version 4) is.
I fetched one record from the database (SQL Server 2000) and read all its fields. I repeated this process 1000 times. DataReader took 17.3327585 seconds and DataTable took 18.37320156 and so DataReader is ~1.04 seconds faster than DataTable for 1000 reads.

So, one would get a performance gain of 0.00104 seconds if DataReader is preferred over DataTable.

Look at Is DataSet slower than DataReader due to…? as well

最冷一天 2024-09-01 04:33:39

当我之前研究这个问题时,我相信我发现 DataReader 和 DataTable 之间的性能差异是微不足道的,除了可能非常大量的数据。从那时起,我通常使用 DataTable,因为它功能更齐全,可以断开连接等。

When I researched this before, I believe I discovered that the performance difference between DataReader and DataTable was trivial, except for perhaps very large amounts of data. Since then, I have typically used DataTable as it's more full-featured, can be worked with disconnected, etc.

漆黑的白昼 2024-09-01 04:33:39

如果您想完全抽象 ADO.NET 的连接和仪式,DataReader 是一个小挑战。我真的不喜欢我的数据工具有一个松散的开放连接,希望 DataReader 被释放(假设您使用了选项CommandBehavior.CloseConnection)。此外,当使用大量 DataReader 时,很难池化连接,因为在前一个 DataReader 关闭之前您无法对连接执行任何操作。它们不能轻易传递。您的数据工具不是真正的抽象。

另一方面,数据表非常灵活,可以编写一些非常高效、清晰的代码。 Linq-To-DataTable 很棒。幸运的是,DataTable 实际上相当高效。对于非大型结果集,它几乎与数据读取器一样快。 (当然,这取决于您正在做什么。)我越来越多地从我的数据工具而不是读者中获取数据表。它确实让生活变得简单。我可以继续使用同一个打开的连接。数据工具中没有“状态”。

获取 DataReader 的代码非常简单。因此,当我确实需要 DataReader(不经常)时,我只需让 DAL 向我提供连接,然后我自己获取 DataReader。

If you want to completely abstract the connections and ceremony of ADO.NET, the DataReader is a small challenge. I really don't like my data tool having an open connection on the loose, hoping the DataReader is disposed (assuming you used option CommandBehavior.CloseConnection). Also, when using a lot of DataReaders, it's difficult to pool connections, since you can't do anything with the connection until the previous DataReader is closed. They can't be passed around easily. Your Data-tool is not a true abstraction.

DataTables on the other hand are extremely flexible and can make for some very efficient, clear code. Linq-To-DataTable is great. Luckily the DataTable is actually pretty efficient. For non-huge result-sets it is almost as fast as the datareader. (it depends of course on exactly what you're doing.) More and more I am getting DataTables from my data-tool rather than Readers. It really makes life simple. I can continue to use the same open connection. There's no "state" in data-tool.

The code for getting a DataReader is very simple. So when I really do need a DataReader (not often), I just let my DAL hand me my connection and I get my DataReader myself.

素手挽清风 2024-09-01 04:33:39

直接来自文档

当您需要检索多行数据时,以便您可以
以其他方式显示或处理数据,有两种基本方法
选择。您可以使用 DataSet 对象或 DataReader 对象。

DataReader 方法通常更快,因为它避免了
与创建 DataSet 对象相关的开销。这
与 DataSet 对象相关的开销包括创建
DataSet 子对象,例如 DataTable、DataRow 和 DataColumn。
然而,DataReader 的灵活性较差,不太适合
对于必须缓存数据并将数据传递给的情况
具有多层的应用程序中的组件。

注意:用于填充DataSet的DataAdapter内部使用DataReader。

满足以下条件时使用 DataReader:

• 您需要对数据进行只进、只读访问(消防水带
场景),并且您希望尽快访问数据,并且
您不需要缓存它。

• 您有一个数据容器,例如业务组件,您可以
将数据放入。

满足以下条件时使用数据集:

• 您必须在层之间缓存或传递数据。

• 您需要 XML 数据的内存中关系视图或
非 XML 操作。

• 您想要更新部分或全部检索到的行,并且您想要
使用 SqlDataAdapter 类的批量更新功能。

• 您必须将数据绑定到 DataReader 无法绑定的控件类型
受到约束。许多能够进行数据绑定的 Windows 窗体控件
需要一个实现 IList 接口的数据源。数据集
实现 IList,但 DataReader 实现 IEnumerable。
IEnumerable 支持将数据绑定到大多数 Web 窗体控件,但不支持
某些 Windows 窗体控件。检查数据源要求
您要绑定的特定控件类型。

• 您必须同时访问多组数据,并且您
不想占用开放的服务器资源。

虽然谈论 DataSet 基本上大部分内容也适用于 DataTable。从效率的角度来看,这是来自 msdn 本身的罕见基准测试。底线是 DataReader 稍微快一点,如果重要的话..

也请参阅此相关问题,其中建议了一些很酷的 ORM 和基准测试。

Straight from the documentation:

When you need to retrieve multiple rows of data so that you can
display or process the data in some other way, you have two basic
choices. You can use a DataSet object or a DataReader object.

The DataReader approach is generally quicker because it avoids the
overhead that is associated with creating a DataSet object. The
overhead that is associated with a DataSet object includes creating
DataSet subobjects such as DataTables, DataRows, and DataColumns.
However, the DataReader provides less flexibility, and is less suited
to situations where you have to cache data and pass the data to
components in an application that has multiple tiers.

Note: The DataAdapter used to fill the DataSet uses a DataReader internally.

Use a DataReader when the following conditions are true:

• You need forward-only, read-only access to data (the fire hose
scenario), and you want to access the data as quickly as possible, and
you do not need to cache it.

• You have a data container such as a business component that you can
put the data in.

Use a DataSet when the following conditions are true:

• You have to cache or pass the data between layers.

• You require an in-memory relational view of the data for XML or
non-XML manipulation.

• You want to update some or all the retrieved rows, and you want to
use the batch update facilities of the SqlDataAdapter class.

• You have to bind data to a control type that the DataReader cannot
be bound to. Many Windows Forms controls capable of data binding
require a data source that implements the IList interface. The DataSet
implements IList, but the DataReader implements IEnumerable.
IEnumerable supports data binding to most Web Form controls but not to
certain Windows Forms controls. Check the data source requirements for
the particular control type that you want to bind.

• You have to access multiple sets of data at the same time, and you
do not want to hold open server resources.

Though talking about DataSet basically most of it holds for DataTable too. From an efficiency standpoint, here is rare benchmarking from msdn itself. The bottom line is DataReader is marginally faster, and if it matters..

See this related question too which suggests some cool ORMs and benchmarking.

我是有多爱你 2024-09-01 04:33:39

这与我在此处发布的内容相同。

我用各种方法自己做了一些基准测试:

public DataTable Read1(string query)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open();
        var table = new DataTable();
        using (var r = cmd.ExecuteReader())
            table.Load(r);
        return table;
    }
}

public DataTable Read2<S>(string query) where S : IDbDataAdapter, IDisposable, new()
{
    using (var da = new S())
    {
        using (da.SelectCommand = conn.CreateCommand())
        {
            da.SelectCommand.CommandText = query;
            da.SelectCommand.Connection.Open();
            DataSet ds = new DataSet();
            da.Fill(ds);
            return ds.Tables[0];
        }
    }
}

public IEnumerable<S> Read3<S>(string query, Func<IDataRecord, S> selector)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open();
        using (var r = cmd.ExecuteReader())
            while (r.Read())
                yield return selector(r);
    }
}

public S[] Read4<S>(string query, Func<IDataRecord, S> selector)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open();
        using (var r = cmd.ExecuteReader())
            return ((DbDataReader)r).Cast<IDataRecord>().Select(selector).ToArray();
    }
}

public List<S> Read5<S>(string query, Func<IDataRecord, S> selector)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open(); 
        using (var r = cmd.ExecuteReader())
        {
            var items = new List<S>();
            while (r.Read())
                items.Add(selector(r));
            return items;
        }
    }
}

1 和 2 返回 DataTable,而其余的强类型结果集,所以它完全不是苹果对苹果,但我同时相应地计时。

仅是要点:

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
    Read1(query); // ~8900 - 9200ms

    Read1(query).Rows.Cast<DataRow>().Select(selector).ToArray(); // ~9000 - 9400ms

    Read2<MySqlDataAdapter>(query); // ~1750 - 2000ms

    Read2<MySqlDataAdapter>(query).Rows.Cast<DataRow>().Select(selector).ToArray(); // ~1850 - 2000ms

    Read3(query, selector).ToArray(); // ~1550 - 1750ms

    Read4(query, selector); // ~1550 - 1700ms

    Read5(query, selector); // ~1550 - 1650ms
}

sw.Stop();
MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());

查询返回大约 1200 行和 5 个字段(运行 100 次)。除了 Read1 之外,一切都表现良好。其中我更喜欢 Read3 ,它延迟返回数据,如枚举的。如果您只需要枚举它,这对记忆很有帮助。要在内存中保存集合的副本,最好根据需要使用 Read4Read5

This is the same as I posted here.

I did some benchmarking myself with various approaches:

public DataTable Read1(string query)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open();
        var table = new DataTable();
        using (var r = cmd.ExecuteReader())
            table.Load(r);
        return table;
    }
}

public DataTable Read2<S>(string query) where S : IDbDataAdapter, IDisposable, new()
{
    using (var da = new S())
    {
        using (da.SelectCommand = conn.CreateCommand())
        {
            da.SelectCommand.CommandText = query;
            da.SelectCommand.Connection.Open();
            DataSet ds = new DataSet();
            da.Fill(ds);
            return ds.Tables[0];
        }
    }
}

public IEnumerable<S> Read3<S>(string query, Func<IDataRecord, S> selector)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open();
        using (var r = cmd.ExecuteReader())
            while (r.Read())
                yield return selector(r);
    }
}

public S[] Read4<S>(string query, Func<IDataRecord, S> selector)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open();
        using (var r = cmd.ExecuteReader())
            return ((DbDataReader)r).Cast<IDataRecord>().Select(selector).ToArray();
    }
}

public List<S> Read5<S>(string query, Func<IDataRecord, S> selector)
{
    using (var cmd = conn.CreateCommand())
    {
        cmd.CommandText = query;
        cmd.Connection.Open(); 
        using (var r = cmd.ExecuteReader())
        {
            var items = new List<S>();
            while (r.Read())
                items.Add(selector(r));
            return items;
        }
    }
}

1 and 2 returns DataTable while the rest strongly typed result set, so its exactly not apples to apples, but I while time them accordingly.

Just the essentials:

Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 100; i++)
{
    Read1(query); // ~8900 - 9200ms

    Read1(query).Rows.Cast<DataRow>().Select(selector).ToArray(); // ~9000 - 9400ms

    Read2<MySqlDataAdapter>(query); // ~1750 - 2000ms

    Read2<MySqlDataAdapter>(query).Rows.Cast<DataRow>().Select(selector).ToArray(); // ~1850 - 2000ms

    Read3(query, selector).ToArray(); // ~1550 - 1750ms

    Read4(query, selector); // ~1550 - 1700ms

    Read5(query, selector); // ~1550 - 1650ms
}

sw.Stop();
MessageBox.Show(sw.Elapsed.TotalMilliseconds.ToString());

The query returned about 1200 rows and 5 fields (run for 100 times). Apart from Read1 all performed well. Of all I prefer Read3 which returns data lazily, as enumerated. This is great for memory if you only need to enumerate it. To have a copy of the collection in memory, you're better off with Read4 or Read5 as you please.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文