Note that there are some explanatory texts on larger screens.

plurals
  1. POSqlDataSource + Disabled Viewstate = Double DataBind
    primarykey
    data
    text
    <p>Here's a test page (.NET 4) I built to show the symptom I'm experiencing:</p> <pre><code>&lt;%@ Page Language="C#" AutoEventWireup="true" ViewStateMode="Disabled" %&gt; &lt;script runat="server"&gt; protected void FormView1_DataBound(object sender, EventArgs e) { System.Diagnostics.Debug.WriteLine("FormView1_DataBound"); } &lt;/script&gt; &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt; &lt;html xmlns="http://www.w3.org/1999/xhtml"&gt; &lt;head runat="server"&gt; &lt;/head&gt; &lt;body&gt; &lt;form id="form1" runat="server"&gt; &lt;asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="&lt;%$ ConnectionStrings:MyConnectionString %&gt;" InsertCommand="INSERT INTO [User] ([First_Name], [Last_Name]) VALUES (@First_Name, @Last_Name)" SelectCommand="SELECT * FROM [User] where [User_ID] = @User_ID"&gt; &lt;SelectParameters&gt; &lt;asp:ControlParameter Name="User_ID" Type="Int32" ControlID="TextBox1" PropertyName="Text" /&gt; &lt;/SelectParameters&gt; &lt;InsertParameters&gt; &lt;asp:Parameter Name="First_Name" Type="String" /&gt; &lt;asp:Parameter Name="Last_Name" Type="String" /&gt; &lt;/InsertParameters&gt; &lt;/asp:SqlDataSource&gt; &lt;asp:TextBox ID="TextBox1" runat="server" Text="351"&gt;&lt;/asp:TextBox&gt; &lt;asp:FormView ID="FormView1" runat="server" DataKeyNames="User_ID" DataSourceID="SqlDataSource1" DefaultMode="Insert" OnDataBound="FormView1_DataBound"&gt; &lt;InsertItemTemplate&gt; First_Name: &lt;asp:TextBox ID="First_NameTextBox" runat="server" Text='&lt;%# Bind("First_Name") %&gt;' /&gt; &lt;br /&gt; Last_Name: &lt;asp:TextBox ID="Last_NameTextBox" runat="server" Text='&lt;%# Bind("Last_Name") %&gt;' /&gt; &lt;br /&gt; &lt;asp:LinkButton ID="InsertButton" runat="server" CausesValidation="True" CommandName="Insert" Text="Insert" /&gt; &lt;/InsertItemTemplate&gt; &lt;/asp:FormView&gt; &lt;asp:Button ID="Button1" runat="server" Text="Button" /&gt; &lt;/form&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>Firstly I know there's gaps in logic. I built this page only to show the symptoms and be as short as possible. Let me explain the setup...</p> <p>I have a page with the viewstatemode DISABLED. I have a basic SqlDataSource with Insert and Select statements, and the SelectStatement has a ControlParameter (which, for testing purposes, just grabs from a dummy text box).</p> <p>I have a FormView that's default to Insert mode. [For brevity I've left off an ItemTemplate from my example, but in reality there is one. There are times where my FormView will be inserting and times it will be readonly. The problem occurs in Insert mode.]</p> <p>Finally, I have an external button that does a postback without inserting. This dummy button just represents doing a postback where you don't want to insert. (In reality, I do some server-side validation.)</p> <p>The problem is simple: when you click the button and postback, the FormView databinds twice. You can see this from the FormView1_DataBound event I added. Thus, if you try entering values in the First Name or Last Name fields, then click the button, the information you entered will be lost. It shouldn't be, since the idea is that if server-side validation fails you can fix and retry without having to re-enter all the fields.</p> <p>I know for sure this is caused by the ControlParameter and I even know why: it expects the ViewState to be on. Because it's off, when it runs its OnEvaluate method, it sees the value returned is different from the value "stored in the viewstate" (ie, NULL, since there is no value). That causes OnParameterChanged to throw, and the FormView to databind a second time....even though it's in Insert mode and isn't even touching the Select statement. (If you were to add a Selecting event listener to the SqlDataSource, it wouldn't even throw. The FormView is binding again for nothing.)</p> <p>If you simply make the ControlParameter a Parameter with a DefaultValue, the problem will go away. The FormView will only bind once and your form values will be retained after the postback.</p> <p>So my question is, how can you get around this annoying problem? Right now, the only way I can see to fix it are:</p> <ul> <li><p>Use only the bland asp: Parameter, then implement the SqlDataSource OnSelecting event to set the values when they're actually needed. I don't like this solution since I have to babysit the SqlDataSource and prevents me from using the useful parameters like ControlParameter, SessionParameter, QueryStringParameter, etc.</p></li> <li><p>Turn on the viewstate...ugh. I know I can turn it on on just the SqlDataSource, but I really feel I shoulnd't need to when it's functionality I don't want. I never need to remember the state of the SqlDataSource nor it's parameters.</p></li> <li><p>Give up on the damn SqlDataSource and go back to SqlCommands for bloody everything...which I'd also prefer not to do since it integrates so well into all of ASP's other controls like FormView, GridView, DropDownList, etc.</p></li> </ul> <p>I'm more than willing to build a custom class for anything. Hell I've tried to make my own custom Parameter, but the root Parameter is so locked down I can't see a way around the damn ViewState &amp; OnParameterChanged functionality.</p> <p>I really do hope I'm missing something blindly obvious.</p> <hr> <p>Edit 1: I came up with the hackiest of hacky solutions. I made my own SqlDataSource. Then, OnLoad:</p> <pre><code>protected override void OnLoad(EventArgs e) { foreach (Parameter p in SelectParameters) { Type t = p.GetType(); MethodInfo mi = t.GetMethod("Evaluate", BindingFlags.NonPublic | BindingFlags.Instance); if (mi == null) return; while (t != null &amp;&amp; !t.Equals(typeof(Parameter))) { t = t.BaseType; } if (t == null) return; FieldInfo fi = t.GetField("_viewState", BindingFlags.NonPublic | BindingFlags.Instance); if (fi == null) return; object o = fi.GetValue(p); if (o == null) return; System.Web.UI.StateBag stateBag = (System.Web.UI.StateBag)o; stateBag["ParameterValue"] = mi.Invoke(p, new object[] { Context, this }); } Base.OnLoad(e); } </code></pre> <p>Basically I hack the value it's supposed to be into the viewstate, tricking it into thinking the value hasn't changed. This works, but it's so very ugly. I hate relying on Reflection to give me a solution. It's not exactly future-proof.</p> <p>I still hope there's an easier solution than this.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload