We will look at the framework rules for defining create, update, and delete operations in or domain service class and convention vs. congiguration options that are available to us.Next we will look at the client-side classes that are involved in data updating.We will dig into the change tracking mechanism that are available to the client for use in our application.We will examine the way submit operations are sent over the network and the message formats that are usedNext will look at how the domain service processes the changes.We will look at errors that commonly occur such as concurrencyWe will wrap up with a look at updating related data in the data object model
In Ria services there are three kinds of operations that can be defined in our domain service. There are query operations as we’ve seen last week. There are Invoke operations which are custom regular WCF service operations that we expose and can do any kind of work and accept and return anything that is serializable. We will look at invoke operations next week.And finally, there are submit operations. These are CUD operations (as in Create, Update, and Delete) which are also WCF operations under the hood but with a Ria services wrapper. The wrapper provides the capability to handle change tracking and identity management and a unit of work pattern that allows us to send all the changes from the client to the server and execute them in a single processing unit.Submit domain operations definitions follow similar general rules as the ones we saw for query operations. The operations must:be publicHave serializable parameters, andNo overloadsRemember our discussion last week about convention vs. configuration rules for query operations. We saw how query operations follow signature based conventions. Submit operations not only follow signature based convention bases operations as well, but also named based conventions.In terms of signature, the submit operation must not return a value and it must take exactly one parameter of an entity type. Remember, entity types identified on the server must have an identity property and meet a few simple rules. With regard to naming conventions, a submit method must begin with a specific prefix that identifies it as an Insert, Update, or Delete operation.Insert methods should begin with the prefix Insert, Add, or Create.Update methods should begin with Update, Change, or Modify andDelete methods should begin with Delete, or RemoveBased on the prefix and entity type of the parameter, the framework will be able to automatically identify which method implements which CUD operation in our service.Alternatively, we can choose to name our method anything we like in which we must annotate the method with the Insert, Update, or Delete attribute
Here is an example of three submit operations that meet the frameworks rules.All three take a Customer parameter which is an entity type defined in our DAL. That can be a POCO class, EF, or L2S.The three methods also have authorized prefixes in their names (Create, Modify, and Remove). We could have also named them AddCustomer, ChangeCustomer, and DeleteCustomer. The framework picks up on these names and will generate the appropriate plumbing for supporting change track submit operations based on these signatures.Note that since the signature and naming conventions are followed here attribute annotation is optional.In Ria services there is also the notion of a named update method.A named update method is a method that updates an entity type but also performs some custom logic as well along the way. It also has a non-standard name. So here we have a named update method and notice that it accepts an entity type parameter among other parameters. This method will update data in the data store for the specified entity instance so it is effectively an update method.To notify the framework that this method should be treated as an update method and so be implemented with the change tracking capabilities, we annotate the method with the Update attribute and set the UsingCustomMethod parameter to true
We saw last week that the tooling generates a few classes on the client based on the operations we defined in our domain serice.We have a typed domain context class that mirrors our domain service.The domain context creates a typed entity container class that manages state on the client.And the entity container creates and manages a bunch of entity sets that willHold the loaded entity instances as well as any newly created instance we create on the client.Entity sets are created through the CreateEntitySet() method that is auto generated in the type entity container class. The method takes an EntitySetOperationsenum parameter that specifies which combination of create, update and delete operations are allowed on the client. This is set by the tooling based on which operations are available in the domain service.Remember that the domain context communicates to the server through a domain client instance that knows how to send and receive messages through the WCF underlying infrastructure. None of this is new to us as we have seen this is the query operations. But there is a new class that plays an important role in data update scenariosThe EntityChangeSet class. EntityChangeSet encapsulates all the changes that have been made on the client that need to be sent and executed back on the server. These changes are managed by the EntityContainer class.During submit operations, the domain client serializes and sends the contents of the EntityChangeSet back to the service, and the service then recreates the change set server-side and plays back and executes the data changes.All of this is triggered by a call to the domain context SubmitChanges method
The submit changes method of the domain context class is an asynchronous method just like the load method we saw for query operations. It immediately returns a SubmitOperation instance which is derrived from OperationBaseJust like the LoadOperation. Behind the scenes the SubmitChanges method calls a BeginSubmit method on the domain client and that method takes an entity ChangeSet instance as one of its input parameters. So submit changes does just what its name says, it sends to the server all of the changes made on the client, and it does so in one shot so the changes are processed server-side as a single unit following a unit-of-work pattern.We can use the submit operation instance to access an EntityChangeSet sent back from the server. So EntityChangeSet is used not only to push changes to the server, but also to retrieve any data return by the server. So for example we can retrieve server generated Ids for newly inserted records, and we can also retrieve error information using the EntitiesInError property.
Lets look at what happens when we make changes on the client before we submit these changes back to the server. First, lets take a look at the client-side change tracking infrastructure.Change tracking and identity management are managed by two classes that work tightly together.EntityContainer, EntitySet and Entity. All three classes implement specifically two change tracking interfaces,IChangeTracking and IRevertibleChangeTracking. As a result of implementing these interfaces, there are protected members that support accepting and reverting changes. They also all expose a public HasChanges property which internally uses the IsChanged property of the IChangeTracking interface. EntityContainer also exposes a GetChanges() method that returns the current entity change set.As we have seen, EntityContainer creates and maintains a bunch of entity sets which are lists of entity instances. The Entity class also plays a role in change tracking through members that return current entity state and original values, that is data values before any client-side changes have been made.Entity container basically delegates the bulk of its work to its contained EntitySet which in turn calls upon Entity to help manage changes.One last piece of the overall change tracking picture is the property notification interfaces InotifyPropertyChanged and the related INotifyCollectionChanged that the three classes implement as appropriate. This enables UI notification and data binding in Silverlight and so these play a role in change tracking at the UI level.
So how do we use these API at the client.A typical scenario is to load some data into the client on the domain service through a load operation call.Then we can start making some updates to the client through some data-bound controls.Then we can access our changes (the changes that are waiting to be pushed back to the server) through the EntityContainer.GetChanges method. We can then examine pending changes through the changeSet.GetChangeSetEntries() method. In each entry we can access data such as the type of the CUD operation that has been performed, the state of the entity, as well as the entities current and original values, and information about the entity’s association invlolvedin the operation.
We can rollback the changes that are made on the client by calling the RejectChanges() on the domain context. This internally calls the RejectChanges on the Entity container through the IRevertibleChangeTracking interface, and that in turn calls RejectChanges on the entity set and then on to the entity class all through the same interface.In certain scenarios we can call AcceptChanges on the entity container by first casting it to IChangeTracking interface type and that call will propagate down through the entity.Typically though, this method is not invoked by the programmer, but by the framework itself, mainly once the changes have been submitted to the server.The members of IChangeTracking and IRevertibleChangeTracking are mainly used by controls that support data updating such as the grid and data form. These methods are used by controls along with the ones exposed by the IEditable object and IEditable collection that are interfaces that these three classes (EntityContainer, EntitySet, and Entity all implement.
Let’s zoom in on the way that EntitySet and Entity manage change tracking.EntitySet has methods to attach a new or existing entity to the client’s container, and to remove an entity from the existing tree in the entity container. These top-level methods will dig deep into the entity tree and it can also walk through any internal tree and detach and entity that is about to be attached tot the entity container. In short, entity set knows how to manage complex associations through inferring and composition.These functionalities are used in association with a set of possible entity states that get set on the entity class’ EntityState property. (Explain states)The GetOriginal method allows us to retrieve the value the instance had before any changes were made. Using the entity state property and the GetOriginal method together allows the entity class to effectively manage changes
Lets look at what happens when we submit our changes. Ria services provides a top-level submit method in the domain service that gets invoked for all create, update, and delete operations, that is all submit operations.If we examine our service contract it appears clearly that the submit operation takes an Ienumerable list of change set entries and invocation returns a IEnumerable of change set entries so in this manner the entries of the change set are passed back and forth between the client and the server.The return entry contain auto-generated values from the server as well as any error information.So, the change set entry type is actually the method type passed between the domain client which is used internally by the domain context and the domain service. The type is used both for transmitting operations to the server and for returning results from the operation back to the domain client
When the domain client calls the begin submit method and passes the client-side change set, a WCF service call is made, the change set data is transmitted and the domain service is initialized. The change set data sent over the wire is de-serialized into a server-side change set class instance which holds a bunch of server-side change set entries that contain all of the update operation data.That change set is passed on to the top level submit method in the domain service class. The submit call initiates a processing life cycle by successfully calling a set of methods that process the passed-in changes.The first method that gets called is AuthorizeChangeSet() which verifies authorization.ValidateChangeSet() is then called to perform validation and sets validation errors if any change set entry that fails validation so that error information is returned to the client.ExecuteChangeSet() is then called, it is actually invoked for each operation in the change set matching a create, update, or delete operation defined in our domain service. Our method is actually wrapped in a domain operation entry instance through which the domain method is invoked.Finally, PersistChangeSet() commits all of the data changes made by the domain method invocation back to the data source. If any errors occur at this point, errors are once again set in the ChengeSetEntry of the change set before it gets serialized back to the client.PersistChangeSet is actually delegated over to the data layer so for example that could be EF.These life cycle methods are all declared virtual so we can override them to customize server handling of our submit requests.An important point to note is data layers such as L2S and EF automatically wrap the persist operation in a transaction, however we can choose to wrap our persist call in our own explicit transaction by overriding the submit method in the domain service. The override would simply need to create new transaction scope and then call the base submit method from within the transaction
Just like for query operations, we can handle submit operation errors on the client using a callback that we pass to the SubmitChanges() method of the domain context.We can use members of the submit operation instance that is returned by the call to access error information. The entities in error property returns the list of entities that caused the validation error or if that’s applicable, the single entity that caused the server call to fail and return.There are a few types of errors that can occur on the server that are in the submit operation. Two common types are validation errors which are triggered as part of our business logic, and concurrency errors. Concurrency errors are present when two clients send conflicting updates of the same data back to the server.We can access detailed error data through the entity class itself. We can inspect validation errors using the ValidationErrors property, and concurrency conflict through the EntityConflict property. The entity API allows us to resolve conflicts on the client by examining the original and submitted versions of the entity instance as well as the stored version when that’s available, and then can decide or let the user decide which version to submit to the server. The Resolve method of the entity conflict class also helps us in resolving these situations