2012-08-28 11 views
6

En un WebControl, tengo una propiedad Filters define así:¿Hay alguna manera de almacenar un delegado anónimo en un viewstate?

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; 
    } 
} 

Este de webcontrol es un DataSource, creé esta propiedad porque quiero tener la posibilidad de filtrar datos de forma sencilla, por ejemplo:

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

funciona muy bien, sin embargo, si cambio de código para esto:

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

no Trab KS más, me sale este error:

Tipo System.Web.UI.Page en la Asamblea '...' no está marcado como serializable .

lo que sucedió:

  1. El serializador inspeccionar el diccionario. Se ve que contiene un delegado anónimo (lambda aquí)
  2. Dado que el delegado se define en una clase, intenta serializar toda la clase, en este caso System.Web.UI.Página
  3. Esta clase no está marcada como serializable
  4. Se produce una excepción debido a 3.

¿hay alguna solución conveniente para resolver esto? No puedo marcar todas las páginas web donde uso el origen de datos como [serializable] por razones obvias.


EDIT 1: algo que no entiendo. Si guardo el Dictionary en el objeto Session (que usa un BinaryFormatter contra LosFormatter para ViewState), ¡funciona! No tengo idea de cómo es posible. Tal vez BinaryFormatter puede serializar cualquier clase, incluso estos que no son [serializable]?


EDIT 2: código más pequeño para reproducir el problema:

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; 
} 
+0

"funciona! No tengo idea de cómo ...": los datos de la sesión permanecen en el servidor, en la memoria. Comenzará a romperse cuando te muevas a más de 2 servidores. –

+0

Gracias por la información – tigrou

Respuesta

0

he encontrado una solución, aquí es cómo lo hice:

he modificado Definición del diccionario de esta manera:

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

(KeyValuePair es serializable, al igual que Dictionary)

y creé una nueva función 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)); 
} 
Ahora

filtros se pueden establecer de esta manera:

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

Funciona, porque las variables capturadas ahora no son más una parte del delegado, pero teniendo en cuenta como parámetros del delegado.

4

Buena pregunta. Puedo confirmar su diagnóstico (con una pequeña corrección: la infraestructura intenta serializar la clase de cierre que probablemente contiene una referencia a su página).

puede definir su propia clase de clausura y tienen que serializado:

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

Eso no es conveniente, sin embargo.

Le sugiero que use un patrón diferente: No serialice "código".Serializar los datos utilizados por el filtro:

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

Aunque no puedo señalar a la razón exacta por la que yo preferiría que intuitivamente sabemos que es un mejor enfoque.

+0

Gracias por su respuesta. No estoy seguro de cómo funcionará la segunda proposición. ¿Quién instanciará y mantendrá la instancia de la clase FilterSettings? Si es la página, ¿no hay ningún riesgo de que el serializador llegue a la página también? ¿Puedes proporcionar más código sobre cómo usar esta solución? – tigrou

+0

Almacenar una clase cuyos campos controle con precisión nunca hará referencia a la página. No hay forma de que el serializador pueda llegar a la página a partir de Closure o FilterSettings. – usr

+0

Todavía no puedo hacerlo funcionar incluso con sus sugerencias. He agregado una muestra de código al final de la pregunta que reproducirá el problema. ¿Puedes echarle un vistazo y darme una pista? – tigrou

Cuestiones relacionadas