4

I have a client-server application, parts of which "talk" to each other through WCF (netTcp binding).

I have my DataContract, which has 1 field of a 3rd party class:

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

  /* other fields */
}

Using reflection i see this:

[Serializable]
public class ThirdPartyResult {
  private IList result;

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

When calling the server from client I have the result as ArrayList on server. After it comes to client the result field becomes a fixed size array.

I didn't use Add service reference, but i use assembly sharing and just do

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

UPDATE: the service contract

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

Now the question:
How can I tell WCF to use ArrayList instead of Array?


I came up with a very bad solution (from my point of view)
Generally I wanted an ArrayList to be preserved to be able to add items to it. Finally I came up with the solution below. Yes, I know, this is completely bad, and that's why I'm still looking for some better variant.

        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);
Alexander Yezutov
  • 3,144
  • 2
  • 21
  • 23
  • Something using `[Serializable]` and `ArrayList` (which is non-typed, i.e. `object`), then it isn't really a data-contract...? – Marc Gravell Dec 06 '11 at 09:33
  • Yeah, you may be right. This is not a true data contract (marked as [DataContract]). However, netTcp binding uses binary serialization and it is enough the class to be [Serializable]. This is not a collection of just object, as in fact it is a collection of `ThirdPartyResult`, which is added to know types (question updated) – Alexander Yezutov Dec 06 '11 at 09:45
  • Can I check; either you are using a service-reference/svcutil, or you have assembly sharing. If you have assembly-sharing, then it is a moot issue since you are defining the types yourself. So... where is `IMyContract` coming from if you aren't (per the question) using a service reference? – Marc Gravell Dec 06 '11 at 09:52
  • I have assembly sharing. Question updated. – Alexander Yezutov Dec 06 '11 at 10:01
  • I found this below, but does not work, but maybe I missed somethng: http://www.datazx.cn/Forums/en-US/175282d6-c0e0-4d49-9417-efeeb83e0ae1/action?threadDisplayName=how-to-force-wcf-service-with-ilist-parameter-to-deserialize-it-as-list-on-server-side-instead&forum=wcf –  Feb 10 '14 at 02:43

4 Answers4

1

Please see the following:

WCF: Serializing and Deserializing generic collections

This solved my problem: Both the private member and custom property method work for me.

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

OR change the property to:

[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>();
        }
    }
}
Community
  • 1
  • 1
1

When you create a new Service Reference (or configuring an existing reference) in the Visual Studio there is a property something like "Deserialize arrays as" and there you can choose array/list/etc. You could take a look at the generated code and change your code to achieve what you want.

Eugene
  • 1,515
  • 1
  • 13
  • 23
0

Ultimately your contract contains no information that would make it choose any particular implementation. The best way to fix this would be; to make result well-typed, perhaps as ArrayList:

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

personally I'd hope to see a List<T> with [DataContract]/[DataMember] etc, but...

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Yes, you are absolutely right. That would be the best way. However, the `ThirdPartyResult` class is compiled and I have no access to it (except Reflection, for sure). So the question basically is: How to influence WCF to choose not array for IList, considering the field will still be IList. – Alexander Yezutov Dec 06 '11 at 10:12
  • @AlexanderYezutov you can't, AFAIK. I would put serious thought into simply **not serializing that type**, and instead serializing a DTO that has similar data. – Marc Gravell Dec 06 '11 at 10:13
  • Thank you, Marc. Not serializing the type is a good idea, but from the other hand I need to use legacy methods of that 3rd party component, which, unfortunately, accept that type. I came up with a weird solution, I've described in the question. But i'm not so happy with it. – Alexander Yezutov Dec 06 '11 at 13:13
0

if nothing else then I would write an extension class to extend 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()); }
    }
  }

and use it like

 ThirdPartyResult r;
     ArrayList arrlist = r.ResultsAsArrayList();
Surjit Samra
  • 4,614
  • 1
  • 26
  • 36
  • thanks. This was one of the approaches I have used. But I was required to add a new value to `ThirdPartyResult.Result`. So I ended up with the variant, I've described in the question itself. **Thanks for your time.** – Alexander Yezutov Dec 06 '11 at 13:10