有没有办法在视图状态中存储匿名委托?

时间:2012-08-28 11:36:58

标签: c# serialization delegates webforms viewstate

WebControl中,我有一个定义如下的属性Filters

public Dictionary<string, Func<T, bool>> Filters
{
    get
    {
       Dictionary<string, Func<T, bool>> filters =
               (Dictionary<string, Func<T, bool>>)ViewState["filters"];
       if (filters == null)
       {
          filters = new Dictionary<string, Func<T, bool>>();
          ViewState["filters"] = filters;
       }
       return filters;
    }
 }

这个webcontrol是DataSource,我创建了这个属性,因为我希望能够轻松过滤数据,例如:

//in page load    
DataSource.Filters.Add("userid", u => u.UserID == 8);

但是,如果我将代码更改为:

,它的效果很好
//in page load    
int userId = int.Parse(DdlUsers.SelectedValue);
DataSource.Filters.Add("userid", u => u.UserID == userId);

它不再起作用了,我收到了这个错误:

  

程序集'...'中的类型System.Web.UI.Page未标记为   序列化的。

发生了什么:

  1. 序列化程序检查字典。它看到它包含一个匿名委托(这里是lambda)
  2. 由于委托是在一个类中定义的,它会尝试序列化整个类,在本例中是System.Web.UI.Page
  3. 此类未标记为可序列化
  4. 因为3而引发异常。
  5. 有没有方便的解决方案来解决这个问题?由于显而易见的原因,我无法将所有使用数据源的网页标记为[serializable]。


    编辑1 :我不明白的事情。如果我将Dictionary存储在Session对象中(对BinaryFormatter使用LosFormatter vs ViewState),它就可以了!我不知道怎么可能。也许BinaryFormatter可以序列化任何类,甚至是那些不是[serializable]

    的类

    编辑2 :重现问题的最小代码:

    void test()
    {
        Test test = new Test();
        string param1 = "parametertopass";
        test.MyEvent += () => Console.WriteLine(param1);
    
        using (MemoryStream ms = new MemoryStream())
        {
           BinaryFormatter bf = new BinaryFormatter();
           bf.Serialize(ms, test); //bang
        }
    }
    
    [Serializable]
    public class Test
    {
       public event Action MyEvent;
    }
    

2 个答案:

答案 0 :(得分:4)

好问题。我可以确认您的诊断(通过一个小小的更正:基础结构尝试序列化闭包类,其中可能包含对您页面的引用)。

您可以定义自己的闭包类并将其序列化:

[Serializable] class Closure { int userId; bool Filter(User u) { ... } };

但这不方便。

我建议你使用不同的模式:不要序列化“代码”。序列化过滤器使用的数据:

class FilterSettings { int userId; int someOtherFiler; string sortOrder; ... }

虽然我无法指出我更倾向于直觉地知道这是一种更好的方法的确切原因。

答案 1 :(得分:0)

我找到了一个解决方案,这就是我如何做到的:

我修改了这样的字典定义:

Dictionary<string, KeyValuePair<Func<T, object[], bool>, object[]>>

KeyValuePair可序列化,就像Dictionary

一样

并创建了一个新的Add()函数:

public void Add(string key, Func<T, object[], bool> filter, params object[] args)
{
    this.Add(key, new KeyValuePair<Func<T, object[], bool>, object[]>
          (filter, args));
}

现在可以通过以下方式设置过滤器:

int userId = int.Parse(DdlUsers.SelectedValue);
DataSource.Filters.Add("userid", (u, args) => u.UserID == (int)args[0], userId);

它有效,因为现在捕获的变量不再是委托的一部分,而是作为委托的参数给出。