Silverlight and WCF RIA Services (3 – Client)

Having taken a look into the service side of WCF RIA Services in the previous post, it seems natural to have a bit of a look into the client side.

If we make a “vanilla” WCF RIA Services project in Visual Studio 2010 as in;

  • File->New Project
  • Choose a Silverlight Application
  • Choose a Web Application Project and elect to use RIA Services
  • Add a new project item to the web application project;
    • A new Domain Service

like the simple DomainService that I added below;

  [EnableClientAccess()]
  public class MyDomainService : DomainService
  {
    [Invoke]
    public int Add(int x, int y)
    {
      return (x + y);
    }
  }

then what is it that we’re programming against on the client side?

The DomainContext

The build process leaves us with a generated class MyDomainContext on the client side which derives from a framework class DomainContext.

What’s a DomainContext? It’s quite a big class – here’s its definition taken from a Visual Studio diagram;

image

I brought in the DomainContext’s friends – DomainClient and EntityContainer onto that diagram.

The DomainClient

Of these, I think that the DomainClient is easiest to understand. From previous investigations we know that a RIA Services client submits 3 fundamental kinds of operations to a RIA services service;

  • Query
  • Invoke
  • Submit

and it’s the DomainClient that knows how to do the client<->server communication for Query, Invoke, Submit asynchronously with cancellation. The DomainContext then is abstracted away from the details of how these operations are transmitted to the server side by relying on the DomainClient to do that work.

Now, the particular implementation of DomainClient that we find in the framework – WebDomainClient – is a specialisation of this class that knows how to communicate with a default RIA Service endpoint.

That is, one that’s using XML, binary encoded over HTTP/HTTPS.

If I wanted my client to communicate with another endpoint such as a SOAP endpoint then I’d be looking to write a DomainClient that knew how to do that and then I’d plug that implementation into a DomainContext. For me, this means that when I’m using a non-default DomainServiceEndpointFactory on the service side I should expect to be looking to write a DomainClient on the client-side.

The DomainClient needs some fairly complex data in order to be able to do its core Query, Invoke, Submit functionality. Taking a look at the signatures, Invoke looks like the simplest;

public IAsyncResult BeginInvoke(InvokeArgs invokeArgs, AsyncCallback callback, object userState);

where an InvokeArgs is a fairly simple class;

image

which “just” captures the operation that we’re trying to invoke server-side and the parameters that need to get passed to it.

What about Query? The signature looks like;

public IAsyncResult BeginQuery(EntityQuery query, AsyncCallback callback, object userState);

where the details of the query to be performed are captured in an instance of EntityQuery;

image

note that the Query property above is an IQueryable.

Finally, what about Submit? Submit in some ways feels the most complicated. The signature looks like;

public IAsyncResult BeginSubmit(EntityChangeSet changeSet, AsyncCallback callback, object userState);

and the “payload” here is the EntityChangeSet class;

image

where each of those [Added/Modified/Removed]Entities properties is collection of Entity ( more to come on that later ) whereas GetChangeSetEntries returns an enumeration of ChangeSetEntry;

image

that’s quite a complex class but I think it’s used both as an input to the Submit operation and as an output from the Submit operation in the sense that the EndSubmit method;

public SubmitCompletedResult EndSubmit(IAsyncResult asyncResult);

returns a SubmitCompletedResult which itself has an IEnumerable<ChangeSetEntry> called Results so the class kind of serves ( at least ) two purposes as it’s being used in the two different directions of data flow from client->server and then back again.

The EntityContainer

What about an EntityContainer? Right now I don’t have any entities in my project so the generated EntityContainer is a little on the “empty” side;

      internal sealed class MyDomainContextEntityContainer : EntityContainer
        {
            
            public MyDomainContextEntityContainer()
            {
            }
        }

By the way – this MyDomainContextEntityContainer class ends up generated as a nested class inside of the MyDomainContext class and I also see that MyDomainContext has a generated override of CreateEntityContainer which looks like;

     protected override EntityContainer CreateEntityContainer()
        {
            return new MyDomainContextEntityContainer();
        }

and so that all links up very logically.

What if I had some entities service-side. Let’s add one so that my service-side code looks like;

 public class Person
  {
    [Key]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
  }
  [EnableClientAccess()]
  public class MyDomainService : DomainService
  {
    public IQueryable<Person> GetPeople()
    {
      return ((new List<Person>()
      {
        new Person() { Id = 1, FirstName = "Fred", LastName = "Smith" }
      }).AsQueryable());
    }

    [Invoke]
    public int Add(int x, int y)
    {
      return (x + y);
    }
  }

