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

tig*_*rou 6 c# viewstate serialization delegates webforms

在a中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;
    }
 }
Run Code Online (Sandbox Code Playgroud)

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

//in page load    
DataSource.Filters.Add("userid", u => u.UserID == 8);
Run Code Online (Sandbox Code Playgroud)

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

//in page load    
int userId = int.Parse(DdlUsers.SelectedValue);
DataSource.Filters.Add("userid", u => u.UserID == userId);
Run Code Online (Sandbox Code Playgroud)

它不再起作用,我收到此错误:

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

发生了什么 :

  1. 序列化程序检查字典.它看到它包含一个匿名委托(这里是lambda)
  2. 由于委托是在类中定义的,因此它会尝试序列化整个类,在本例中为System.Web.UI.Page
  3. 此类未标记为Serializable
  4. 它因3而引发异常.

有没有方便的解决方案来解决这个问题?由于显而易见的原因,我无法将所有使用数据源的网页标记为[可序列化].


编辑1:我不明白的东西.如果我存储DictionarySession对象中(使用BinaryFormattervs LosFormatterfor 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;
}
Run Code Online (Sandbox Code Playgroud)

tig*_*rou 0

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

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

Dictionary<string, KeyValuePair<Func<T, object[], bool>, object[]>>
Run Code Online (Sandbox Code Playgroud)

(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));
}
Run Code Online (Sandbox Code Playgroud)

现在过滤器可以这样设置:

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

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