问题
I have a Gridview with an ObjectDataSource, sort of like this:
<asp:GridView ID="myGridView" runat="server" AllowSorting="True" ondatabound="myGridView_DataBound" cssClass="coolTable"
OnRowDataBound="myGridView_RowDataBound"
AllowPaging="True" AutoGenerateColumns="False" DataSourceID="myDataSource">
<PagerSettings mode="NextPreviousFirstLast">
</PagerSettings>
</asp:GridView>
<asp:ObjectDataSource ID="myDataSource" runat="server"
SelectMethod="GetSearchResults" EnablePaging="true"
StartRowIndexParameterName="startIndex"
MaximumRowsParameterName="pageSize"
SortParameterName="sortBy" SelectCountMethod="GetSearchCount" >
</asp:ObjectDataSource>
Andy my function, GetSearchResults, is called, so that's all good. The problem is that in GetSearchResults, I want to use other variables besides the ones passed to it, but they do not seem to have values when GetSearchResults runs. I stepped through in the debugger, and I can see that Page_Load is called before GetSearchResults - but referencing any of the controls on my page throws an error, and fields belonging to my page have no value (even though I set them at Page_Load).
I read ASP.Net Object Data Source - Data Binding and skimmed the Page Life Cycle Overview linked to there, but still do not understand why my other variables are not available.
But here is my real question - I don't really care why they aren't available; I would like to know a good pattern to make values available (that were set during Page_Load) to my GetSearchResults function. Currently I'm saving things in session, but that seems kind of ridiculous.
[EDIT to add background] I am doing some database queries on Page_Load to set some values which in turn affect the layout and content of my page. Those values are also used to modify the selection criteria for the data in my GridView. I started using the ObjectDataSource because to allow me to efficently page through a lot of records (https://msdn.microsoft.com/en-us/library/bb445504.aspx) but didn't initially understand that a new instance of the Page is created and the method called after that - I was thinking it was handled like a postback. I was hoping to avoid saving those interim values in form fields, session variables, etc. It looks like maybe the only way to do that is to fill the Gridview during the normal page lifecycle, but it looks like that means giving up the automatic paging of the Gridview.
回答1:
In this question Andy points why the page elements are not available in SelectMethod, here is the MSDN explanation :
If it is an instance method, the business object is created and destroyed each time the method that is specified by the SelectMethod property is called.
But, in my trials I can access the page variables by current context's Request.Forms collection. It's a little confusing. But if you define some html form elements, you can access them in SelectMethod by Request.Forms collection. Also you can access server variables' values, but, if you dig into it, you can see their name depends on the hierarchy in page control tree.
Here is my trial, in my aspx file :
<input name="txtCriteria"></input>
<asp:Button runat="server" ID="btnPostSearch" OnClick="btnPostSearch_Click"/>
<asp:GridView ID="myGridView" runat="server" AllowSorting="True"
AllowPaging="True" AutoGenerateColumns="False" DataSourceID="myDataSource">
<Columns>
<asp:BoundField DataField="Result" />
</Columns>
<PagerSettings mode="NextPreviousFirstLast">
</PagerSettings>
</asp:GridView>
<asp:ObjectDataSource ID="myDataSource" runat="server" TypeName=""
SelectMethod="GetSearchResults">
</asp:ObjectDataSource>
And this is the code behind file :
public List<SearchResult> GetSearchResults()
{
string criteria = string.Empty;
if (HttpContext.Current.Request["txtCriteria"] != null)
{
criteria = HttpContext.Current.Request["txtCriteria"];
}
List<SearchResult> searchResults = new List<SearchResult>();
searchResults.Add(new SearchResult() { Result = "trial 1 " + criteria });
searchResults.Add(new SearchResult() { Result = "trial 2 " + criteria });
return searchResults;
}
protected void btnPostSearch_Click(object sender, EventArgs e)
{
myGridView.DataBind();
}
回答2:
When you start using ASP.NET's DataSourceControls (ObjectDataSource, SqlDataSource, AccessDataSource, etc.) and their counterparts DataBoundControls (DropDownList, DetailsView, ListView, FormView, GridView), you really want to forget about ASP.NET lifecycle (and stop pulling hair), and if you do it well, you can even forget about code-behind code (aspx.cs files) because now the system can be pretty automatic between datasources and databound controls.
In fact this paradigm (that has appeared only starting with .NET 2.0) really helps to focus on declarative HTML-like code.
A DataSourceControl uses a collection of objects of type Parameter as parameters to the methods it uses. For example, the ObjectDataSource's SelectMethod uses the SelectParameters property (of ParameterCollection type).
You can define these parameters declaratively. Let's take an example:
<form id="form1" runat="server">
<div>
<asp:TextBox ID="MyTextBox" Text="3" runat="server" />
<asp:Button runat="server" Text="Run" />
<asp:GridView ID="myGridView" runat="server" DataSourceID="myDataSource" />
<asp:ObjectDataSource ID="myDataSource" runat="server"
SelectMethod="GetSearchResults"
TypeName="WebApplication1.Code.MyModel">
<SelectParameters>
<asp:ControlParameter ControlID="MyTextBox" PropertyName="Text" Name="myCount" />
</SelectParameters>
</asp:ObjectDataSource>
</div>
</form>
Here, myDataSource defines a GetSearchResults
as the SelectMethod
(I've omitted your other parameters but the idea is the same) method on a MyModel
class in some class. It also defines a parameter named myCount
. This parameter is a ControlParameter (there are others): it will connect to the ASP.NET control MyTextBox
which happens to be defined as a TextBox control, and will use the TextBox's Text
property as the the value of the myCount
parameter.
Here is the code of the object model:
namespace WebApplication1.Code
{
public class MyModel
{
public string Name { get; set; }
public static IEnumerable GetSearchResults(int myCount)
{
for (int i = 0; i < myCount; i++)
{
yield return new MyModel { Name = "item " + i };
}
}
}
}
As you see, the method also has a myCount
parameter defined (it's case sensitive, and yes, ASP.NET will convert automatically from string
to int
, it's almost magic, it uses TypeConverters under the hood), so everything will work as expected, without any code-behind code. It's some kind of an MV pattern (M model V view) and DataSourceControl/DataBoundControl do the binding.
So, you have to think this way now, and use parameters. If the provided list of parameters (QueryString, Cookie, Form, Profile, Route, Session) is no enough, you can provide your own. For example I can define a special parameter that will get myCount randomly (it's just an example :-):
I can use it like this, and I can define custom arguments for this parameter:
<%@ Register Namespace="WebApplication1.Code" TagPrefix="my" Assembly="WebApplication1" %>
...
<SelectParameters>
<my:RandomParameter Name="myCount" Min="10" Max="20" />
</SelectParameters>
The custom parameter type code:
public class RandomParameter : Parameter
{
protected override object Evaluate(HttpContext context, Control control)
{
// you can get to page or environment from here with 'context' and 'control' parameters
return new Random(Environment.TickCount).Next(Min, Max);
}
[DefaultValue(1)]
public int Min
{
get
{
object o = ViewState["Min"];
return o is int ? (int)o : 1;
}
set
{
if (Min != value)
{
ViewState["Min"] = value;
OnParameterChanged();
}
}
}
[DefaultValue(10)]
public int Max
{
get
{
object o = ViewState["Max"];
return o is int ? (int)o : 10;
}
set
{
if (Max != value)
{
ViewState["Max"] = value;
OnParameterChanged();
}
}
}
}
来源:https://stackoverflow.com/questions/32642032/wrestling-with-objectdatasource-other-controls-and-variables-not-defined