and now on the client-side the generation process spits out some different things. Firstly, I now see that I have a different EntityContainer;

       internal sealed class MyDomainContextEntityContainer : EntityContainer
        {
            
            public MyDomainContextEntityContainer()
            {
                this.CreateEntitySet<Person>(EntitySetOperations.None);
            }
        }

and the constructor is using the base class CreateEntitySet method to create an EntitySet<Person> and add it in to the EntityContainer.

I also get something derived from Entity – my Person class;

    [DataContract(Namespace="http://schemas.datacontract.org/2004/07/SilverlightApplication5.Web")]
    public sealed partial class Person : Entity
    {
        
        private string _firstName;
        
        private int _id;
        
        private string _lastName;
        
        #region Extensibility Method Definitions

        /// <summary>
        /// This method is invoked from the constructor once initialization is complete and
        /// can be used for further object setup.
        /// </summary>
        partial void OnCreated();
        partial void OnFirstNameChanging(string value);
        partial void OnFirstNameChanged();
        partial void OnIdChanging(int value);
        partial void OnIdChanged();
        partial void OnLastNameChanging(string value);
        partial void OnLastNameChanged();

        #endregion
        
        
        /// <summary>
        /// Initializes a new instance of the <see cref="Person"/> class.
        /// </summary>
        public Person()
        {
            this.OnCreated();
        }
        
        /// <summary>
        /// Gets or sets the 'FirstName' value.
        /// </summary>
        [DataMember()]
        public string FirstName
        {
            get
            {
                return this._firstName;
            }
            set
            {
                if ((this._firstName != value))
                {
                    this.OnFirstNameChanging(value);
                    this.RaiseDataMemberChanging("FirstName");
                    this.ValidateProperty("FirstName", value);
                    this._firstName = value;
                    this.RaiseDataMemberChanged("FirstName");
                    this.OnFirstNameChanged();
                }
            }
        }
        
        /// <summary>
        /// Gets or sets the 'Id' value.
        /// </summary>
        [DataMember()]
        [Editable(false, AllowInitialValue=true)]
        [Key()]
        [RoundtripOriginal()]
        public int Id
        {
            get
            {
                return this._id;
            }
            set
            {
                if ((this._id != value))
                {
                    this.OnIdChanging(value);
                    this.ValidateProperty("Id", value);
                    this._id = value;
                    this.RaisePropertyChanged("Id");
                    this.OnIdChanged();
                }
            }
        }
        
        /// <summary>
        /// Gets or sets the 'LastName' value.
        /// </summary>
        [DataMember()]
        public string LastName
        {
            get
            {
                return this._lastName;
            }
            set
            {
                if ((this._lastName != value))
                {
                    this.OnLastNameChanging(value);
                    this.RaiseDataMemberChanging("LastName");
                    this.ValidateProperty("LastName", value);
                    this._lastName = value;
                    this.RaiseDataMemberChanged("LastName");
                    this.OnLastNameChanged();
                }
            }
        }
        
        /// <summary>
        /// Computes a value from the key fields that uniquely identifies this entity instance.
        /// </summary>
        /// <returns>An object instance that uniquely identifies this entity instance.</returns>
        public override object GetIdentity()
        {
            return this._id;
        }
    }

I pasted all the code in there as I think it’s worth looking at.

We have these 3 classes – EntityContainer, EntitySet<T> ( where T : Entity ) and Entity working together on the client side.

EntityContainer

EntityContainer is largely a dictionary of <Type,EntitySet> and mostly delegates its work down to the EntitySets that it contains. So, you can walk up to it and ask for EntitySet<Customer> or EntitySet<Order> and so on.

It implements property changed notification and also IRevertibleChangeTracking. If I quickly derive my own EntityContainer like this one below;

  public class PeopleContainer : EntityContainer
  {
    public PeopleContainer()
    {
      this.CreateEntitySet<Person>(EntitySetOperations.All);
    }
  }

then I can write code against the “change tracking” functionality as in;

 PeopleContainer container = new PeopleContainer();          

      Debug.Assert(!container.HasChanges);

      container.GetEntitySet<Person>().Add(
        new Person()
        {
          Id = 1
        });

      Debug.Assert(container.HasChanges);

      ((IRevertibleChangeTracking)container).AcceptChanges();

      Debug.Assert(!container.HasChanges);

