简体   繁体   中英

Reading POST Request XML - Boolean value always read as false

I'm working on a WCF RESTful web service hosted in IIS. I'm currently working on a fairly simple post request, sending the following XML to the endpoint:

<StockListRequestData xmlns="http://myWebService.com/endpoint">
<UserID>2750</UserID>
<StockDatabase>stockLeekRoadVenue</StockDatabase>
<InStockOnly>true</InStockOnly>
</StockListRequestData>

This XML matches a DataContract on my web service:

[DataContract(Namespace = "http://myWebService.com/endpoint")]
public class StockListRequestData
{
    [DataMember]
    public string UserID { get; set; }

    [DataMember]
    public string StockDatabase { get; set; }

    [DataMember]
    public bool InStockOnly { get; set; }
}

The problem is the <InStockOnly>true</InStockOnly> element, when I set this to true or false it will always be interpreted as false ...

Here is the code that handles the request:

public StockListResponseData GetListOfProducts(StockListRequestData requestData)
    {
        var stockList = new StockList(requestData.InStockOnly, requestData.StockDatabase);
        StockListResponseData response;
        if (stockList.Any())
        {
            var stockArray = new Stock[stockList.Count];
            var i = 0;
            foreach (var s in stockList)
            {
                stockArray[i] = s;
                i++;
            }
            response = new StockListResponseData
                           {
                               StockList = stockArray,
                               WasSuccessful = true,
                           };
            return response;
        }
        response = new StockListResponseData
                       {
                           WasSuccessful = false
                       };
        return response;
    }

The StockList class:

[DataContract]
public class StockList : List<Stock>
{
    public StockList(bool inStockOnly, string stockDb)
    {
        if (inStockOnly)
        {
            // Get only products that are in stock
            var conn = AndyServerDatabase.ConnectToStockMovementByDb(stockDb);
            conn.Open();
            // Compile SQL query
            var q = new SqlCommand(null, conn) { CommandText = "SELECT StockID, Name, PerBox FROM Stock WHERE InStock = 1;" };

            // Execute query
            var rdr = q.ExecuteReader();

            // Check that the output isn't null
            if (rdr.HasRows)
            {
                while(rdr.Read())
                {
                    var id = Convert.ToInt32(rdr[0]);
                    var name = rdr[1].ToString();
                    var perBox = Convert.ToInt32(rdr[2]);
                    Add(new Stock(id, name, perBox));
                }
                conn.Close();
            }
            // Output is null
            conn.Close();
        }
        else
        {
            // Get all products
            // Get only products that are in stock
            var conn = AndyServerDatabase.ConnectToStockMovementByDb(stockDb);
            conn.Open();
            // Compile SQL query
            var q = new SqlCommand(null, conn) { CommandText = "SELECT StockID, Name, PerBox FROM Stock;" };
            q.Prepare();

            // Execute query
            var rdr = q.ExecuteReader();

            // Check that the output isn't null
            if (rdr.HasRows)
            {
                while (rdr.Read())
                {
                    var id = Convert.ToInt32(rdr[0]);
                    var name = rdr[1].ToString();
                    var perBox = Convert.ToInt32(rdr[2]);
                    Add(new Stock(id, name, perBox));
                }
                conn.Close();
            }
            // Output is null
            conn.Close();
        }
        // Add();
    }
}

Resultant XML:

<StockListResponseData xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <StockList xmlns:a="http://schemas.datacontract.org/2004/07/SMS">
        <a:Stock>
            <a:Id>1</a:Id>
            <a:Name>Smirnoff Vodka (70cl)</a:Name>
            <a:PerBox>6</a:PerBox>
            <a:Quantity>0</a:Quantity>
            <a:Remains>0</a:Remains>
        </a:Stock>
        <a:Stock>
            <a:Id>2</a:Id>
            <a:Name>Jagermeister (70cl)</a:Name>
            <a:PerBox>6</a:PerBox>
            <a:Quantity>0</a:Quantity>
            <a:Remains>0</a:Remains>
        </a:Stock>
    </StockList>
    <WasSuccessful>true</WasSuccessful>

I hope this is enough to go on, I've been stumped for ages and just can't figure out why it's behaving in such a way.. if you need additional code I haven't included please feel free to ask.

Many thanks,

Andy

Edit:

To add some context to show what is happening:

For example, I know that:

    <a:Stock>
        <a:Id>2</a:Id>
        <a:Name>Jagermeister (70cl)</a:Name>
        <a:PerBox>6</a:PerBox>
        <a:Quantity>0</a:Quantity>
        <a:Remains>0</a:Remains>
    </a:Stock>

Has its InStock row set to 0, which means that this should not be displayed in the resultant XML if I pass in true .

I have changed the StockList constructors if(inStockOnly) to if(!inStockOnly) - I then pass in <InStockOnly>true</InStockOnly> - when it gets to the StockList constructor it is then inverted and the correct data is displayed - so it must be reading it as false by the time it gets to this if statement.

If I pass in <InStockOnly>false</InStockOnly> it still displays the "correct" results, therefore it is reading it as false until it gets to the inversion!

Likewise if I leave it as if(inStockOnly) and pass in <InStockOnly>false</InStockOnly> it displays the data for false !

I have also added requestData.InStockOnly to the StockListResponseData DataContract and there it displays it outputs the value of requestData.InStockOnly as false .

Your discovery has led me to the explanation, and someone with a problem similar to yours:

WCF DataContract DataMember order? http://msdn.microsoft.com/en-us/library/ms729813.aspx

Next in order are the current type's data members that do not have the Order property of the DataMemberAttribute attribute set, in alphabetical order.

When the order of data members is not explicitly specified, their serialization order is alphabetical. That explains why InStockOnly worked when it was moved to the top, because it's first alphabetically. On the other hand, why StockDatabase worked is a bit of a mystery, because that's after UserId alphabetically (does AndyServerDatabase.ConnectToStockMovementByDb() use a default value if StockDb is null?).

For the sake of argument, if for whatever reason you wanted to keep the order you have there, you could do this:

[DataContract(Namespace = "http://myWebService.com/endpoint")]
public class StockListRequestData
{
    [DataMember(Order = 0)]
    public string UserID { get; set; }

    [DataMember(Order = 1)]
    public string StockDatabase { get; set; }

    [DataMember(Order = 2)]
    public bool InStockOnly { get; set; }
}

In fact, it's probably a good practice to explicitly indicate the order, so adding new properties later doesn't break anything.

I tried the suggestion above and it still didn't work! Kept searching and found another solution, actually setting the 'Specified' property to true:

PackageImagesPayload payload = new PackageImagesPayload();
payload.UsesSplitBy = usesSplitBy;
payload.UsesSplitBySpecified = true;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM