Silverlight 3 and RIA Service – Creating a Proxy for Cross Domain HttpRequests

One of the first things I thought about after writing my first two articles on RIA Services (Basic and Advanced) was how I could take advantage of this new technology.

The first thing I could thing of was a solution that helps overcoming the Cross Domain Issues when accessing resources on the internet. What about something like a proxy service to overcome cross domain issues? Access our known proxy server which will forward the request to the actual resource on the internet.

image

Let’s first define the interface to communicate through. A very simple ProxyRequest that contains the resource to access on the ‘Any resource’ side, and an Id to make sure we can correctly associate the Request and the Response later on.

namespace MM.Ria.CrossDomainProxy
{
    public class ProxyRequest
    {
        [Key]
        public Guid Id { get; set; }

        public string Url { get; set; }
    }
}

The ProxyResponse is very simple as well. Almost the same as the ProxyRequest a RequestId associated with the Id in the ProxyRequest, the Content and of course something to store any error that occurred.

namespace MM.Ria.CrossDomainProxy
{
    public class ProxyResponse
    {
        [Key]
        public Guid RequestId { get; set; }

        public string Content { get; set; }

        public string Error { get; set; }
    }
}

And we need an Interface with an Operation, let’s call it process.

namespace MM.Ria.CrossDomainProxy
{
    public interface IProxy
    {
        ProxyResponse Process(ProxyRequest request);
    }
}

We now create the RIA Service implementation, by just delegating everything to a RegularProxy implementation. So this one is simple as well.

namespace MM.Ria.CrossDomainProxy
{
    [EnableClientAccess]
    public class DomainProxy : DomainService, IProxy
    {
        private readonly RegularProxy m_proxy = new RegularProxy();

        [ServiceOperation]
        public ProxyResponse Process(ProxyRequest request)
        {
            return m_proxy.Process(request);
        }
    }
}

And the magic is in the RegularProxy, don’t think we should call it magic though. Just an usage of the WebClient class to download the data from the Url.

namespace MM.Ria.CrossDomainProxy
{
    public class RegularProxy : IProxy
    {
        public ProxyResponse Process(ProxyRequest request)
        {
            var response = new ProxyResponse {RequestId = request.Id};
            try
            {
                using (var client = new WebClient())
                {
                    string data = client.DownloadString(request.Url);
                    response.Content = data;
                }
            }
            catch (Exception exception)
            {
                response.Error = exception.Message;
            }

            return response;
        }
    }
}

Don’t forget this is a very basic Proxy implementation and doesn’t implement all the features we can think of. But this first basic set up can easily be enhanced with for example Credentials support, Caching and other things as well.

But how can we use it in our Silverlight application? Simple as well.

var domainProxy = new DomainProxy();
domainProxy.ProcessCompleted += (sender, e) => Debug.WriteLine((e.ReturnValue as ProxyResponse).Content);
domainProxy.Process(new ProxyRequest
                        {
                            Id = Guid.NewGuid(), 
                            Url = "http://www.silverlight.net/"
                        });

Looks like the keyword is simple when we’re creating RIA Services. I really like it.

Ps. This article is cross posted on: Mark Monster’s blog and Silverlight Help.

My focus on Silverlight

I think most of my readers noticed a while ago. My focus is almost completely on Silverlight. This is because I think Silverlight is a very interesting piece of technology. Besides that I think there are lots of blogs about general .NET. Specialization is my power I think. I’m not able to write a lot of in depth articles about general .NET that aren’t about a topic that’s covered on another blog as well. So that’s my focus on Silverlight.

I think some of my articles are ranked good in the Silverlight community. A few minutes ago I had a look at the Skim page of SilverlightCream. My article about caching in Silverlight made it to this weeks top 5.

image

This encourages me to continue my work on Silverlight. I know there’s even a lot to learn on the Silverlight area, specially after Silverlight 3 Beta came out. But when a few weeks ago Ken Tucker asked me to contribute to Silverlight-Help, I just couldn’t say no. That’s why you can now read my Silverlight articles on both Silverlight-Help and this blog. Please let me know if you have questions or if you want to help on Silverlight-Help as well.

Silverlight 3 and RIA Services – The advanced things

My last article was about the basics of RIA Services. This article goes about the more advanced things in RIA Services. This article contains a few things that make the usage of RIA Services in more advanced scenario’s possible.

  • Complex Types in RIA Services
  • RIA Services in a separate class library
  • RIA Services and Out of browser mode
  • RIA Services on a different host

Using Complex Types in RIA Services

In my other article I just made use just a string as input parameter and a string as output parameter. It’s however very well possible to use custom types instead. There’s hower one prerequisite for those custom types.

  • Each Custom Type needs to have one property decorated with a KeyAttribute. This is required for input and output types.

I created a WorldRequest and a WorldResponse which fits nicely in the example for HelloWorld.

public class WorldRequest
{
    [Key]
    public string Name { get; set; }
}

public class WorldResponse
{
    [Key]
    public string Greeting { get; set; }
}

This can be used in the HelloWorldDomainService just like you would do in a normal class.

[EnableClientAccess]
public class HelloWorldDomainService : DomainService
{
    [ServiceOperation]
    public WorldResponse HelloWorld(WorldRequest worldRequest)
    {
        return new WorldResponse { Greeting = string.Format("Hello {0}.", worldRequest.Name) };
    }
}

After compilation those custom types get generated in the Silverlight project as well as we saw in my previous article. The Custom Type is now overloaded from Entity. And has a specific method called GetIdentity. This has something to do with the KeyAttribute that’s required to add to one property inside a custom types. Although this is probably necessary for integration with the Entity Framework it doesn’t make sense in my example.

[DataContract(Namespace="http://schemas.datacontract.org/2004/07/MM.Ria")]
public sealed partial class WorldRequest : Entity
{
    
    private string _name;
    
    [DataMember()]
    [Key()]
    public string Name
    {
        get
        {
            return this._name;
        }
        set
        {
            if ((this._name != value))
            {
                this.ValidateProperty("Name", value);
                this.RaiseDataMemberChanging("Name");
                this._name = value;
                this.RaiseDataMemberChanged("Name");
            }
        }
    }
    
    public override object GetIdentity()
    {
        return this._name;
    }
}

We can now easily use this generated class as we would have done in different scenario’s.

var helloWorld = new HelloWorldDomainContext();
helloWorld.HelloWorldCompleted += HelloWorldHelloWorldCompleted;
helloWorld.HelloWorld(new WorldRequest{Name = "Mark Monster"});

And in the EventHandler we have to cast the ReturnValue property to the WorldResponse type. Again I hope they make InvokeEventArgs a generic type so that we don’t have to cast this ReturnValue and have it return the correct type.

private void HelloWorldHelloWorldCompleted(object sender, InvokeEventArgs e)
{
    Debug.WriteLine((e.ReturnValue as WorldResponse).Greeting);
}

Move code for the RIA Service to a class library

Next thing that came in my mind was the fact what if I want to share my RIA Services with other people? Do they need to have the source or is a different approach possible?

Luckily it’s possible to create class library (standard .NET) add references to a few dlls.

image

After that just move your RIA Service code in there. This library can now be used in any Web project that’s linked to a Silverlight application. Just add a reference to the new library containing your RIA Service and while building code will get generated in your Silverlight application.

image

What about using RIA Services in the Out of browser mode?

It just works. I didn’t have to do anything to make the Out of browser version communicate with the RIA Services.

When the host running the RIA Services is different from the url hosting the Silverlight application?

Of course in some scenario’s the RIA Services are hosted on a different location than the Silverlight application itself. Of course you have to remind yourself about cross domain issues, so make sure the host has a clientaccesspolicy.xml or crossdomain.xml file in place.

When we take a closer look into the generated code we’ll find a default constructor and a constructor where you can specify the service URI.

