I’ve been working on my DevConnections demos today and as I was going through my demos I ended up significantly reworking a few of the samples and moving around projects. This actually went fairly smoothly but when I got to my AJAX StockPortfolio sample that mixes jQuery and WCF I got into a nasty mess of a problem that took me a good two hours to resolve. It turns out it was a typical problem of operator error on my part, but this still is one to write up and make others aware of.
My sample project consists of a WCF AJAX service that uses ASP.NET AJAX semantics for access from the client. This means the service is configured like this at the service level in the .SVC file:
<%@ ServiceHost Language="C#" Debug="true"
Service="WcfAjax.JsonStockService"
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"
%>
This tells WCF to publish the service by default using similar semantics as the old ASMX style services. Note the Factory attribute which is set to WebScriptServiceHostFactory which provides a pre-configured factory for typical MS AJAX style behavior. This factory means that all requests by default use WebInvoke() attributes with POST as the method to send data to the server and using wrapped message format that the ASP.NET AJAX client expects messages to be sent and recieved with. Using the factory makes the process very easy as you don’t have to create any web.config entries to configure the service although you can still do that to override particular settings.
With this done setting up the service is as easy as creating a class and plopping the [ServiceContract] onto it. Since this is an in-application Ajax service only I’m defining both the implementation and contract on a single class rather than seperate service contract interface and implementation class:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[ServiceContract]
public class JsonStockService
{
private StockServer Stocks = new StockServer();
private User User = null;
[OperationContract]
public StockQuote GetStockQuote(string symbol)
{
return Stocks.GetStockQuote(symbol);
}
/// <summary>
/// Returns a full list of items to the caller in the Items
/// property for the PortfolioMessage
/// </summary>
/// <param name="userToken"></param>
/// <returns></returns>
[OperationContract]
public PortfolioMessage GetPortfolioItems(string userToken)
{
// Validate and get this.User instance
this.ValidateToken(userToken);
busPortfolioItem portfolioItem = new busPortfolioItem();
IQueryable<PortfolioItem> items = portfolioItem.GetItemsForUser(this.User.Pk);
PortfolioMessage msg = new PortfolioMessage
{
TotalValue = portfolioItem.GetPortfolioTotalValue(this.User.Pk),
TotalItems = portfolioItem.GetPortfolioItemCount(this.User.Pk),
Items = items.ToList()
};
return msg;
}
}
/// <summary>
/// Message Item sent to client. Contains Portfolio detail
/// about a single item or complete portfolio
/// </summary>
[DataContract]
public class PortfolioMessage
{
[DataMember]
public decimal TotalValue { get; set; }
[DataMember]
public int TotalItems { get; set; }
[DataMember]
public PortfolioItem SingleItem { get; set; }
[DataMember]
public List<PortfolioItem> Items { get; set; }
}
My service has about 20 different methods of varying complexity, but I ran into a problem with the GetPortfolioItems() method. When called from the service my client would receive an empty response from the server. No data returned, but no error either. No headers, no data – just a null response. This is a bit difficult to debug as you might expect and this is what ended up taking me 2 hours to figure out.
So what can I do to figure out what the problem is? As you can see in the method above the result returned is a complex object. It’s basically a result message that contains a few pieces of information and all portfolio related method results all return this message object. The message object either has a single entity or a list of entites set along with a coupe of subtotals that require less code on the client.
First debugging step: Set a break point in the method and surprise, surprise the method actually completes properly. The code in the service fires to the server, but the the response still bombs with no result data. I checked the msg in the debugger and it’s fine no issues there.
So the problem most likely has to do with serialization. First to verify that the method is properly routed and working I returned null and that made it correctly all the way back to the client. No issue there. I then changed the methods WCF attributes to try and access it through a plain HttpGet:
[OperationContract]
[WebInvoke(Method="Get")]
public PortfolioMessage GetPortfolioItems(string userToken)
which should allow me to access the request directly in the browser with a url like
http://localhost/jquery/stockportfolio/StockService.svc/GetPorfolioItems?userToken=2322121321
Here’s the unfortunate result:
The result is an incomplete page – or to be exact a page that didn’t return any output. It seems browsers handle this inconsistently. In FireFox I saw the above page as well as a browser based “Resource Could not be Found” error. Regardless, the bummer on this is that you get nothing back. No error, no message of failure – just no response at all.
In experimenting around with the problem method it seemed clear that the problem is with serialization. The question is just what. Next step for me was to enable WCF tracing and tracking errors. To enable tracing you can use something like the following:
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true"
logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true"
maxMessagesToLog="3000" maxSizeOfMessageToLog="10000" />
</diagnostics>
</system.serviceModel>
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Error,ActivityTracing"
propagateActivity="true">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add initializeData="c:\projects2008\articles\jquery\traces.xml"
type="System.Diagnostics.XmlWriterTraceListener" name="messages">
<filter type="" />
</add>
</listeners>
</source>
</sources>
</system.diagnostics>
You can also use the WCF Configuration Tool which makes this process a little easier at least once you’ve set up the basic diagnostics settings. You can find this tool in Visual Studio off Tools | WCF Configuration and then specifying the .config file.
You’ll probably want to just log errors which is by setting the switchValue to log just errors – otherwise the log will become very verbose with entries that’s difficult to read.
Looking at the logs turned out to be not very helpful other than confirming what I already knew: WCF was unable to serialize my message structure. I got a full stack trace of the error on the server, but no indication of what actually went wrong. Also there’s no indication on why there’s no error response to the client. It looks like WCF just quit processing the message after the serialization failure and doing an immediate output return.
Not very helpful.
At the beginning of this post I mentioned that the problem was operator error. I played around with different results from my message structure. Returning null, returning an empty message structure all of that worked. But as soon as I returned any entity data from my Linq To SQL model the failure would kick back in. It turns out when I copied the model somehow I failed to set the serialization options on the Linq to Sql Model:
Somewhere in the copy process or maybe when mucking around with the model I failed to set the serialization options for the generated entities. Setting the switch to Unidirectional turns on the [DataContract] and [DataMember] attributes on the generated properties of the generated model:
[Table(Name="dbo.PortfolioItems")]
[DataContract()]
public partial class PortfolioItem : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _Pk;
public PortfolioItem()
{
this.Initialize();
}
[Column(Storage="_Pk", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true, UpdateCheck=UpdateCheck.Never)]
[DataMember(Order=1)]
public int Pk
{
get
{
return this._Pk;
}
set
{
if ((this._Pk != value))
{
this.OnPkChanging(value);
this.SendPropertyChanging();
this._Pk = value;
this.SendPropertyChanged("Pk");
this.OnPkChanged();
}
}
}
…
}
Without this option the entities are not serializable. This makes perfect sense. Flog the developer for the oversight :-}.
However, what doesn’t make perfect sense is why WCF is failing so gracelessly in this scenario. Other exceptions such as user thrown exceptions in the code or errors in plain code actually return a proper error response to the client, but the serialization error does not.
Other Posts you might also like