当你不能使用ViewState时你会怎么做?

时间:2022-01-15 00:12:58

I have a rather complex page that dynamically builds user controls inside of a repeater. This repeater must be bound during the Init page event before ViewState is initialized or the dynamically created user controls will not retain their state.

我有一个相当复杂的页面,可以在转发器内动态构建用户控件。在初始化ViewState之前,必须在Init页面事件期间绑定此转发器,否则动态创建的用户控件将不会保留其状态。

This creates an interesting Catch-22 because the object I bind the repeater to needs to be created on initial page load, and then persisted in memory until the user opts to leave or save.

这会创建一个有趣的Catch-22,因为绑定转发器的对象需要在初始页面加载时创建,然后保留在内存中,直到用户选择离开或保存。

Because I cannot use ViewState to store this object, yet have it available during Init, I have been forced to store it in Session.

因为我不能使用ViewState来存储这个对象,但是在Init期间它可用,我*将它存储在Session中。

This also has issues, because I have to explicitly null the session value during non postbacks in order to emulate how ViewState works.

这也有问题,因为我必须在非回发期间显式地使会话值为空,以便模拟ViewState的工作方式。

There has to be a better way to state management in this scenario. Any ideas?

在这种情况下,必须有一种更好的状态管理方法。有任何想法吗?

Edit: Some good suggestions about using LoadViewState, but I'm still having issues with state not being restored when I do that.

编辑:关于使用LoadViewState的一些好建议,但是当我这样做时,我仍然遇到状态未被恢复的问题。

Here is somewhat if the page structure

这有点是页面结构

Page --> UserControl --> Repeater --> N amount of UserControls Dynamicly Created.

Page - > UserControl - > Repeater - >动态创建的N量UserControl。

I put the overridden LoadViewState in the parent UserControl, as it is designed to be completely encapsulated and independent of the page it is on. I am wondering if that is where the problem is.

我将重写的LoadViewState放在父UserControl中,因为它被设计为完全封装并独立于它所在的页面。我想知道问题出在哪里。

10 个解决方案

#1


4  

The LoadViewState method on the page is definitely the answer. Here's the general idea:

页面上的LoadViewState方法绝对是答案。这是一般的想法:

protected override void LoadViewState( object savedState ) {
  var savedStateArray = (object[])savedState;

  // Get repeaterData from view state before the normal view state restoration occurs.
  repeaterData = savedStateArray[ 0 ];

  // Bind your repeater control to repeaterData here.

  // Instruct ASP.NET to perform the normal restoration of view state.
  // This will restore state to your dynamically created controls.
  base.LoadViewState( savedStateArray[ 1 ] );
}

SaveViewState needs to create the savedState array that we are using above:

SaveViewState需要创建我们上面使用的savedState数组:

protected override object SaveViewState() {
  var stateToSave = new List<object> { repeaterData, base.SaveViewState() };
  return stateToSave.ToArray();
}

Don't forget to also bind the repeater in Init or Load using code like this:

不要忘记使用以下代码绑定Init或Load中的转发器:

if( !IsPostBack ) {
  // Bind your repeater here.
}

#2


1  

This also has issues, because I have to explicitly null the session value during non postbacks in order to emulate how ViewState works.

这也有问题,因为我必须在非回发期间显式地使会话值为空,以便模拟ViewState的工作方式。

Why do you have to explicitly null out the value (aside from memory management, etc)? Is it not an option to check Page.IsPostback, and either do something with the Session variable or not?

为什么必须显式地清除值(除了内存管理等)?它不是检查Page.IsPostback的选项,是否可以使用Session变量执行某些操作?

#3


1  

I have always recreated my dynamic controls in the LoadViewState event. You can store the number of controls needed to be created in the viewstate and then dynamically create that many of them using the LoadControl method inside the LoadViewState event. In this event you have access to the ViewState but it has not been restored to the controls on the page yet.

我总是在LoadViewState事件中重新创建我的动态控件。您可以在viewstate中存储需要创建的控件数,然后使用LoadViewState事件中的LoadControl方法动态创建其中的许多控件。在这种情况下,您可以访问ViewState,但尚未恢复到页面上的控件。

#4


0  

@DancesWithBamboo:

If I dynamically bind the controls there, will ASP.NET automatically handle their state? The problem with doing it in Page_Load is that viewstate is already loaded and it does not add the dynamic controls in the repeater. I would think it would do the same in LoadViewState?

如果我动态绑定那里的控件,ASP.NET会自动处理它们的状态吗?在Page_Load中执行此操作的问题是viewstate已经加载,并且它不会在转发器中添加动态控件。我认为它会在LoadViewState中做同样的事情吗?

#5


0  