/// <summary>
/// Default constructor.
/// </summary>
public HelloWorldDomainContext() : 
        base(new HttpDomainClient(new Uri("DataService.axd/MM-Ria-HelloWorldDomainService/", System.UriKind.Relative)))
{
}

/// <summary>
/// Constructor used to specify a data service URI.
/// </summary>
/// <param name="serviceUri">
/// The HelloWorldDomainService data service URI.
/// </param>
public HelloWorldDomainContext(Uri serviceUri) : 
        base(new HttpDomainClient(serviceUri))
{
}

We can now make use of the constructor which accepts a serviceUri to connect to the RIA Service that’s running at a different location. The tricky part however is the DataService.axd/MM-Ria-HelloWorldDomainService/ part. Besides DataService.axd it contains also the namespace and type name for the RIA Service, but instead of using dots to separate the namespace parts and the type name dashes are used. But for the easy parts, just copy it from the generated code I would say.

var helloWorld = new HelloWorldDomainContext(new Uri("http://myhostruningsomewhere.com/DataService.axd/MM-Ria-HelloWorldDomainService/", UriKind.Absolute));
helloWorld.HelloWorldCompleted += HelloWorldHelloWorldCompleted;
helloWorld.HelloWorld(new WorldRequest {Name = "Mark Monster"});

Ps. This article is cross posted on: Mark Monster’s blog and Silverlight Help.

Silverlight 3 and RIA Services – The basics

At the Mix 09 conference Microsoft announced a new technology called RIA Services. This new technology simplifies the communication between the RIA Client Technologies like Ajax and Silverlight and the Server technology ASP.NET.

Everybody who has been using Silverlight 2 knows about the lack of support for specific features that we’re used to make use of in for example ASP.NET. And yes, we need to remember that Silverlight is a technology that runs inside the browser (or outside the browser when activated the specific Silverlight 3 Beta feature).

image

Let’s start with a very simple RIA Service and let’s call it HelloWorldDomainService. I’ve seen other articles about RIA Services but they most of the time dive deep by combining the Entity Framework right away.

First thing: what special things does a RIA Service need?

  • The RIA Service needs to be derived from DomainService, or from a different class that’s derived from DomainService.
  • The RIA Service needs to be decorated with an attribute called EnableClientAccessAttribute.
  • The operations need to be decorated with an attribute called ServiceOperationAttribute.

A very simple HelloWorldDomainService now looks like this (this class needs to be in the ASP.NET Project, not the Silverlight project).

[EnableClientAccess]
public class HelloWorldDomainService : DomainService
{
    [ServiceOperation]
    public string HelloWorld(string name)
    {
        return string.Format("Hello {0}.", name);
    }
}

Second thing that’s important is the configuration is correct.

So what’s required? A configuration for a new HttpHandler specially for RIA Services in general. Just add this piece in the HttpHandlers part of the Web.config.

<add path="DataService.axd" verb="GET,POST" type="System.Web.Ria.DataServiceFactory, System.Web.Ria, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>

Next thing is getting the Silverlight project bound to the ASP.NET project.

When you right click on the Silverlight project and choose properties you’ll get something similar to this.

image

Select the ASP.NET server project link to the ASP.NET server project that contains the RIA Services. In this example the ASP.NET server project is called MM.Sl3Experiments.Web.

Let’s build the whole solution…

… what happens? Let’s have a look at all files (also hidden files) in the Silverlight project by hitting the show all files button in the solution explorer. And you will suddenly see a folder called Generated_Code containing a file alike to <WebProjectName>.g.cs.

image

So there’s a lot of code generation behind the scenes? Yes it is. And that’s why I recommend people to write kind of a Service Agent so you’re not actually calling the generated code directly. But that’s something for a different post, or something you can figure out yourself. We’re now talking about RIA Services specifically.

But what gets generated?

For the sake of this article I removed a lot of code from this generated class, things that are just there but have no real purpose in this example. What we have left is this:

public sealed partial class HelloWorldDomainContext : DomainContext
{
    public HelloWorldDomainContext() :
        base(new HttpDomainClient(new Uri("DataService.axd/MM-Sl3Experiments-Web-HelloWorldDomainService/", System.UriKind.Relative)))
    {
    }

    public event System.EventHandler<InvokeEventArgs> HelloWorldCompleted;

    public void HelloWorld(string name)
    {
        Dictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("name", name);
        base.InvokeServiceOperation("HelloWorld", typeof(string), parameters, this.OnHelloWorldCompleted, null);
    }

    private void OnHelloWorldCompleted(InvokeEventArgs args)
    {
        if ((HelloWorldCompleted != null))
        {
            this.HelloWorldCompleted(this, args);
        }
    }
}

What we have in the generated code are the following things:

  1. A constructor to create a HelloWorldDomainContext.
  2. A method that’s called HelloWorld with typed arguments.
  3. An event that get’s called upon completion of the HelloWorld execution.

So we can easily write this code inside our Silverlight application to call the HelloWorldDomainService that’s really running on the ASP.NET Server side.

var helloWorld = new HelloWorldDomainContext();
helloWorld.HelloWorldCompleted += new EventHandler<InvokeEventArgs>(HelloWorldHelloWorldCompleted);
helloWorld.HelloWorld("Mark Monster");

Just very simple, create a new object, attach to the Completed event and call the method.

The method being called upon completion only prints the return value to the Debug window.

private void HelloWorldHelloWorldCompleted(object sender, InvokeEventArgs e)
{
    Debug.WriteLine(e.ReturnValue as String);
}

Places for improvement for RIA Services…

One thing that can be made better is the fact that InvokeEventArgs doesn’t have typed property ReturnValue. It’s of type object, and you still need to cast it to the right type. A recommendation for the team behind RIA services would be to make InvokeEventArgs generic (InvokeEventArgs<T>) to make something like this possible:

/// <summary>
/// The generated class
/// </summary>
public sealed partial class HelloWorldDomainContext : DomainContext
{
    public HelloWorldDomainContext() :
        base(new HttpDomainClient(new Uri("DataService.axd/MM-Sl3Experiments-Web-HelloWorldDomainService/", System.UriKind.Relative)))
    {
    }

    public event System.EventHandler<InvokeEventArgs<string>> HelloWorldCompleted;

    public void HelloWorld(string name)
    {
        Dictionary<string, object> parameters = new Dictionary<string, object>();
        parameters.Add("name", name);
        base.InvokeServiceOperation("HelloWorld", typeof(string), parameters, this.OnHelloWorldCompleted, null);
    }

    private void OnHelloWorldCompleted(InvokeEventArgs<string> args)
    {
        if ((HelloWorldCompleted != null))
        {
            this.HelloWorldCompleted(this, args);
        }
    }
}

And an improved way of using. The ReturnValue is now typed.

var helloWorld = new HelloWorldDomainContext();
helloWorld.HelloWorldCompleted += new EventHandler<InvokeEventArgs<string>>(HelloWorldHelloWorldCompleted);
helloWorld.HelloWorld("Mark Monster");

private void HelloWorldHelloWorldCompleted(object sender, InvokeEventArgs<string> e)
{
    Debug.WriteLine(e.ReturnValue);
}

Another improvement would be to include the generated files in the project. This is because of two reasons. The first and most important reason would be Source Control. I think we want to have those generated files under Source Control as well. We now would have to add those files manually to source control, or right click on those generated items to include them in the project, so the Source Control Systems will pick them up automatically.

The second reason I want those files to be included is: ReSharper, although I know it’s not a product by Microsoft. Because the generated files are not part of the project,  I won’t be able to make use of ReSharper’s intellisense and what about the ReSharper Code Analysis?

In the end I really like this ability to run code on the server side this easily. I know RIA Services is still in CTP, let’s hope for a few improvements. It’s already a good product, but it could be even better.

Ps. This article is cross posted on: Mark Monster’s blog and Silverlight Help.