Вот тестовая страница (.NET 4), которую я создал, чтобы показать симптомы, которые я испытываю:
<%@ Page Language="C#" AutoEventWireup="true" ViewStateMode="Disabled" %>
<script runat="server">
protected void FormView1_DataBound(object sender, EventArgs e) {
System.Diagnostics.Debug.WriteLine("FormView1_DataBound");
}
</script>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
</head>
<body>
<form id="form1" runat="server">
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:MyConnectionString %>"
InsertCommand="INSERT INTO [User] ([First_Name], [Last_Name]) VALUES (@First_Name, @Last_Name)"
SelectCommand="SELECT * FROM [User] where [User_ID] = @User_ID">
<SelectParameters>
<asp:ControlParameter Name="User_ID" Type="Int32" ControlID="TextBox1" PropertyName="Text" />
</SelectParameters>
<InsertParameters>
<asp:Parameter Name="First_Name" Type="String" />
<asp:Parameter Name="Last_Name" Type="String" />
</InsertParameters>
</asp:SqlDataSource>
<asp:TextBox ID="TextBox1" runat="server" Text="351"></asp:TextBox>
<asp:FormView ID="FormView1" runat="server" DataKeyNames="User_ID" DataSourceID="SqlDataSource1"
DefaultMode="Insert" OnDataBound="FormView1_DataBound">
<InsertItemTemplate>
First_Name:
<asp:TextBox ID="First_NameTextBox" runat="server" Text='<%# Bind("First_Name") %>' />
<br />
Last_Name:
<asp:TextBox ID="Last_NameTextBox" runat="server" Text='<%# Bind("Last_Name") %>' />
<br />
<asp:LinkButton ID="InsertButton" runat="server" CausesValidation="True" CommandName="Insert"
Text="Insert" />
</InsertItemTemplate>
</asp:FormView>
<asp:Button ID="Button1" runat="server" Text="Button" />
</form>
</body>
</html>
Во-первых, я знаю, что в логике есть пробелы. Я построил эту страницу только для того, чтобы показать симптомы и быть как можно короче. Позвольте мне объяснить настройки ...
У меня есть страница с режимом просмотра ОТКЛЮЧЕНО. У меня есть базовый SqlDataSource с инструкциями Insert и Select, а SelectStatement имеет ControlParameter (который для целей тестирования просто захватывает пустое текстовое поле).
У меня есть FormView по умолчанию для режима вставки. [Для краткости я исключил ItemTemplate из моего примера, но на самом деле он есть. Есть моменты, когда мой FormView будет вставлять, и времена будут только для чтения. Проблема возникает в режиме вставки.]
Наконец, у меня есть внешняя кнопка, которая выполняет обратную передачу без вставки. Эта фиктивная кнопка просто представляет выполнение обратной передачи, которую вы не хотите вставлять. (На самом деле, я делаю некоторые проверки на стороне сервера.)
Проблема проста: когда вы нажимаете кнопку и выполняете обратную передачу, FormView привязывает данные дважды. Вы можете видеть это из события FormView1_DataBound, которое я добавил. Таким образом, если вы попытаетесь ввести значения в поля «Имя» или «Фамилия», а затем нажмите кнопку, введенная вами информация будет потеряна. Этого не должно быть, поскольку идея заключается в том, что если проверка на стороне сервера не удалась, вы можете исправить и повторить попытку, не вводя заново все поля.
Я точно знаю, что это вызвано ControlParameter, и я даже знаю, почему: он ожидает, что ViewState включен. Поскольку он отключен, когда он запускает свой метод OnEvaluate, он видит, что возвращаемое значение отличается от значения, «хранящегося в viewstate» (т. Е. NULL, поскольку значения нет). Это приводит к тому, что OnParameterChanged выбрасывает, а FormView привязывает данные во второй раз ... даже если он находится в режиме вставки и даже не касается оператора Select. (Если бы вы добавили прослушиватель события Selecting в SqlDataSource, он бы даже не выдавал. FormView снова связывается даром.)
Если вы просто сделаете ControlParameter параметром со значением по умолчанию, проблема исчезнет. FormView будет связываться только один раз, а ваши значения будут сохранены после обратной передачи.
Итак, мой вопрос: как вы можете обойти эту досадную проблему?
Прямо сейчас, единственный способ исправить это:
Используйте только bland asp: Parameter, затем реализуйте событие SqlDataSource OnSelecting, чтобы установить значения, когда они действительно необходимы. Мне не нравится это решение, так как я вынужден присматривать за SqlDataSource и не позволяет мне использовать полезные параметры, такие как ControlParameter, SessionParameter, QueryStringParameter и т. Д.
Включите режим просмотра ... тьфу. Я знаю, что могу включить его только на SqlDataSource, но я действительно чувствую, что мне не нужно, когда его функциональность мне не нужна. Мне никогда не нужно запоминать состояние SqlDataSource и его параметры.
Откажитесь от проклятого SqlDataSource и вернитесь к SqlCommands для кровавого всего ... что я бы предпочел не делать, поскольку он так хорошо интегрируется во все другие элементы управления ASP, такие как FormView, GridView, DropDownList и т.д.
Я более чем готов создать собственный класс для чего угодно. Черт, я пытался создать свой собственный параметр, но корневой параметр настолько заблокирован, что я не вижу пути к чертовой функциональности ViewState & OnParameterChanged.
Я действительно надеюсь, что упускаю что-то слепо очевидное.
Редактировать 1: Я придумал самые хакерские решения. Я сделал свой собственный SqlDataSource. Тогда OnLoad:
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 && !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);
}
По сути, я взламываю значение, которое должно быть в состоянии просмотра, обманом заставляя его думать, что значение не изменилось. Это работает, но это так ужасно. Я ненавижу полагаться на Reflection, чтобы дать мне решение. Это не совсем будущее.
Я все еще надеюсь, что есть более простое решение, чем это.