1) there's probably a way to get it to work... you just have to make sure to add your controls to the tree at the right moment. Too soon and you don't get ViewState. Too late and you don't get ViewState.

1)可能有办法让它工作......你只需要确保在适当的时候将控件添加到树中。太快了,你没有得到ViewState。太晚了,你没有得到ViewState。

2) If you can't figure it out, maybe you can turn off viewstate for the hole page and then rely only on querystring for state changes? Any link that was previously a postback would be a link to another URL (or a postback-redirect).

2)如果你无法弄明白,也许你可以关闭井页的viewstate然后只依赖querystring进行状态变化?之前回发的任何链接都是指向另一个URL(或回发重定向)的链接。

This can really reduce the weight of the page and make it easier to avoid issues with ViewState.

这确实可以减轻页面的重量,并且更容易避免ViewState的问题。

#6


0  

@Jonathan:

Yes, the runtime will populate the viewstate of the controls as long as you create the right number of them. The controls' viewstate will populate after this event.

是的,只要您创建正确数量的控件,运行时就会填充控件的视图状态。控件的viewstate将在此事件后填充。

#7


0  

Yes, the runtime will populate the viewstate of the controls as long as you create the right number of them. The controls' viewstate will populate after this event.

是的,只要您创建正确数量的控件,运行时就会填充控件的视图状态。控件的viewstate将在此事件后填充。

In what order is LoadViewState called? I added an overridden method signature and it does not seem to be stepping into it.

LoadViewState以什么顺序调用?我添加了一个重写的方法签名,它似乎没有踩到它。

#8


0  

protected override void LoadViewState(object savedState)
{
   // Put your code here before base is called
   base.LoadViewState(savedState);
}

Is that what you meant? Or did you mean in what order are the controls processed? I think the answer to that is it quasi-random.

这是你的意思吗?或者您的意思是控件处理的顺序是什么?我认为答案就是准随机。

Also, why can't you load the objects you bind to before Page_Load? It's ok to call your business layer at any time during the page lifecycle if you have to, with the exception of pre-render and anything after.

另外,为什么不能在Page_Load之前加载绑定的对象?如果必须,可以在页面生命周期的任何时间调用业务层,但预渲染和后续操作除外。

#9


0  

When creating dynamic controls ... I only populate them on the initial load. Afterwords I recreate the controls on postback in the page load event, and the viewstate seems to handle the repopulating of the values with no problems.

创建动态控件时...我只在初始加载时填充它们。后来我在页面加载事件中重新创建了回发控件,并且viewstate似乎处理了值的重新填充而没有任何问题。

#10


0  

I have to explicitly null the session value during non postbacks in order to emulate how ViewState works.

我必须在非回发期间显式地使会话值为空,以便模拟ViewState的工作方式。

I'm still foggy as to why you can't store whatever object(s) you are binding against in session. If you could store that object in session the following should work:

我仍然模糊为什么你不能存储你在会话中绑定的任何对象。如果您可以将该对象存储在会话中,则以下内容应该有效:

  1. On first load bind your top user control to the object during OnPreInit. Store the object in session. Viewstate will automatically be stored for those controls. If you have to bind the control the first time on Page_Load that is ok, but you'll end up having two events that call bind if you follow the next step.
  2. 首次加载时,在OnPreInit期间将*用户控件绑定到对象。将对象存储在会话中。将自动为这些控件存储Viewstate。如果你必须第一次在Page_Load上绑定控件,那么你可以在下一步操作时调用bind两个事件。

  3. On postback, rebind your top user user control in the OnPreInit method against the object you stored in session. All of your controls should be recreated before the viewstate load. Then when viewstate is restored, the values will be set to whatever is in viewstate. The only caveat here is that when you bind again on the postback, you have to make 100% sure that the same number of controls are created again. The key to using Repeaters, Gridviews etc... with dynamic controls inside of them is that they have to be rebound on every postback before the viewstate is loaded. OnPreInit is typically the best place to do this. There is no technical constraint in the framework that dictates that you must do all your work in Page_Load on the first load.
  4. 在回发时,将OnPreInit方法中的*用户用户控件重新绑定到您在会话中存储的对象。应在viewstate加载之前重新创建所有控件。然后,当恢复viewstate时,值将设置为viewstate中的任何值。这里唯一需要注意的是,当你再次绑定回发时,你必须100%确保再次创建相同数量的控件。使用Repeater,Gridviews等进行动态控制的关键是它们必须在加载视图状态之前在每个回发上反弹。 OnPreInit通常是最好的地方。框架中没有技术限制要求您必须在第一次加载时在Page_Load中完成所有工作。

This should work. However, if you can't use session for some reason, then you'll have to take a slightly different approach such as storing whatever you are binding against in the database after you bind your control, then pulling it out of the database and rebinding again on every postback.

