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

时间:2010-03-10 19:04:33

标签: .net asp.net ado.net

如果您只需要显示数据但不需要操作数据,则DataReader比DataTable更有效。但是,要从数据访问层获取DataReader,我应该打开连接对象吗?我认为这也是一个非常大的效率问题。那么为了充分利用DataReader,还有另一种方法吗?

9 个答案:

答案 0 :(得分:11)

是的,数据阅读器绝对是最有效的 - 但你希望长时间保持连接开放!

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

这可能是你能获得的最有效率 - 它有点工作,有些无聊的代码,以及所有这些,但它的速度和它一样快。

如果您对开发人员的工作效率比对原始速度更感兴趣,为什么不使用某种ORM来做所有这些无聊,烦人的来回映射?保存许多编码和杂乱的东西来维护!

答案 1 :(得分:3)

我从不让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对象位于2之间)。 DB是普通对象(非静态对象),但每个应用程序只实例化一次。

答案 2 :(得分:2)

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

答案 3 :(得分:1)

我通常使用CommandBehavior.CloseConnection打开阅读器。然后我浏览阅读器并将数据读入我自己的对象模型或列表或内存中的数据,然后关闭阅读器。它与数据表有很多相同的东西,但我只是讨厌处理膨胀和松散类型的数据结构。

答案 4 :(得分:1)

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

因此,如果DataReader优于DataTable,那么将获得0.00104秒的性能提升。

同时查看Is DataSet slower than DataReader due to…?

答案 5 :(得分:0)

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

另一方面,DataTables非常灵活,可以提供一些非常高效,清晰的代码。 Linq-To-DataTable很棒。幸运的是DataTable是actually pretty efficient。对于非巨大的结果集,它几乎与datareader一样快。 (当然,这取决于你正在做什么。)越来越多我从数据工具而不是读者那里获得DataTables。它真的让生活变得简单。我可以继续使用相同的开放连接。数据工具中没有“状态”。

获取DataReader的代码非常简单。所以,当我真的需要一个DataReader(不经常)时,我只是让我的DAL把我的连接交给我,我自己拿到了DataReader。

答案 6 :(得分:0)

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

答案 7 :(得分:0)

Straight from the documentation

  

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

     

DataReader方法通常更快,因为它避免了   与创建DataSet对象相关联的开销。该   与DataSet对象关联的开销包括创建   DataSet子对象,如DataTables,DataRows和DataColumns。   但是,DataReader提供的灵活性较低,并且不太适合   在必须缓存数据并将数据传递给的情况下   具有多个层的应用程序中的组件。

     

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

     

满足以下条件时使用DataReader:

     

•您需要只进,只读的数据访问权限(消防水带   方案),并且您希望尽快访问数据,并且   你不需要缓存它。

     

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

     

满足以下条件时使用DataSet:

     

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

     

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

     

•您希望更新部分或全部检索到的行   使用SqlDataAdapter类的批量更新工具。

     

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

     

•您必须同时访问多组数据   不想保留打开的服务器资源。

虽然谈论DataSet基本上大部分也适用于DataTable。从效率的角度来看,here is rare benchmarking from msdn itself。底线是DataReader略快,如果重要的话......

See this related question也暗示了一些很酷的ORM和基准测试。

答案 8 :(得分:0)

这与我发布的here相同。

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

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