Search code examples
c#.netvb.netweb-serviceswcf

WCF service call returns data but receiver gets null


I am debugging a wcf service that basically connects to my sql db, gathers data and returns the data in lists (to the caller, when called, that is). However, my client (the caller) gets null data even though, when I step through the wcf code, it is returning data.

Here is the client code that makes the call to the wcf service. Note that it is here, when I set a breakpoint and look at

GetServerUpdatesSyncOthers

after the call returns, my lists are all null when they should not be (yes the client is in vb.net but the wcf service is in c#)

  Dim svc As PocketPCServerClient = GetServiceClient()

  Dim updates As ServerUpdatesSyncDTO = svc.GetServerUpdatesSyncOthers(myDeviceId, timestamps.ToArray, operatorName, Me.IdMethodName(idMethod), idMethodValue, destinationHub, withLocationGroup)

For the record, the ServerUpdatesSyncDTO looks like this on the client side:

<DataContract()>
Public Class ServerUpdatesSyncDTO
Public Const EMPLOYEES_PROCESS_MAXIMUM_NONE As Integer = 0
Public Const EMPLOYEES_PROCESS_MAXIMUM_ALL As Integer = -1

<DataMember()>
Public Property SystemDateTimeUtc As DateTime
<DataMember()>
Public Property SyncTimestamps As List(Of SyncTimestampDTO)
<DataMember()>
Public Property DeviceSettings As List(Of DeviceSettingDTO)
<DataMember()>
Public Property Couriers As List(Of CourierDTO)
<DataMember()>
Public Property Validation As List(Of ValidationDTO)
<DataMember()>
Public Property NameVariants As List(Of String)
<DataMember()>
Public Property NameScrubs As List(Of String)
<DataMember()>
Public Property Notes As List(Of String)
<DataMember()>
Public Property Notifications As List(Of NotifyDTO)
<DataMember()>
Public Property Locations As List(Of LocationDTO)
<DataMember()>
Public Property UpsValids As List(Of UpsValidDTO)
<DataMember()>
Public Property StatusCodes As List(Of StatusCodeDTO)
<DataMember()>
Public Property HubRecon As List(Of String)
<DataMember()>
Public Property HubNames As List(Of String)
<DataMember()>
Public Property ExtCustomFieldNames As CustomFieldHeadersDTO
<DataMember()>
Public Property HoldPickupLocations As List(Of HoldPickupDTO)
<DataMember()>
Public Property EmployeeUpdates As List(Of EmployeeDTO)
<DataMember()>
Public Property EmployeeInserts As List(Of EmployeeDTO)
<DataMember()>
Public Property EmployeeDeletes As List(Of EmployeeDTO)
<DataMember()>
Public Property EmployeesProcessMore As Boolean
<DataMember()>
Public Property EmployeesProcessMoreIdGreaterThan As String
End Class

And on the wcf service side, a c# version of the ServerUpdatesSyncDTO:

  [DataContract]
public class ServerUpdatesSyncDTO
{
    public const int EMPLOYEES_PROCESS_MAXIMUM_NONE = 0;
    public const int EMPLOYEES_PROCESS_MAXIMUM_ALL = -1;

        [DataMember]
        public DateTime SystemDateTimeUtc { get; set; }
        [DataMember]
        public List<SyncTimestampDTO> SyncTimestamps { get; set; }
        [DataMember]
        public List<DeviceSettingDTO> DeviceSettings { get; set; }
        [DataMember]
        public List<CourierDTO> Couriers { get; set; }
        [DataMember]
        public List<ValidationDTO> Validation { get; set; }
        [DataMember]
        public List<string> NameVariants { get; set; }
        [DataMember]
        public List<string> NameScrubs { get; set; }
        [DataMember]
        public List<string> Notes { get; set; }
        [DataMember]
        public List<NotifyDTO> Notifications { get; set; }
        [DataMember]
        public List<LocationDTO> Locations { get; set; }
        [DataMember]
        public List<UpsValidDTO> UpsValids { get; set; }
        [DataMember]
        public List<StatusCodeDTO> StatusCodes { get; set; }
        [DataMember]
        public List<EmployeeDTO> EmployeeUpdates { get; set; }
        [DataMember]
        public List<EmployeeDTO> EmployeeInserts { get; set; }
        [DataMember]
        public List<EmployeeDTO> EmployeeDeletes { get; set; }
        [DataMember]
        public bool EmployeesProcessMore { get; set; }
        [DataMember]
        public string EmployeesProcessMoreIdGreaterThan { get; set; }
        [DataMember]
        public List<string> HubRecon { get; set; }
        [DataMember]
        public List<string> HubNames { get; set; }
        [DataMember]
        public CustomFieldHeadersDTO ExtCustomFieldNames { get; set; }
        [DataMember]
        public List<HoldPickupDTO> HoldPickupLocations { get; set; }
    }

Finally, the wcf service method that is called by the client. Keep in mind, I can set a break point here and step through and I see that this method DOES return valid data, no exceptions, each and every time. For example, one of the items, a list of Couriers. I have 48 of them in my list on the wcf side when it returns but the client shows null for Couriers in "updates" after the call returns.

 public ServerUpdatesSyncDTO GetServerUpdatesSyncOthers(string deviceId, List<SyncTimestampDTO> syncTimestamps, string operatorName, string locationCriteriaType, string locationCriteria, string destinationHub, bool withLocationGroup)
    {
        var thisMethodName = MethodBase.GetCurrentMethod().Name;
        CurrentTaskName = thisMethodName;
        CurrentDeviceId = deviceId;

        if (string.IsNullOrWhiteSpace(deviceId))
            throw new FaultException(string.Format("Argument null [{0}]", "deviceId"));
        if (syncTimestamps == null)
            throw new FaultException(string.Format("Argument null [{0}]", "syncTimestamps"));
        if (string.IsNullOrWhiteSpace(operatorName))
            throw new FaultException(string.Format("Argument null [{0}]", "operatorName"));
        if (locationCriteriaType.ToUpper() != LOCATION_ID_METHOD_BY_EMPLOYEE && locationCriteriaType.ToUpper() != LOCATION_ID_METHOD_BY_NAME)
            throw new FaultException(string.Format("locationCriteriaType [{0}] is invalid. Valid values are {1} or {2}", locationCriteriaType, LOCATION_ID_METHOD_BY_EMPLOYEE, LOCATION_ID_METHOD_BY_NAME));
        if (string.IsNullOrWhiteSpace(locationCriteria) & locationCriteriaType != LOCATION_ID_METHOD_BY_EMPLOYEE)
            throw new FaultException(string.Format("Argument null [{0}]", "locationCriteria"));

        var result = new ServerUpdatesSyncDTO();

        try
        {
            WriteServerLog(SERVER_TASK_BEGIN);
            using (var conn = GetSQLConnection())
            {
                var homeLocationCode = 0;
                var loginLocationCode = 0;
                List<int> groupLocationCodes;

                if ((locationCriteriaType == LOCATION_ID_METHOD_BY_EMPLOYEE) && (string.IsNullOrWhiteSpace(locationCriteria)))
                {
                    locationCriteria = operatorName;
                }

                loginLocationCode = GetLocationCode(conn, null, locationCriteriaType, locationCriteria);
                groupLocationCodes = GetGroupLocationCodes(conn, null, loginLocationCode);

                if (locationCriteriaType == LOCATION_ID_METHOD_BY_EMPLOYEE)
                {
                    homeLocationCode = loginLocationCode;
                }
                else
                {
                    homeLocationCode = GetLocationCode(conn, null, LOCATION_ID_METHOD_BY_EMPLOYEE, operatorName);
                }

                var lastUpdateUtc = GetLocationSyncDateTimeUtc(loginLocationCode, syncTimestamps);
                result.Couriers = GetCourierUpdates(conn, lastUpdateUtc, loginLocationCode);
                result.Validation = GetValidationUpdates(conn, lastUpdateUtc, loginLocationCode);
                result.HubNames = GetHubNames(conn, lastUpdateUtc, loginLocationCode);

                result.NameVariants = GetNameVariantUpdates(conn, lastUpdateUtc);
                result.NameScrubs = GetNameScrubUpdates(conn, lastUpdateUtc);
                result.Notes = GetNotesUpdates(conn, loginLocationCode);
                result.Notifications = GetNotifyUpdates(conn, lastUpdateUtc);
                result.Locations = GetLocationUpdates(conn, lastUpdateUtc);
                result.UpsValids = GetUpsValidUpdates(conn, lastUpdateUtc);
                result.StatusCodes = GetStatusCodeUpdates(conn, lastUpdateUtc);
                result.ExtCustomFieldNames = GetExtendedCustomFieldHeaderUpdates(conn, lastUpdateUtc);

                if (Properties.Settings.Default.HoldForPickupUpdates)
                {
                    result.HoldPickupLocations = GetHoldPickupUpdates(conn);
                }
                else
                {
                    result.HoldPickupLocations = new List<HoldPickupDTO>();
                }

                if (!string.IsNullOrWhiteSpace(destinationHub))
                {
                    result.HubRecon = GetHubReconUpdates(conn, destinationHub);
                }
                else
                {
                    result.HubRecon = new List<string>();
                }

                result.DeviceSettings = GetDeviceSettingUpdates(conn, loginLocationCode, deviceId);

                result.SystemDateTimeUtc = DateTime.Now.ToUniversalTime();
                result.SyncTimestamps = GetSyncTimestampUpdates(result.SystemDateTimeUtc, syncTimestamps, loginLocationCode, groupLocationCodes, withLocationGroup);

            }

            WriteServerLog(SERVER_TASK_COMPLETE, true, true);
            return result;

        }
        catch (FaultException ex)
        {
            throw ex;
        }
        catch (Exception ex)
        {
            string exText = FormatException(ex, string.Format("{0}:{1}", Assembly.GetExecutingAssembly().GetName().Name, thisMethodName));
            WriteServerLog(thisMethodName, ServerLogDTO.LogTypeEnum.Critical, exText, ServerLogDTO.EventDataFormatEnum.MultilineText);
            throw new FaultException(exText);
        }
    }  

So, I assume that while it should be okay for the client to be vb and the service to be wcf, there must be some sort of mismatch that vb does not like when it gets data returned? I just don't see it. Data types are all the same and they are both lists.

Thanks in advance for the help!


Solution

  • I found the issue. Typically, the client has a service reference with the wcf service where it can pull down the WSDL from the service and create the client class for you. This is done by going to References > Add service Reference and point to the web service address.

    There are two things to note with this, both of which were an issue for me. 1. You can right click the service reference and do an update. This will pull down and update the service reference. This is a must if changes were made to the WCF service so that the clients that reference said service, get updated as well. Otherwise, the interface will differ and things wont work. 2. When you add or edit the service reference, there are options to select what type of collections you use. By default, mine was selected as arrays. However, in the WCF service, I did not use arrays but rather, lists. If there is a mismatch with your service reference/client code, collections will be broken. You should get warnings on this if you update your service reference but in my case, I had not updated my service reference either.

    Therefore, after I updated my service reference, I also selected that I was using Lists and not arrays. After that, everything works as expected.

    Hope this helps.