这应该工作。但是,如果由于某种原因无法使用会话,则必须采取稍微不同的方法,例如在绑定控件后将数据库中存储的任何内容存储在数据库中,然后将其从数据库中拉出并重新绑定每次回发都会再次发生。

Am I missing some obvious detail about your situation? I know it can be very tricky to explain the subtleties of the situation without posting code.

我错过了一些关于你情况的明显细节吗?我知道在不发布代码的情况下解释情况的微妙之处可能非常棘手。

EDIT: I changed all references to OnInit to OnPreInit in this solution. I forgot that MS introduced this new event in ASP.NET 2.0. According to their page lifecycle documentation, OnPreInit is where dynamic controls should be created/recreated.

编辑:我在此解决方案中将所有对OnInit的引用更改为OnPreInit。我忘了MS在ASP.NET 2.0中引入了这个新事件。根据他们的页面生命周期文档,OnPreInit是应该创建/重新创建动态控件的地方。

#1


4  

The LoadViewState method on the page is definitely the answer. Here's the general idea:

页面上的LoadViewState方法绝对是答案。这是一般的想法:

protected override void LoadViewState( object savedState ) {
  var savedStateArray = (object[])savedState;

  // Get repeaterData from view state before the normal view state restoration occurs.
  repeaterData = savedStateArray[ 0 ];

  // Bind your repeater control to repeaterData here.

  // Instruct ASP.NET to perform the normal restoration of view state.
  // This will restore state to your dynamically created controls.
  base.LoadViewState( savedStateArray[ 1 ] );
}

SaveViewState needs to create the savedState array that we are using above:

SaveViewState需要创建我们上面使用的savedState数组:

protected override object SaveViewState() {
  var stateToSave = new List<object> { repeaterData, base.SaveViewState() };
  return stateToSave.ToArray();
}

Don't forget to also bind the repeater in Init or Load using code like this:

不要忘记使用以下代码绑定Init或Load中的转发器:

if( !IsPostBack ) {
  // Bind your repeater here.
}

#2


1  

This also has issues, because I have to explicitly null the session value during non postbacks in order to emulate how ViewState works.

这也有问题,因为我必须在非回发期间显式地使会话值为空,以便模拟ViewState的工作方式。

Why do you have to explicitly null out the value (aside from memory management, etc)? Is it not an option to check Page.IsPostback, and either do something with the Session variable or not?

为什么必须显式地清除值(除了内存管理等)?它不是检查Page.IsPostback的选项,是否可以使用Session变量执行某些操作?

#3


1  

I have always recreated my dynamic controls in the LoadViewState event. You can store the number of controls needed to be created in the viewstate and then dynamically create that many of them using the LoadControl method inside the LoadViewState event. In this event you have access to the ViewState but it has not been restored to the controls on the page yet.

我总是在LoadViewState事件中重新创建我的动态控件。您可以在viewstate中存储需要创建的控件数,然后使用LoadViewState事件中的LoadControl方法动态创建其中的许多控件。在这种情况下,您可以访问ViewState,但尚未恢复到页面上的控件。

#4


0  

@DancesWithBamboo:

If I dynamically bind the controls there, will ASP.NET automatically handle their state? The problem with doing it in Page_Load is that viewstate is already loaded and it does not add the dynamic controls in the repeater. I would think it would do the same in LoadViewState?

如果我动态绑定那里的控件,ASP.NET会自动处理它们的状态吗?在Page_Load中执行此操作的问题是viewstate已经加载,并且它不会在转发器中添加动态控件。我认为它会在LoadViewState中做同样的事情吗?

#5


0  

1) there's probably a way to get it to work... you just have to make sure to add your controls to the tree at the right moment. Too soon and you don't get ViewState. Too late and you don't get ViewState.

1)可能有办法让它工作......你只需要确保在适当的时候将控件添加到树中。太快了,你没有得到ViewState。太晚了,你没有得到ViewState。

2) If you can't figure it out, maybe you can turn off viewstate for the hole page and then rely only on querystring for state changes? Any link that was previously a postback would be a link to another URL (or a postback-redirect).

2)如果你无法弄明白,也许你可以关闭井页的viewstate然后只依赖querystring进行状态变化?之前回发的任何链接都是指向另一个URL(或回发重定向)的链接。

This can really reduce the weight of the page and make it easier to avoid issues with ViewState.

这确实可以减轻页面的重量,并且更容易避免ViewState的问题。

#6


0  

@Jonathan:

Yes, the runtime will populate the viewstate of the controls as long as you create the right number of them. The controls' viewstate will populate after this event.

是的,只要您创建正确数量的控件,运行时就会填充控件的视图状态。控件的viewstate将在此事件后填充。

