Why is my WCF RIA Services custom object deserializing with an extra list member?
- by oasasaurus
I have been developing a Silverlight WCF RIA Services application dealing with mock financial transactions. To more efficiently send summary data to the client without going overboard with serialized entities I have created a summary class that isn’t in my EDM, and figured out how to serialize and send it over the wire to the SL client using DataContract() and DataMember(). Everything seemed to be working out great, until I tried to bind controls to a list inside my custom object. The list seems to always get deserialized with an extra, almost empty entity in it that I don’t know how to get rid of. So, here are some of the pieces.
First the relevant bits from the custom object class:
<DataContract()> _
Public Class EconomicsSummary
Public Sub New()
RecentTransactions = New List(Of Transaction)
TotalAccountHistory = New List(Of Transaction)
End Sub
Public Sub New(ByVal enUser As EntityUser)
Me.UserId = enUser.UserId
Me.UserName = enUser.UserName
Me.Accounts = enUser.Accounts
Me.Jobs = enUser.Jobs
RecentTransactions = New List(Of Transaction)
TotalAccountHistory = New List(Of Transaction)
End Sub
<DataMember()> _
<Key()> _
Public Property UserId As System.Guid
<DataMember()> _
Public Property NumTransactions As Integer
<DataMember()> _
<Include()> _
<Association("Summary_RecentTransactions", "UserId", "User_UserId")> _
Public Property RecentTransactions As List(Of Transaction)
<DataMember()> _
<Include()> _
<Association("Summary_TotalAccountHistory", "UserId", "User_UserId")> _
Public Property TotalAccountHistory As List(Of Transaction)
End Class
Next, the relevant parts of the function called to return the object:
Public Function GetEconomicsSummary(ByVal guidUserId As System.Guid) As EconomicsSummary
Dim objOutput As New EconomicsSummary(enUser)
For Each objTransaction As Transaction In (From t As Transaction In Me.ObjectContext.Transactions.Include("Account") Where t.Account.aspnet_User_UserId = guidUserId Select t Order By t.TransactionDate Descending Take 10)
objTransaction.User_UserId = objOutput.UserId
objOutput.RecentTransactions.Add(objTransaction)
Next
objOutput.NumTransactions = objOutput.RecentTransactions.Count
…
Return objOutput
End Function
Notice that I’m collecting the NumTransactions count before serialization. Should be 10 right? It is – BEFORE serialization.
The DataGrid is bound to the data source as follows:
<sdk:DataGrid AutoGenerateColumns="False" Height="100" MaxWidth="{Binding ElementName=aciSummary, Path=ActualWidth}"
ItemsSource="{Binding Source={StaticResource EconomicsSummaryRecentTransactionsViewSource}, Mode=OneWay}"
Name="gridRecentTransactions" RowDetailsVisibilityMode="VisibleWhenSelected" IsReadOnly="True">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn x:Name="TransactionDateColumn" Binding="{Binding Path=TransactionDate, StringFormat=\{0:d\}}" Header="Date" Width="SizeToHeader" />
<sdk:DataGridTextColumn x:Name="AccountNameColumn" Binding="{Binding Path=Account.Title}" Header="Account" Width="SizeToCells" />
<sdk:DataGridTextColumn x:Name="CurrencyAmountColumn" Binding="{Binding Path=CurrencyAmount, StringFormat=\{0:c\}}" Header="Amount" Width="SizeToHeader" />
<sdk:DataGridTextColumn x:Name="TitleColumn" Binding="{Binding Path=Title}" Header="Description" Width="SizeToCells" />
<sdk:DataGridTextColumn x:Name="ItemQuantityColumn" Binding="{Binding Path=ItemQuantity}" Header="Qty" Width="SizeToHeader" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
You might be wondering where the ItemsSource is coming from, that looks like this:
<CollectionViewSource x:Key="EconomicsSummaryRecentTransactionsViewSource" Source="{Binding Path=DataView.RecentTransactions, ElementName=EconomicsSummaryDomainDataSource}" />
When I noticed that the DataGrid had the extra row I tried outputting some data after the data source finishes loading, as follows:
Private Sub EconomicsSummaryDomainDataSource_LoadedData(ByVal sender As System.Object, ByVal e As System.Windows.Controls.LoadedDataEventArgs) Handles EconomicsSummaryDomainDataSource.LoadedData
If e.HasError Then
System.Windows.MessageBox.Show(e.Error.ToString, "Load Error", System.Windows.MessageBoxButton.OK)
e.MarkErrorAsHandled()
End If
Dim objSummary As EconomicsSummary = CType(EconomicsSummaryDomainDataSource.Data(0), EconomicsSummary)
Dim sb As New StringBuilder("")
sb.AppendLine(String.Format("Num Transactions: {0} ({1})", objSummary.RecentTransactions.Count.ToString(), objSummary.NumTransactions.ToString()))
For Each objTransaction As Transaction In objSummary.RecentTransactions
sb.AppendLine(String.Format("Recent TransactionId {0} dated {1} CurrencyAmount {2} NewBalance {3}", objTransaction.TransactionId.ToString, objTransaction.TransactionDate.ToString("d"), objTransaction.CurrencyAmount.ToString("c"), objTransaction.NewBalance.ToString("c")))
Next
txtDebug.Text = sb.ToString()
End Sub
Output from that looks like this:
Num Transactions: 11 (10)
Recent TransactionId 2283 dated 6/1/2010 CurrencyAmount $31.00 NewBalance $392.00
Recent TransactionId 2281 dated 5/31/2010 CurrencyAmount $33.00 NewBalance $361.00
Recent TransactionId 2279 dated 5/28/2010 CurrencyAmount $8.00 NewBalance $328.00
Recent TransactionId 2277 dated 5/26/2010 CurrencyAmount $22.00 NewBalance $320.00
Recent TransactionId 2275 dated 5/24/2010 CurrencyAmount $5.00 NewBalance $298.00
Recent TransactionId 2273 dated 5/21/2010 CurrencyAmount $19.00 NewBalance $293.00
Recent TransactionId 2271 dated 5/20/2010 CurrencyAmount $20.00 NewBalance $274.00
Recent TransactionId 2269 dated 5/19/2010 CurrencyAmount $48.00 NewBalance $254.00
Recent TransactionId 2267 dated 5/18/2010 CurrencyAmount $42.00 NewBalance $206.00
Recent TransactionId 2265 dated 5/14/2010 CurrencyAmount $5.00 NewBalance $164.00
Recent TransactionId 0 dated 6/1/2010 CurrencyAmount $0.00 NewBalance $361.00
So I have a few different questions:
-First and foremost, where the devil is that extra Transaction entity coming from and how do I get rid of it? Does it have anything to do with the other list of Transaction entities being serialized as part of the EconomicsSummary class (TotalAccountHistory)? Do I need to decorate the EconomicsSummary class members a little more/differently?
-Second, where are the peculiar values coming from on that extra entity?
PRE-POSTING UPDATE 1: I did a little checking, it looks like that last entry is the first one in the TotalAccountHistory list. Do I need to do something with CollectionDataContract()?
PRE-POSTING UPDATE 2: I fixed one bug in TotalAccountHistory, since the objects weren’t coming from the database their keys weren’t unique. So I set the keys on the Transaction entities inside TotalAccountHistory to be unique and guess what? Now, after deserialization RecentTransactions contains all its original items, plus every item in TotalAccountHistory. I’m pretty sure this has to do with the deserializer getting confused by two collections of the same type. But I don’t yet know how to resolve it…