Archive for the ‘Silverlight’ Category

Failed to deserialize change-set. Operation is not valid due to the current state of the object.

I’ve had a long disucssion with myself over on the MSDN forums here:
http://forums.silverlight.net/forums/p/140254/313870.aspx#313870

I’ve reproduced it here:

I’ve written a domain service that allows basic CRUD operations for a person’s “Contacts”.  Lots and lots of unit tests run just fine against the server side code, and the Silverlight client can download all the contacts I put into the database using a SQL script and display them very nicely.

Unfortunately, I can’t create a new contact from the Silverlight side.  In the submit operation’s completed event handler I get the error message:

{System.Windows.Ria.Data.EntityOperationException:
Failed to deserialize change-set. Operation is not valid due to the current state of the object.
at System.Windows.Ria.Data.HttpDomainClient.GetRequestResult(HttpDomainClientAsyncResult httpAsyncResult)
at System.Windows.Ria.Data.HttpDomainClient.EndSubmitCore(IAsyncResult asyncResult)
at System.Windows.Ria.Data.DomainClient.EndSubmit(IAsyncResult asyncResult)
at System.Windows.Ria.Data.DomainContext.CompleteSubmitChanges(IAsyncResult asyncResult)}

However,  in the submit operation completed event handler submitOp.EntitiesInError.Count()=0.  Thus, it doesn’t seem to be my new contact instance object that’s the problem.

I’ve overriden all the methods in the domain service adding try/catch blocks (or at least a breakpoint) and just calling base.whatever()  the only method that seems to be called is the Initialize(DomainServiceContext context) method, which completes normally.  The insert method of the domain service is definitely not called:

[Insert]
 public void AddContact( ContactDto newContact ) {
    Contract.Requires(GetCurrentUserId()!=null, "The request must be authenticated");
    Contract.Requires(newContact!=null, "The contact to add must not be null");
    Contract.Requires(newContact.Id==0, "All new contacts must have a zero ID");
    Contract.Ensures(newContact.Id!=0, "The contact's ID should not be zero after adding it to the database");
    System.Diagnostics.Trace.WriteLine("*** SERVER *** AddContact( "+ newContact + " )");
    var userID = GetCurrentUserId();
    this.UnitOfWork.SaveContact(userID, newContact);
 }

The JSON that is passed to the server is:

[{"Entity":{"__type":"Contact:#MyApplication.DataAccessLayer.Contracts.Data",
"Archived":null,
 "Category":null,
 "Company":"ABC inc",
 "DataVersion":null,
 "DateCreated":"\/Date(-621355788000000500)\/",
 "DateModified":null,
 "DateViewed":null,
 "FirstName":"Robert",
 "HomeAddressId":null,
 "Id":0,
 "LastName":"McCarter",
 "Notes":null,
 "NotificationFrequency":null,
 "OwnerId":0,
"SpouseFirstName":null,
 "SpouseLastName":null,
 "WorkAddressId":null},
"Id":0,
 "Operation":1,
 "OperationData":null,
 "OperationName":null}]

So, in a separate solution I started from scratch and created a new Silverlight  .NET RIA Services project. I copied the definition of the DTO contact class into this new project.  I can easily create new instances of the DTO contact class in the new project.   Then I copied my simplified GUI in to the original project and it doesn’t work, I get the same error as above.  Then I copied my simplified domain service to the original project and it STILL doesn’t work, I get the same error.

The contact DTO class was being automatically generated by my ORM tool, and was being annotated by that tool as follows:

[System.Runtime.Serialization.DataContract(Name="Contact")]
public partial class ContactDto : BaseEntityDto {
....
}

When I had previously copied the DTO code to my simplified experimentation project I had not copied the attributes.  The moment I copied the attributes it stopped working.  Changing the name of the data contract to match the name of the class fixes the problem.

Notice that this is actually visible in the original JSON:

    Contact:#MyApplication.DataAccessLayer.Contracts.Data

this should have been

    ContactDto:#MyApplication.DataAccessLayer.Contracts.Data

I don’t know WCF so I don’t fully understand what this name is actually doing, although according to the MSDN documentation (http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractattribute.aspx) the default value is the name of the class.

Is it possible that this is a bug in .NET RIA Services, where RIA Services is assuming the data contract name is the same as the class name even when it doesn’t have to be?

Update:

I’ve asked Mindscape if LightSpeed could be modified to make this work:
http://www.mindscape.co.nz/forums/Post.aspx?ThreadID=2507&PostID=7399

 

 

Advertisements

OnCreated not called when created

I’m using Microsoft .NET RIA Services to build a Silverlight application, and today I had a bug.  At first it looked like the DataGrid’s RowDetailsTemplate wasn’t working, but that wasn’t it.

After debugging it for a little while I realized that the property I had data bound to, “FullName” wasn’t being set by the OnCreated() method,  which is a partial method called by the class’s constructor.

Apparently .NET RIA Services is using reflection in  such a way that it doesn’t call the constructor when it is creating client side objects from a server response.  This is possible using the serialization method: FormatterServices.GetUninitializedObject().  So when .NET RIA Services is de-serializing objects from the server it doesn’t call the constructor and the objects are created without their OnCreated() method being called.

In my case, the solution was simply to move the initialization of the derived  FullName property into the property getter rather than in OnCreated() method.  Then I had to implement the appropriate partial methods for the “FirstName”, “LastName” “SpouseFirstName” and “SpouseLastName”properties so that they would raise the property changed event for the “FullName” property.

Nice and easy, however it’s still a shame there’s no way for an object to know when it has been newly retrieved from the server.