Question

Background:

Writing a client that consumes a web service, in C# using .NET 4.0. Proxy class includes an object of type record, which is populated from a SOAP response from the web service.

Situation:

The API includes two functions, findRecords and getRecords. Both functions download the actual record, with the difference being that findRecords is of type void and furnishes the record via an out argument, whereas getRecords is of type record and so returns the record.

Problem:

After executing a call to findRecords, I can access the members of the record object (like recordID, recordTitle, etc.) for use in other function arguments. However, if I try to pass the record object itself as an argument, I get an ArgumentNullException. The only way I can currently pass a record as an argument is if I use the record returned from the getRecords function. The drawback to this approach is that it doubles the number of API calls I need to make, which slows down both my client and the web service.

Question:

Why is it behaving this way, and is there something I can do to be able to pass the record object from findRecords as an argument?

Code:

This is the definition of the findRecords function:

    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://localhost:8080/findRecords", RequestNamespace="http://www.<redacted>.com/ws/schemas", ResponseNamespace="http://www.<redacted>.com/ws/schemas", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    public void findRecords([System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] string username, [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] filter filter, [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="integer")] string offset, [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="integer")] string count, [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] out System.Nullable<int> numResults, [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] [System.Xml.Serialization.XmlIgnoreAttribute()] out bool numResultsSpecified, [System.Xml.Serialization.XmlArrayAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] [System.Xml.Serialization.XmlArrayItemAttribute("list", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)] out record[] results) {
        object[] results1 = this.Invoke("findRecords", new object[] {
                    username,
                    filter,
                    offset,
                    count});
        numResults = ((System.Nullable<int>)(results1[0]));
        numResultsSpecified = ((bool)(results1[1]));
        results = ((record[])(results1[2]));
    }

The definition of the getRecords function:

    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://localhost:8080/getRecords", RequestNamespace="http://www.<redacted>.com/ws/schemas", ResponseNamespace="http://www.<redacted>.com/ws/schemas", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    [return: System.Xml.Serialization.XmlArrayAttribute("records", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)]
    [return: System.Xml.Serialization.XmlArrayItemAttribute("list", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)]
    public record[] getRecords([System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] string username, [System.Xml.Serialization.XmlArrayAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] [System.Xml.Serialization.XmlArrayItemAttribute("list", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="integer", IsNullable=false)] string[] stiIds) {
        object[] results = this.Invoke("getRecords", new object[] {
                    username,
                    recordIds});
        return ((record[])(results[0]));
    }

And what I'm trying to do with it:

        // Objects to use for the findRecords call
        int? numResults;
        bool numResultsSpecified;
        record[] records;

        // Object for handling and saving the XML
        XRecord r;

                try
                {
                    ws.findRecords(usernames[uname_Index], GetFilter(), offset.ToString(), count.ToString(),
                        out numResults, out numResultsSpecified, out returnRecords);

                    for (int i = 0; i < returnRecords.Length; i++)
                    {
                        count--;
                        r = GrabRecord(returnRecords[i]);
                        r.record.Save(@".\Records\" + r.id + "_" + r.date2 + ".xml");
                    }
                }

......

    private static XRecord GrabRecord(record _record)
    {
        XNamespace nameSpace = "http://www.<redacted>.com/ws/schemas";

        XDocument r =
            new XDocument(
                new XElement(nameSpace + "getRecordsResponse",
                    new XAttribute(XNamespace.Xmlns + "ns1", nameSpace),
                    new XElement("list",
                        new XElement("ID", _record.id),
                        new XElement("title", _record.title),
            ...............
                        new XElement("comments", _record.comments),
                        new XElement("category", _record.category),
                        _record.modifiedDateSpecified ? new XElement("modifiedDate", _record.modifiedDate) : null,
                        new XElement("attachments", from a in _record.attachments
                                                    select new XElement("list",
                                                        new XElement("id", a.id),
                                                        new XElement("filePath", a.filePath),
                                                        new XElement("type", a.type))));

        XRecord xr = new XRecord();
        xr.record = r;
        xr.id = _record.id;
        xr.date2 = ConvertToDateString(_record.modifiedDate);

        return xr;
    }

Here's the exception and stacktrace information. The line numbers referenced refer to the lines "XDocument r = " and "r = GrabRecord(...)" lines in the corresponding functions.

Unexpected error: Value cannot be null. Parameter name: source at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable1 source, Func2 selector) at WsClient.WSAPI.GrabRecord(record _record) in C:\...WSAPI.cs:line 1235 at WsClient.WSAPI.PersistentUpdate(String[] usernames) in C:\...WSAPI.cs:line 354

As rsbarro suggested, I edited the code in the GrabRecord function to eliminate the possibility that ConvertToDateString() could have been causing the problem if modifiedDate was null. This did not eliminate the problem, and the exception message did not change.

Was it helpful?

Solution

From looking at the code, I'm guessing you are getting an ArgumentNullException in the call to GrabRecord. There is a potential problem in GrabRecord that could cause an ArgumentNullException. When you are building your XDocument to be stored in r, you check _record.modifiedDateSpecified before creating the modifiedDate XElement. However, towards the end of the method, the following statement is executed without any null check before it:

xr.date2 = ConvertToDateString(_record.modifiedDate);

It is possible that _record.modifiedDate is null, and that ConvertToDateString is kicking off an ArgumentNullException. Of course it is possible that ConvertToDateString handles null values without erroring, but without seeing that code it is tough to say.

If the issue is not what I've suggested, could you please update the question with some more details about the exception, and also add a stack trace?

Hope that helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top