but largely this is the work of the contained EntitySets being co-ordinated by the EntityContainer to work together, I don’t think there’s a huge amount of work that the container itself is doing here.

In terms of how an EntitySet gets into the container – I think the only way is to call EntityContainer.CreateEntitySet() and that’s protected so you would need to derive an EntityContainer like I did with my example PeopleContainer there in order to do that.

Normally, there’s no need to do that because that’s what the tooling does for the entity sets that it “sees” exposed by the server-side when it code-gens an EntityContainer derived class for the client-side.

The EntityContainer also has methods called LoadEntities where you can pass a whole collection of Entity ( of mixed types ) and the EntityContainer will loop through and make sure that each Entity gets dropped into the EntitySet<T> for that particular Entity type.

As part of loading you can opt for whether the Entity that’s being loaded will;

  • be ignored if an Entity with the same ID is already present in the EntitySet
  • overwrite an existing Entity with the same ID in the EntitySet even if that Entity has been modified
  • overwrite only unmodified properties of an existing Entity with the same ID in the EntitySet

EntitySet

The EntityContainer contains a bunch of EntitySets.

There’s the EntitySet class and its derived class EntitySet<T>. As the name suggests, this is a set of Entity of a particular type that also supports property change notification and revertible change tracking along with collection changed notification.

An EntitySet can be associated with an EntityContainer and ( so far as I can work out so far ) the only way to do that is to have the EntityContainer create the EntitySet via the EntityContainer.CreateEntitySet<T> method which creates the EntitySet<T> and sets its EntityContainer property to the right value ( i.e. the owning EntityContainer );

image 

I guess the properties/methods largely speak for themselves with the possible exception of Attach/Detach which feel familiar to me from [LINQ to SQL/LINQ to Entities]. If Add() means “treat this as a newly created entity” then there needs to be a method that means “treat this as an entity that already exists” and Attach() looks to have those semantics.

Entity

EntitySet contains a bunch of Entity objects.

Entity is quite an interesting class – it’s an abstract class so the intention is that you derive from it and it implements a whole slew of interfaces;

image

and so an Entity is an object that supports;

  • property change notification ( fairly standard )
  • being an editable object ( fairly standard too – I’ve used this before in combination with the DataForm )
  • the new validation interface from Silverlight 4 – INotifyDataErrorInfo ( fairly standard when you’ve seen it but a little bit painful to implement )
  • the notion of change tracking via IChangeTracking and the idea of reversing those changes via the derived IRevertibleChangeTracking

and so I can write code that makes use of the ability of the object to track changes and so on such as;

      PeopleContainer container = new PeopleContainer();

      Person person = new Person()
      {
        Id = 1,
        FirstName = "Mike",
        LastName = "Taulty"        
      };

      EntitySet<Person> personSet = container.GetEntitySet<Person>();
      Debug.Assert(person.EntityState == EntityState.Detached);

      personSet.Add(person);
      Debug.Assert(person.EntityState == EntityState.New);

      person = new Person()
      {
        Id = 2,
        FirstName = "Mike",
        LastName = "Taulty"
      };
      personSet.Attach(person);
      Debug.Assert(person.EntityState == EntityState.Unmodified);

      person.LastName = "Jones";
      Debug.Assert(person.EntityState == EntityState.Modified);

      person = (Person)person.GetOriginal();
      Debug.Assert(person.LastName == "Taulty");

      int identity = (int)person.GetIdentity();
      Debug.Assert(identity == 2);

      Debug.Assert(personSet.HasChanges);

      ((IRevertibleChangeTracking)personSet).RejectChanges();

      Debug.Assert(personSet.Count == 1);

      foreach (Person p in personSet)
      {
        Debug.Assert(p.HasChanges == false);
        Debug.Assert(p.EntityState == EntityState.Unmodified);
        Debug.Assert(p.LastName == "Taulty" && p.Id == 2);
      }

and it’s clear that the Entities involved here go through states represented by their EntityState property depending upon what’s been done to them and there’s also the notion of the original values being accessible ( for the entity that I Attached because it perhaps doesn’t really make sense for the Entity that I Added ) and then being able to Accept or, in this particular case, RejectChanges to get back to where I started.

