RIA Services SP2 RTW released

We’re happy to announce the final release of WCF RIA Services V1 SP2:

In this release, we add the following new features:

  • Support for Silverlight 5 (the standalone installer linked above also supports Silverlight 4)
  • DateTimeOffset support + various bugfixes
  • Entity Framework Code-First development, using this NuGet package

Known issues include:

  • Entity Framework 4.1 is required, but version 4.2 isn’t supported yet
  • Requires Visual Studio 2010 or SP1. Visual Studio 11 isn’t supported yet
  • Work on supporting Universal Providers for improved Windows Azure deployment is ongoing
  • If you want to create a Silverlight 4 Business Application Template, first create an instance of the Silverlight 5 template, and then use the Silverlight project properties to retarget it to Silverlight 4. You might see some temporary errors (System.Windows.Markup.XamlParseException) in the designer and error window, but rebuilding will make those disappear.

Active Federation using RIA Services and WIF

UPDATE: A coworker here at Microsoft has brought up some interesting thoughts regarding this approach, and I’ve added a brief discussion below the second diagram.

Recently I decided to experiment with RIA Services in federated authentication scenarios. For those of you that aren’t familiar with federation, let’s consider the following scenario: my company Contoso builds an expense reporting application that I host in the cloud. One day I get Fabrikam to sign up as a client, and now I have to onboard all Fabrikam employees onto my system. It is true that I could create a login for every employee, but that is impractical because Fabrikam employees have to remember that extra login, and also I would need to keep my list of logins in sync when employees join or leave Fabrikam.

The solution for this is federated authentication, where my app can use a third-party (Fabrikam’s) authentication provider (also known as a Security Token Service or STS) to authenticate users. I won’t try to explain federation from scratch, because Microsoft’s patterns & practices has a great write-up. You can squint and ignore some of the claims stuff as it’s not super relevant to what I’m describing here.

To build an app with federated auth using RIA Services, most folks resort to passive federation, which you might be familiar with from using things like your LiveID login. The passive approach is browser-friendly and uses a combination of cookies, URIs and redirects to log you in to the site you’re trying to access.  The downside in the case of Silverlight is that you cannot complete the login inside your app: you have to navigate away from the Silverlight control, type in your credentials into a HTML (or ASPX) page, and then be bounced back by your browser to reload the control. Another downside is that you need to host a HTML page for the user to type in their credentials, in addition to the Silverlight app and the web services, which might not be desirable.

The following diagram has a more detailed explanation of the exact flow that happens during the passive scenario:

Passive federation with RIA Services

From what I’ve seen, most folks use this passive pattern with RIA today. Eugenio Pace’s blog has a few great write-ups and demos that you can try out. Some are a bit dated, but you should be able to find your way around.

What I wanted to try out was the active federation case, in which the Silverlight app talks dirctly to the identity provider. This has the benefit of having the user type their credentials directly into the Silverlight app, without having to leave the page, which results in a more polished and faster user experience. Eventually, as the crypto support in Silverlight improves, it may end up being a more secure solution as well, if we are able to encrypt and sign the tokens that are being exchanged. The following diagram demonstrates the slightly different message flow in the active case. Note that the app itself is doing most of the work, not the browser.

Active federation with RIA Services

Some interesting considerations emerged in conversation with Robert O’Brien, an architect here at Microsoft. Please consider these before building your own app:

  • One of the things we gain in this approach over a passive federation approach is the ability to log the user in from inside our UI. That provides for a better user experience, but it also puts the burden on our app to expose a credentials UI and pass the token over to the service. Potentially this could be a security risk for our app. Some folks believe that this goes against the spirit of federated authentication since it takes control away from the identity provider.
  • The implementation shown here relies on WS-Trust as the protocol for the token. Unfortunately in today’s federated web, it seems like only ADFS and maybe OpenID servers honor that protocol. If you are trying to use WS-Trust and use Facebook/Yahoo/Google (either directly or through Azure’s Access Control Service) as the identity provider, you will find that WS-Trust is probably not supported. So outside the enterprise environment, this approach might not work.

Now some specifics of the sample. It’s a simple master/detail application that allows you to display and edit data. There are two registered users fabrikam\yavor and fabrikam\test and the password for both of them is 12345. If you don’t log in and try to display the data, you will get an error because of the EnableClientAccess attribute.

[RequiresAuthentication]
[EnableClientAccess()]
public class CustomersService : LinqToEntitiesDomainService<AdventureWorksLT2008R2Entities>

All authenticated users can view the data, but only users in the Editor role can make changes to it. Only the user fabrikam\yavor is in that role.