#7


0  

Yes, the runtime will populate the viewstate of the controls as long as you create the right number of them. The controls' viewstate will populate after this event.

是的,只要您创建正确数量的控件,运行时就会填充控件的视图状态。控件的viewstate将在此事件后填充。

In what order is LoadViewState called? I added an overridden method signature and it does not seem to be stepping into it.

LoadViewState以什么顺序调用?我添加了一个重写的方法签名,它似乎没有踩到它。

#8


0  

protected override void LoadViewState(object savedState)
{
   // Put your code here before base is called
   base.LoadViewState(savedState);
}

Is that what you meant? Or did you mean in what order are the controls processed? I think the answer to that is it quasi-random.

这是你的意思吗?或者您的意思是控件处理的顺序是什么?我认为答案就是准随机。

Also, why can't you load the objects you bind to before Page_Load? It's ok to call your business layer at any time during the page lifecycle if you have to, with the exception of pre-render and anything after.

另外,为什么不能在Page_Load之前加载绑定的对象?如果必须,可以在页面生命周期的任何时间调用业务层,但预渲染和后续操作除外。

#9


0  

When creating dynamic controls ... I only populate them on the initial load. Afterwords I recreate the controls on postback in the page load event, and the viewstate seems to handle the repopulating of the values with no problems.

创建动态控件时...我只在初始加载时填充它们。后来我在页面加载事件中重新创建了回发控件,并且viewstate似乎处理了值的重新填充而没有任何问题。

#10


0  

I have to explicitly null the session value during non postbacks in order to emulate how ViewState works.

我必须在非回发期间显式地使会话值为空,以便模拟ViewState的工作方式。

I'm still foggy as to why you can't store whatever object(s) you are binding against in session. If you could store that object in session the following should work:

我仍然模糊为什么你不能存储你在会话中绑定的任何对象。如果您可以将该对象存储在会话中,则以下内容应该有效:

  1. On first load bind your top user control to the object during OnPreInit. Store the object in session. Viewstate will automatically be stored for those controls. If you have to bind the control the first time on Page_Load that is ok, but you'll end up having two events that call bind if you follow the next step.
  2. 首次加载时,在OnPreInit期间将*用户控件绑定到对象。将对象存储在会话中。将自动为这些控件存储Viewstate。如果你必须第一次在Page_Load上绑定控件,那么你可以在下一步操作时调用bind两个事件。

  3. On postback, rebind your top user user control in the OnPreInit method against the object you stored in session. All of your controls should be recreated before the viewstate load. Then when viewstate is restored, the values will be set to whatever is in viewstate. The only caveat here is that when you bind again on the postback, you have to make 100% sure that the same number of controls are created again. The key to using Repeaters, Gridviews etc... with dynamic controls inside of them is that they have to be rebound on every postback before the viewstate is loaded. OnPreInit is typically the best place to do this. There is no technical constraint in the framework that dictates that you must do all your work in Page_Load on the first load.
  4. 在回发时,将OnPreInit方法中的*用户用户控件重新绑定到您在会话中存储的对象。应在viewstate加载之前重新创建所有控件。然后,当恢复viewstate时,值将设置为viewstate中的任何值。这里唯一需要注意的是,当你再次绑定回发时,你必须100%确保再次创建相同数量的控件。使用Repeater,Gridviews等进行动态控制的关键是它们必须在加载视图状态之前在每个回发上反弹。 OnPreInit通常是最好的地方。框架中没有技术限制要求您必须在第一次加载时在Page_Load中完成所有工作。

This should work. However, if you can't use session for some reason, then you'll have to take a slightly different approach such as storing whatever you are binding against in the database after you bind your control, then pulling it out of the database and rebinding again on every postback.

这应该工作。但是,如果由于某种原因无法使用会话,则必须采取稍微不同的方法,例如在绑定控件后将数据库中存储的任何内容存储在数据库中,然后将其从数据库中拉出并重新绑定每次回发都会再次发生。

Am I missing some obvious detail about your situation? I know it can be very tricky to explain the subtleties of the situation without posting code.

我错过了一些关于你情况的明显细节吗?我知道在不发布代码的情况下解释情况的微妙之处可能非常棘手。

EDIT: I changed all references to OnInit to OnPreInit in this solution. I forgot that MS introduced this new event in ASP.NET 2.0. According to their page lifecycle documentation, OnPreInit is where dynamic controls should be created/recreated.

编辑:我在此解决方案中将所有对OnInit的引用更改为OnPreInit。我忘了MS在ASP.NET 2.0中引入了这个新事件。根据他们的页面生命周期文档,OnPreInit是应该创建/重新创建动态控件的地方。