Entity manages to do this kind of change tracking because it has methods RaiseDataMemberChanging and RaiseDataMemberChanged so it’s possible for the base class implementation to be aware of before/after values and track the changes being made.

What else can Entity do?

Validate

It has capabilities for validation. By default ( i.e. if you don’t override ) this is based on the System.ComponentModel.DataAnnotations attributes ( and custom variants of those ) so if I updated my Person entity on the server side to be something like;

public class Person
  {
    [Key]
    public int Id { get; set; }

    [StringLength(10, ErrorMessage="Too long")]
    public string FirstName { get; set; }
    public string LastName { get; set; }
  }

then I might write a little client-side code;

    Person person = new Person()
      {
        Id = 1,
        FirstName = "Mike",
        LastName = "Taulty"        
      };
      Debug.Assert(!person.HasValidationErrors);

      person.FirstName = "NameTooLong";

      Debug.Assert(person.HasValidationErrors);

and, because the property setter for FirstName includes a call to the ValidateProperty() method, that causes validation to fire and so I have validation errors once that property set has completed. I could find out more about those errors with code like;

      Debug.Assert(person.ValidationErrors.Count == 1);
      Debug.Assert(person.ValidationErrors.First().MemberNames.First() == "FirstName");
      Debug.Assert(person.ValidationErrors.First().ErrorMessage == "Too long");

and they also surface via the Entity class implementing INotifyDataErrorInfo so I could use that interface to determine similar information.

Validation capabilities in RIA Services are not tied to only the built-in attributes from System.ComponentModel.DataAnnotations – you can do a whole bunch more around custom validation at both the property and the entity level.

I’ll follow up on this in a later post but as an example if I wanted to do a little cross-field validation on my entity by adding this code on the server side ( to a file called PersonValidator.shared.cs in order to share that code with the client-side );

public static class PersonValidator
  {
    public static ValidationResult ValidatePerson(Person person, ValidationContext ctx)
    {
      ValidationResult result = ValidationResult.Success;

      if ((person.FirstName != "Mike") ||
        (person.LastName != "Taulty"))
      {
        result = new ValidationResult("Wrong name",
          new string[] { "" });
      }
      return (result);
    }
  }

and then applying that attribute to my Person entity on the server-side;

 [CustomValidation(typeof(PersonValidator), "ValidatePerson")]
  public class Person
  {
    [Key]
    public int Id { get; set; }

    [StringLength(10, ErrorMessage="Too long")]
    public string FirstName { get; set; }
    public string LastName { get; set; }
  }

then I can write client-side code to exercise this a little as in;

 Person person = new Person()
      {
        Id = 1,
        FirstName = "Mike",
        LastName = "Taulty"        
      };

      ValidationContext context = 
        new ValidationContext(person, null, null);

      Debug.Assert(
        Validator.TryValidateObject(
          person,
          context,
          person.ValidationErrors));

      person.LastName = "Smith";

      Debug.Assert(
        !Validator.TryValidateObject(
          person,
          context,
          person.ValidationErrors));

      Debug.Assert(person.HasValidationErrors);

and so the Entity class clearly has validation capabilities.

Invoke A Custom Update Method

The Entity class also has these intriguing methods/properties (protected) called InvokeAction, IsActionInvoked, CanInvokeAction and an enumerable EntityActions property.

What’s this about? It looks to come down to the ability to specify custom update methods to be called server-side at the time of a SubmitChanges call.

As an example, I can write some method called Foo on the server-side like this one added to my domain service;

  [EnableClientAccess()]
  public class MyDomainService : DomainService
  {
    [Update(UsingCustomMethod = true)]
    public void Foo(Person p)
    {
    }

and one of the ways that manifests itself on the client-side is by these additional generated methods on the Person class;

        [Display(AutoGenerateField=false)]
        public bool IsFooInvoked
        {
            get
            {
                return base.IsActionInvoked("Foo");
            }
        }
        
        [Display(AutoGenerateField=false)]
        public bool CanFoo
        {
            get
            {
                return base.CanInvokeAction("Foo");
            }
        }
        
        public void Foo()
        {
            this.OnFooInvoking();
            base.InvokeAction("Foo");
            this.OnFooInvoked();
        }
        
        protected override void OnActionStateChanged()
        {
            base.UpdateActionState("Foo", "CanFoo", "IsFooInvoked");
        }

So, the method call Foo() that we make is turned into a call onto the base-class Entity.InvokeAction() method.

The base-class effectively captures the details of the method call made ( including parameters if we had any ) such that those details can later be retrieved from the Entity.

In the usual scenario this is used by the DomainContext in order to defer the server-side invocation of the method Foo until DomainContext.SubmitChanges() is called when the corresponding server-side functionality will be invoked as part of the whole SubmitChanges cycle.

Sticking with my made-up PeopleContainer class on the client-side I can write a little code against this functionality such as;

      PeopleContainer container = new PeopleContainer();

      Person person = new Person()
      {
        Id = 1,
        FirstName = "Mike",
        LastName = "Taulty"        
      };

      container.GetEntitySet<Person>().Add(person);

      person.Foo();

      Debug.Assert(person.IsFooInvoked);

      Debug.Assert(!person.CanFoo);

      Debug.Assert(person.EntityActions.Single().Name == "Foo");

and we can see that by calling Foo what I’ve actually done is to set a flag to say that Foo has been invoked, that it cannot be invoked again ( right now ) and the framework has stored the fact that I called Foo into the EntityActions collection.

Whilst EntityActions is a collection, as far as I can tell it only supports the notion of a single custom invocation being outstanding at any one time.

So, we have this notion of “capturing” a method call on an Entity instance along with the parameters that it was invoked with ( not shown here ) such that the other componentry can later invoke the corresponding server-side functionality.

All in all, EntityContainer, EntitySet and Entity are pretty useful classes to have around on the client side providing a lot of functionality for us that we’d otherwise have to write.

The DomainContext

What’s left for the DomainContext then, given that;

  • the EntityContainer deals with storing all the Entity instances into neat little organised sets and has capabilities for change tracking, validation and so on
  • the DomainClient deals with the logistics of making sure that Query, Update, Invoke operations get from the client-side to the server-side and back again.

Largely – I think it glues these other two types together.

It does some work in its Load methods to use the DomainClient in order to load the entities for the EntityQuery from the server-side and it takes the results of that operation and pushes them into the EntityContainer via its LoadEntities method.

It also does a bunch of work around its SubmitChanges method to communicate with the EntityContainer and determine what changes have been made to the data before building up an EntityChangeSet to pass through into the DomainClient and get those changes submitted across to the server-side.

It also provides a place for the code-generation tools to work upon in the sense that if I have a domain service;

  public class Person
  {
    [Key]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
  }

  [EnableClientAccess()]
  public class MyDomainService : DomainService
  {
    public IQueryable<Person> GetPeople()
    {
      return ((new List<Person>()
      {
        new Person() { Id = 1, FirstName = "Fred", LastName = "Smith" }
      }).AsQueryable());
    }
  }

then the code-generation process produces me a DomainContext;

public sealed partial class MyDomainContext : DomainContext
    {
        
        partial void OnCreated();

        public MyDomainContext() : 
                this(new WebDomainClient<IMyDomainServiceContract>(new Uri("SilverlightApplication5-Web-MyDomainService.svc", UriKind.Relative)))
        {
        }
        
        public MyDomainContext(Uri serviceUri) : 
                this(new WebDomainClient<IMyDomainServiceContract>(serviceUri))
        {
        }
        
        public MyDomainContext(DomainClient domainClient) : 
                base(domainClient)
        {
            this.OnCreated();
        }
        
        public EntitySet<Person> Persons
        {
            get
            {
                return base.EntityContainer.GetEntitySet<Person>();
            }
        }
        
        public EntityQuery<Person> GetPeopleQuery()
        {
            this.ValidateMethod("GetPeopleQuery", null);
            return base.CreateQuery<Person>("GetPeople", null, false, true);
        }
        
        protected override EntityContainer CreateEntityContainer()
        {
            return new MyDomainContextEntityContainer();
        }

	// Snipped out the definition of the service interface here.       
        
        internal sealed class MyDomainContextEntityContainer : EntityContainer
        {
            
            public MyDomainContextEntityContainer()
            {
                this.CreateEntitySet<Person>(EntitySetOperations.None);
            }
        }
    }

and the generation process does quite a lot to take MyDomainContext and derive it from DomainContext.

It’s reasonably clear that the Persons property is just reaching into the EntityContainer for the right EntitySet<T> and that when we perform a MyDomainContext.Load() using the results of MyDomainContext.GetPeopleQuery() then the DomainContext will use the DomainClient to grab the data from the server-side and then drop the results into the EntityContainer again.