[RequiresRole("Editors")]
public void UpdateCustomer(Customer currentCustomer)

The bottom line here is that even though we have a STS with external user authentication, we kept authorization local to our app (we use ASP.NET roles). That’s a choice you can make as a developer - you can also externalize the user roles (or claims) as part of the STS, or keep them local.

There are lots of extra details in the app, which you’ll see by exploring the source.

The code is available here. Note that you need the following installed on your machine for it to work:

Here are some additional notes on the structure of the sample:

  • First, please run the included SetupCertificates.cmd script inside the Scripts folder
  • The solution will create two applications in your local IIS instance: AddressBookFederatedAuth and IdentityProviderAndSts. Make sure you launch Visual Studio as Administrator, so it has permission to create those. Also make sure you enable HTTPS so both of the applications in the IIS Manager
  • Change the Application pool under which those applications are running to use the Network Service account. 
  • Use SQL Server Management Studio, make sure Network Service is added as a login to your SQL instance. When you add the account, make sure you give him some sane Server Roles (for example sysadmin). Let me know in the comments if you find any of these steps difficult and I can provide more detailed instructions.

I want to credit Eugenio Pace, Kyle McClellan, and the folks behind the Identity Developer Training Kit, from where I shamelessly stole some code.

RIA Services T4 template to copy comments from server to client

As you know one of the new features in RIA Services V1 SP1 is support for T4 templates. The T4 template is a design-time artifact that can modify the way RIA Services client-side code generation happens. For more information on T4 templates, check out Jeff Handley’s blog post on the subject.

Recently I was experimenting with RIA Services and I discovered that the IntelliSense comments for my entities were very sparse on the client. For example my Customer entity has a CompanyName property and the IntelliSense comment for that was:

Gets or sets the ‘CompanyName’ value. 

It was clear that this comment was being generated automatically, which was quite annoying because I had used my entity model on the server to carefully create useful comments for each property.

EF designer showing property documentation

I hear EF is actually smart enough that it supports defining these comments in the database itself, and they will get copied over when you create the model, but I haven’t tried it myself.

So I set out to write a T4 template to take those comments from the server types generated by EF and copy them over to my Silverlight project.

The first step was to tell Visual Studio to scrape all the types in my server project and generate a XML file containing their comments, by going to the Build tab of project properties and checking this box: 

Enabling XML comments in project properties

This file is usually used for IntelliSense, but you can open it up and the schema is pretty self-explanatory. Because code comments don’t get compiled into assemblies, this is the only way I know to tell VS to export the comments from my entities.

The next step was to write a simple T4 template using RIA Services extensibility. What this does is that as it is about to generate a type on the client (which happens when you build), it will look up that type name and all its properties in the above XML file, and copy the comments over to the generated file.

[DomainServiceClientCodeGenerator(typeof(CommentsClientCodeGenerator), "C#")]
public class CommentsClientCodeGenerator : CSharpClientCodeGenerator
{
    private XElement comments;

    public CommentsClientCodeGenerator()
    {
        comments = XElement.Load(new FileStream("..\\AddressBook.Web\\bin\\AddressBook.Web.XML", FileMode.Open));
    }

    protected override EntityGenerator EntityGenerator
    {
        get
        {
            return new CommentEntityGenerator(comments);
        }
    }

    public class CommentEntityGenerator : CSharpEntityGenerator
    {
        private XElement comments;

        public CommentEntityGenerator(XElement comments)
        {
            this.comments = comments;
        }

        private string PrintInnerXml(XElement element)
        {
            string[] lines = element.Nodes()
                .Aggregate("", (b, node) => b += node.ToString())
                .Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            StringBuilder sb = new StringBuilder();
            foreach(string line in lines)
            {
                sb.AppendLine(line.Trim().Insert(0, "///"));
            }
            return sb.ToString().Trim();
        }

        private XElement FindCommentElementByName(string name)
        {
            return comments
                .Descendants("member")
                .Where(x => String.Compare(x.Attributes("name").FirstOrDefault().Value.ToString(), name, true) == 0)
                .FirstOrDefault();
        }

        protected override void GenerateClassDeclaration()
        {
            XElement element = FindCommentElementByName("T:" + this.Type.FullName);
            if (element != null)
            {
                this.WriteLine(PrintInnerXml(element));
            }
            base.GenerateClassDeclaration();
        }

        protected override void GenerateProperty(PropertyDescriptor propertyDescriptor)
        {
            XElement element = FindCommentElementByName("P:" + propertyDescriptor.ComponentType.FullName + "." + propertyDescriptor.Name);
            if (element != null)
            {
                this.WriteLine(PrintInnerXml(element));
            }
            base.GenerateProperty(propertyDescriptor);
        }
    }
}

