RIA Services ComplexTypes Sample Contents Overview ....................................................................................................................................................... 1 Application Details ........................................................................................................................................ 1 Running the sample .................................................................................................................................. 3 ComplexTypes in the EntityFramework Data Model ................................................................................ 3 ComplexType usage in RIA Services .......................................................................................................... 5 Standard Create/Update/Delete Functionality......................................................................................... 6 ComplexType Parameters in DomainService Operations ......................................................................... 7 ComplexType UI Binding ........................................................................................................................... 8 Cross-Tier Validation ............................................................................................................................... 10 IEditableObject Behavior ........................................................................................................................ 11 SubmitChanges/RejectChanges .............................................................................................................. 12 Overview This sample application demonstrates use of the new ComplexTypes feature in a variety of scenarios, including: Use of EntityFramework mapped complex types in the data model, both as nested Entity members as well as mapped stored procedure return types Use of POCO complex types as parameters to DomainService invoke operations Use of complex types as return values from DomainService invoke operations Application Details The sample app was created using the Silverlight Business Application Template. The Order Management page allows you to select a customer, at which time the orders for that customer are asynchronously loaded in a master/details view: The Customer Management page shows a master/detail view of Customers, and also displays the order history for the selected customer. These results are loaded on demand as customers are selected by executing an Invoke operation mapped to a sproc: Running the sample To run the sample, open and run the ComplexTypesSample solution. The application attaches two databases to the local SqlExpress instance (a Northwind database and the ASP.NET database used for authentication). Notes on the app: The sample requires the following to be installed: o WCF RIA Services V1SP1 o The Silverlight 4 Toolkit o SqlExpress When the initial app screen is displayed you must log in. The default database is configured with the sample credentials UserName=daboss, Password=imtheboss! Both the login screen as well as the “register new user” screen use ComplexTypes in their implementation (details below). To facilitate playing with the app, select the “Keep me signed in” option on the login page: Once logged in you can navigate to the Order Management and Customer Management pages. Each of these pages bind to ComplexTypes in different ways (details below). The pages support edit and submit/reject demonstrating use of ComplexTypes in DataForm and DataGrid binding scenarios, as well as in update scenarios. ComplexTypes in the EntityFramework Data Model Opening the Northwind.edmx file in the server project Models folder you can see how several EF complex types have been mapped in the model. The EF model browser shows the two types: The Address CT is shared by both the Customer Entity as well as the Order entity, where the address details for each are mapped to the CT. Creation of a new complex type can be done in the EF designer simply by selecting one or more properties of an entity and selecting “Refactor into new Complex Type” context menu option. After this is done the set of properties will now be exposed by the entity as a single CT property: You can then use the Mapping Details window to map the database columns to your CT properties: In a similar way, you can also map a sproc return Type to a CT. In this sample I mapped the CustOrdHistory sproc to a new CT Type CustomerOrderHistoryResult: This sproc is used in the sample app exposed by the CustomerManagement service GetCustomerOrderHistory operation. ComplexType usage in RIA Services The generated Northwind ObjectContext for the above model is used in this sample by both the CustomerManagement and OrderManagement DomainServices. RIA Services recognizes these types as ComplexTypes and uses this information during codegen to generate the corresponding proxy Types, members and methods. For example, in the client project an Address class has been generated that derives from ComplexObject: The Order and Customer entities have corresponding ComplexObject properties generated. You’ll also notice that the metadata pipeline for CTs behaves the same way it does for entities. For example, you can apply additional metadata via a “buddy class”. In addition, some of the EF model metadata also flows to the client. Below you can see both a Max Length model attribute as well as a buddy class DisplayAttribute being carried to the client codegen: Standard Create/Update/Delete Functionality The Order.ShipAddress and Customer.Address CT properties participate in updates as you’d expect. The client detects modifications, performs cross tier validation, etc. On the server, an updated entity with CT member updates is attached normally. ComplexType Parameters in DomainService Operations ComplexTypes can be passed as parameters to Invoke and Custom Update operations, and can be returned from Invoke operations. In the sample, the CustomerOrderHistory sproc is exposed in the CustomerManagement service via an Invoke operation and it returns a collection of CTs: This DomainService method results in a CustomerOrerHistoryResult ComplexObject being generated on the client, along with a corresponding Invoke operation on the CustomerManagement DomainContext: As an example of POCO CTs that aren’t mapped in the EntityFramework model, this sample has two examples. First the UserRegistrationService RegistrationData class has been updated to be a CT: On the client this is codegenned as a CT deriving from ComplexObject. The full IEditableObject and Validation behavior provided by the ComplexObject base class can be seen in the UI: Similarly the LoginInfo Type used on the login screen has also been made a ComplexObject. These modifications to the standard Business Application Template (BAT) to use ComplexTypes will be made in the actual product soon. This will allow us to remove several workarounds the BAT currently employs. ComplexType UI Binding In general the binding story for CT properties is straight forward – in your xaml you simply use dotted paths to reference the nested CT properties. Since ComplexObject implements the right set of binding interfaces (INotifyPropertyChanged, IEditableObject, INotifyDataErrorInfo), you get rich UI functionality. Below the DataForm xaml and resulting UI are shown for the bound members of Order.ShipAddress: Since DataForm wasn’t designed to do deep change tracking on nested instances, it doesn’t work properly in update scenarios where you’re modifying nested CT properties. The underlying issue is that the DataForm only tracks top level properties to determine if the current instance has been modified in the session. This will result in the commit/cancel buttons not behaving properly. This sample works around this by configuring the DataForm in a certain way (AutoEdit="False" AutoCommit="True" CommandButtonsVisibility="Edit, Cancel"). You can also create your own update UI bound to your CTs and control the entire experience yourself. The sample also demonstrates the validation behavior of ComplexObject in nesting scenarios. In the below scenario, the Order.ShipAddress.PostalCode member has been set to an invalid value. You see the error reported on the control itself since Address instance implements INDEI which the control is bound to. In addition, the error summary control is bound to INDEI on the Order instance – this demonstrates that nested CT instances report their validation attributes up to their parent. Cross-Tier Validation Above you saw client side validation occurring as properties were set. As with entities, all this validation also runs server side. In addition to validation that runs on both tiers, you can also have server side only validation (declarative or imperative). In the sample, the UpdateCustomer method the Customer.Address.Country member is validated: Notice that on the server the dotted path to the invalid member is specified. This validation error will be sent back to the client, and the error will be applied down the hierarchy, ultimately registering on the Customer.Address.Country member which will cause the UI to display the server error: IEditableObject Behavior Since ComplexObject implements IEO, when bound to a DataForm or other control providing edit sessions based on this interface, changes can be committed/cancelled easily. Below, the current Order has been put into edit mode and a property has been changed. Hitting the Cancel button will cancel the edit and the value will be reverted. To commit the edit, navigate to another Order (again working around the issue that the DataForm Commit button doesn’t work for nested CT edits). SubmitChanges/RejectChanges Once one or more edits have been made, the Save/Reject buttons will become enabled. You can set a breakpoint on the server to see that the nested CT instance is sent as part of the changeset and will be populated in both the current and original instances passed to your update method. As with entities, the RoundtripOriginalAttribute governs which properties are roundtripped to the server as part of the original object. You must either mark the members of your ComplexType as ConcurrencyMode=Fixed in your EF model (in which case the FX will infer RTO for you), or via the buddy class: In addition to setting the concurrency mode on the individual members of the ComplexType, you must also apply RoundtripOriginalAttribute on the Order.ShipAddress property itself. See the Order and Customer buddy metadata classes in the sample.