2011-12-06 22 views
5

Tengo una aplicación cliente-servidor, partes de las cuales "hablan" entre sí a través de WCF (enlace netTcp).WCF. IList se deserializa como matriz. ¿Cómo hacer que sea una lista de escritura (ArrayList)?

tengo mi DataContract, que tiene 1 campo de una clase tercera parte:

[Serializable] 
public class MyResult{ 
    public ThirdPartyResult Result {get;set;} 

    /* other fields */ 
} 

Usando la reflexión veo esto:

[Serializable] 
public class ThirdPartyResult { 
    private IList result; 

    public IList Result 
    { 
    get { return result ?? (result = new ArrayList());} 
    } 
} 

Al llamar al servidor desde el cliente tengo el result como ArrayList en el servidor. Después de que llega al cliente, el campo result se convierte en una matriz de tamaño fijo.

me hizo no utilización referencia de servicio Agregar, pero yo uso el montaje compartir y sólo hacer

ChannelFactory<IMyContract>.CreateChannel(new NetTcpBinding("Configuration.Name"), address); 

ACTUALIZACIÓN: Contrato el servicio

[ServiceContract] 
[ServiceKnownType(typeof(ArrayList))] 
[ServiceKnownType(typeof(ThirdPartyResult))] 
public interface IMyContract 
{ 
    MyResult GetResult(); 
} 

Ahora la pregunta:
¿Cómo puedo decirle a WCF que use ArrayList en lugar de Array?


me ocurrió una solución muy mala (desde mi punto de vista)
Generalmente Yo quería un ArrayList a ser preservado para poder añadir elementos a ella. Finalmente, se me ocurrió la solución a continuación. Sí, lo sé, esto es completamente malo, y es por eso que todavía estoy buscando una mejor variante.

 if (thirdParty.Results != null && thirdParty.Results.IsFixedSize) 
     { 
      var results = new ArrayList(thirdParty.Results); 

      // Finding result by ReferenceEquals to not be tight to private variable name 
      var resultsField = thirdParty.GetType() 
       .GetFields(BindingFlags.Default | BindingFlags.Instance | BindingFlags.NonPublic) 
       .Where(f => ReferenceEquals(f.GetValue(thirdParty), thirdParty.Results)) 
       .FirstOrDefault(); 

      if (resultsField != null) 
       resultsField.SetValue(thirdParty, results); 
     } 
     thirdParty.AddResult(otherChild); 
+0

Algo usando '[Serializable]' y 'ArrayList' (que no es escrito, es decir,' object'), entonces no es realmente una contrato de datos ...? –

+0

Sí, puede que tengas razón. Este no es un contrato de datos verdadero (marcado como [DataContract]). Sin embargo, el enlace netTcp usa serialización binaria y es suficiente que la clase sea [Serializable]. Esta no es una colección de solo objeto, ya que de hecho es una colección de 'ThirdPartyResult', que se agrega a los tipos conocidos (pregunta actualizada) –

+0

¿Puedo verificarlo? o bien está utilizando un servicio-referencia/svcutil, o tiene ensamblado compartido. Si tiene un método de ensamblaje compartido, entonces es un tema discutible ya que usted mismo está definiendo los tipos. Entonces ... ¿de dónde viene 'IMyContract' si no está (según la pregunta) utilizando una referencia de servicio? –

Respuesta

0

En última instancia, su contrato no contiene información que lo haga elegir una implementación en particular. La mejor manera de solucionar esto sería; para hacer result bien mecanografiadas, tal vez como ArrayList:

private ArrayList result; 
public IList Result { 
    get { return result ?? (result = new ArrayList());} 
} 

personalmente yo espero para ver un List<T> con [DataContract]/[DataMember] etc, pero ...

+0

Sí, tienes toda la razón. Esa sería la mejor manera. Sin embargo, la clase 'ThirdPartyResult' está compilada y no tengo acceso a ella (excepto Reflection, seguro). Entonces la pregunta básicamente es: Cómo influir en WCF para elegir no array para IList, considerando que el campo seguirá siendo IList. –

+0

@AlexanderYezutov no puedes, AFAIK. Me gustaría pensar seriamente en ** no serializar ese tipo **, y en su lugar serializar un DTO que tenga datos similares. –

+0

Gracias, Marc. No es una buena idea serializar el tipo, pero por otro lado necesito usar métodos heredados de ese componente de terceros, que, desafortunadamente, aceptan ese tipo. Encontré una solución extraña, la describí en la pregunta. Pero no estoy tan feliz con eso. –

0

si nada más, entonces yo escribiría una clase de extensión para extender ThirdPartyResult

public static class ThirdPartyResultExtension 
    { 
    public static ArrayList ResultsAsArrayList(this ThirdPartyResult d) 
    { 
     ArrayList list = new ArrayList(); 
     list.AddRange(d.Result); 
     return list; 
    } 

    } 

    public class ThirdPartyResult 
    { 
    private IList result; 

    public IList Result 
    { 
     get { return result ?? (result = new ArrayList()); } 
    } 
    } 

y usarlo como

ThirdPartyResult r; 
    ArrayList arrlist = r.ResultsAsArrayList(); 
+0

gracias. Este fue uno de los enfoques que he usado. Pero se me requirió agregar un nuevo valor a 'ThirdPartyResult.Result'. Así que terminé con la variante, la describí en la pregunta misma. ** Gracias por su tiempo. ** –

1

Cuando crea una nueva referencia de servicio (o configura una referencia existente) en Visual Studio hay una propiedad algo así como "Deserializar matrices como" y allí puede elegir una matriz/lista/etc. Puede echar un vistazo al código generado y cambiar su código para lograr lo que desea.

+0

Sí, tiene razón. Y especialmente para eso he escrito, que no uso "Service Reference", pero uso el ensamblaje compartido –

1

consulte el siguiente:

WCF: Serializing and Deserializing generic collections

Esto resolvió mi problema: Tanto el método de trabajo y miembro de propiedad personalizada privada para mí.

[DataMember] 
private List<Person> members = new List<Person>(); 

o cambiar la propiedad a:

[DataMember] 
private Iist<Person> members = new Iist<Person>(); 

[DataMember()] 
public IList<Person> Feedback { 
    get { return m_Feedback; } 
    set { 
     if ((value != null)) { 
      m_Feedback = new List<Person>(value); 

     } else { 
      m_Feedback = new List<Person>(); 
     } 
    } 
} 
Cuestiones relacionadas