That’s all I had to do… as soon as I started building this class (in its own project) inside my solution, the code comments I defined on the server started appearing in my Silverlight client.

The code is available here. Note that you need the following installed on your machine for this to work:

Hope you find this useful!

Презентацията ми за WCF RIA Servces от Дни на Microsoft ‘11

Благодаря на всички които дойдоха на моята презентация за RIA Services миналата седмица по време на Дни на Microsoft ‘11. Отдолу ще намерите презентацията и примерите които показах.

Примери

Integrating WCF Routing with RIA Services

Recently we have had a few customers ask us how WCF RIA Services integrates with the WCF Routing features shipped in .Net 4. I spent some time over the last week building a prototype and learning about the issues you encounter in this scenario.

The sample source code is available here for download. Please keep in mind that in order for the sample works, you need the AdventureWorksLT database deployed to your local SQL Server Express instance… and don’t forget to update the connection string in Web.config!

First, let’s go over the scenarios where a router may be needed:

  • Deployment in a DMZ: In this enterprise deployment scenario, a DMZ is used to separate the untrusted network (usually the Internet) from the application/data tier (where a domain service may live). In this case all the router does is pipe traffic from a public Internet address to a private address in the trusted subnet. There is no smartness required on the part of the router and at this point it is just doing HTTP in/HTTP out piping with very little regard as to the message payload.
  • Content-based routing: In this case the router needs to open up the incoming message and make a decision, based on some information in that message, which endpoint to route the message to. This can be part of the DMZ scenario, but it is not required to enable it.

In the DMZ scenario, the WCF router may be overkill (correct me if I’m wrong). The WCF router is content-based, which means can decode incoming messages and make decisions based on different filters. Unless you are using the content routing features, a simpler router such as IIS’ Application Request Routing will offer better performance and probably be easier to set up. Sandrino Di Mattia has a really good write-up about how to do this.

With this disclaimer out of the way, here is how I got RIA Services working with the WCF content-based router.

First hurdle: binary-encoded POX

In order to offer optimal wire size, RIA Services uses WCF’s binary encoder combined with plain old XML (referred to as POX, in contrast to SOAP). In WCF terms that means the BinaryMessageEncodingBindingElement needs to be configured with MessageVersion.None. However if you try that yourself, you’ll notice that the current implementation of the encoder does not allow that. RIA Services uses a private version of the encoder. So in my sample, I provide my own implementation of the binary encoder, which supports plain XML. Here is the binding that the router needs to understand RIA Services traffic.

Binding binaryPoxBinding = new CustomBinding( 
   new RiaBinaryMessageEncodingBindingElement(), 
   new HttpTransportBindingElement { 
      ManualAddressing = true, 
      MaxReceivedMessageSize = int.MaxValue
   } 
);

Second hurdle: switching to HTTP POST

Domain services use HTTP GET requests by default on their queries. GET offers caching benefits over POST, which are particularly relevant in the case of domain services, where large data sets with static data may be retrieved frequently.

Unfortunately, I could not figure out a way to get the WCF router to successfully route a GET request. Speaking to the team who designed the router, it seems SOAP scenarios took precedence to REST scenarios when they did the work, and since SOAP does everything over POST, the other HTTP verbs all get turned to POSTs. This is unfortunate, however it is easy to tell the domain service to switch to using only POST:

[QueryAttribute(HasSideEffects = true)]
public IQueryable GetCustomers()

Final hurdle: URI query strings

Along the same lines as the previous issue, the WCF router will ignore anything that comes in the URI after the endpoint name. This is unfortunate, as domain services expect the name of the query to come in the URI. WCF extensibility comes to the rescue, and by plugging in a simple message inspector, we can correct the address of outgoing messages.

public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
{ 
   // This ensures that the router will correctly roundtrip URI query string parameters
   object via; 
   if (request.Properties.TryGetValue("Via", out via)) 
   { 
      string query = (via as Uri).AbsolutePath.Replace(_routerAddress.AbsolutePath, "");
      UriBuilder builder = new UriBuilder(_clientAddress);
      builder.Path += query;
      request.Headers.To = builder.Uri; 
   } 
   return null; 
}

These three fixes are included in the sample project. Once the fixes are applied, we can start taking advantage of the capabilities of the router. By default we plug in a filter that matches all incoming messages.

rc.FilterTable.Add(new MatchAllMessageFilter(), endpointList);

However nothing prevents us from using some more sophisticated filters such as the XPathMessageFilter. Using that filter, we could look for specific elements inside the domain service message and route to different services based on that.

Hope this is useful to folks out there!
-Yavor