【问题标题】:ListView + ObjectDataSource SelectMethod called twice when EnableViewState="false"当 EnableViewState="false" 时,ListView + ObjectDataSource SelectMethod 调用了两次
【发布时间】:2010-11-29 23:08:42
【问题描述】:

注意:这个问题已经完全修改,现在我有了一个更简单的例子。

我设置了一个示例页面,其中只有一个 ListViewObjectDataSource。第一次出现页面时(!IsPostBack),我的GetList 方法被调用一次。在分页 (IsPostBack) 之后,GetList 方法被调用了两次——第一次使用旧的分页值,第二次使用新的值。

如果我在ListView 上设置EnableViewState="true",那么GetList 方法只会被调用一次。在我看来,ListView 想要一个“初始状态”,它要么从 ViewState 获得,要么通过重新运行该方法获得。

有什么方法可以禁用 ListView 上的 ViewState 并防止 SelectMethod 被调用两次?

ASPX 页面:

    <asp:ListView ID="TestListView" runat="server" DataSourceID="ODS" EnableViewState="false">
        <LayoutTemplate>
            <asp:PlaceHolder ID="itemPlaceHolder" runat="server" />

            <asp:DataPager ID="TestPager" runat="server" PageSize="10">
                <Fields>
                    <asp:NumericPagerField />
                </Fields>
            </asp:DataPager>
        </LayoutTemplate>
        <ItemTemplate>
            <div><%# Eval("Title") %></div>
        </ItemTemplate>
    </asp:ListView>

    <asp:ObjectDataSource ID="ODS" runat="server" SelectMethod="GetList" SelectCountMethod="GetListCount"
        TypeName="Website.Test" EnablePaging="true" />

ASPX 代码隐藏:

namespace Website
{
    public partial class Test : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        public IList<DataItem> GetList(int maximumRows, int startRowIndex)
        {
            return GetListEnumerable().Skip(startRowIndex).Take(maximumRows).ToList();
        }

        public IEnumerable<DataItem> GetListEnumerable()
        {
            for (int i = 0; i < 100; i++)
            {
                yield return new DataItem { Title = i.ToString() };
            }
        }

        public int GetListCount()
        {
            return 100;
        }
    }

    public class DataItem
    {
        public string Title { get; set; }
    }
}

【问题讨论】:

    标签: asp.net listview objectdatasource


    【解决方案1】:

    要么打开 ODS 缓存。

    <asp:ObjectDataSource ID="ODS" ... EnableCaching="true" />
    

    这样,只有在需要新数据时才会调用 GetList。回发到已经检索到数据的页面将使用缓存版本,而不是调用 GetList。

    或者将您的 DataPager 移出 ListView 并设置 PagedControlID 属性。

    【讨论】:

    • 我想到了缓存,但在我的情况下有无限的参数可能性,所以我希望缓存持续时间非常短(比如 10 秒)。如果用户在 10 秒后页面,它仍然会重复查询,这会降低页面速度。在这里启用 ViewState 将是一个更好的选择。将 DataPager 移出 ListView 并没有帮助——它仍然被双重查询。我想知道我是否可以直接或间接(通过反射)设置BaseDataBoundControl.RequiresDataBinding
    • 我尝试通过反射将RequiresDataBinding 设置为false,这样可以防止双重查询。但是,它只是发生的第一个查询(在分页之前),所以我不能使用它。我想我暂时将 ViewState 保持打开状态。
    • 我没有考虑参数。我不知道缓存是如何实现的。但是,将寻呼机移出列表视图对我来说是诀窍。我想这不是一个可靠的解决方案。 RequiresDataBinding 在几个地方被覆盖。如果时间允许,您可以尝试从 ListView 创建自己的控件并同时处理 CreateChildren 和 RequiresDataBinding。然后,总会有那个 ViewState... :)
    【解决方案2】:

    实际上你应该使用 OnSelecting 事件。

    发生的情况是 ObjectDataSource 调用方法 SelectMethod 两次

    1. 第一次获取数据。
    2. 下次它会得到计数。

    所以我认为你必须实现 OnSelecting 事件

    <asp:ObjectDataSource ID="ODS" runat="server" SelectMethod="GetList" SelectCountMethod="GetListCount" 
        OnSelecting="ods_Selecting">
        TypeName="Website.Test" EnablePaging="true" /> 
    

    然后在 ObjectDataSource 尝试调用 count 方法时取消该事件。

     protected void ods_Selecting(object sender,
                    ObjectDataSourceSelectingEventArgs e)
     {
          if (e.ExecutingSelectCount)
          {
               //Cancel the event   
               return;
          }
    }
    

    您可以查看以下链接中提到的完整实施 http://www.unboxedsolutions.com/sean/archive/2005/12/28/818.aspx

    希望这会有所帮助。

    【讨论】:

    • 在提供 SelectCountMethod 时不应调用 select 来获取计数。在上面的例子中,它在每次创建子控件时在回发和预渲染时被调用两次。
    • @Ruslan:如果你调试你可以看到 ods_Selecting 事件会被命中两次。
    • 这里有些混乱。我的SelectMethod 缓存了SelectCountMethod 使用的计数,因此数据库不会因此而被命中两次。有SelectMethodSelectCountMethodOnSelecting 事件。通常(启用ViewState),SelectMethodSelectCount 方法会被调用一次OnSelecting 被调用两次,一次是e.ExecutingSelectCount = false,然后是e.ExecutingSelectCount = true。在 ViewState 禁用 的情况下,SelectMethodSelectCountMethod 被调用两次OnSelecting 被调用 四次
    【解决方案3】:

    我有一个类似的问题,它的工作方式因浏览器而异。 IE 一种方式,所有其他浏览器一种方式.. 可能与您遇到的问题不同。

    我是这样解决的:

            protected void DropDownDataBound(object sender, EventArgs e)
        {
            // Issue with IE - Disable ViewState for IE browsers otherwhise the dropdown will render empty.
            DropDownList DDL = (DropDownList)sender;
            if (Request.Browser.Browser.Equals("IE", StringComparison.CurrentCultureIgnoreCase))
                DDL.ViewStateMode = System.Web.UI.ViewStateMode.Disabled;
            else
                DDL.ViewStateMode = System.Web.UI.ViewStateMode.Inherit;           
        }
    

    【讨论】:

    • 我的总是渲染(它永远不会为空)并且浏览器在这种情况下没有任何区别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-29
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    • 1970-01-01
    相关资源
    最近更新 更多