A new RIA approach in LightSwitch: the ServerApplicationContext RIA Service.

Introduction

Ria Services are in the LightSwitch community very popular.  They are interesting because they allow me to reshape my data in a very elegant way.

In this article I’ll introduce another way to use RIA Services based on the LightSwitch V3 ServerApplicationContext.

How are RIA services traditionally used in LightSwitch?

As you know, when it comes to databases, we distinct in LightSwitch 2 basic approaches

  • the intrinsic database approach or
  • the external database approach.

In contrast to the external approach, when working intrinsically, LightSwitch takes all responsibility for the (intrinsic) database scheme.

Now, what’s the role of RIA Services in this picture. Basically, a RIA service is a service layer on top of a data layer (in most cases, but not necessarily,  based on entity framework).

Why are they interesting?

Also RIA services allows 2 approaches

  • they allow me to connect to and reshape an  external data source: this can be really anything, I can even provide an Iqueryable “connection” to an xml file.
  • they allow me to reshape my existing LightSwitch data model. Classic example (remember this, I’ll refer to soon): I have a customer and an order table and I want to have a DTO (data transfer object) composed of CustomerName and OrderCount. This is called an aggregation and this is not out-of-the-box available in LightSwitch. So, thank you RIA services ! I tend to call this type of RIA Service: an intrinsic database RIA Service.

In most cases the second approach, the intrinsic database RIA Service is used. There is a handy Nuget package available for injecting the necessary functionality directly in a .Net 4.0 Class Library:

ria

What are the drawbacks?

The LightSwitch application domain logic is completely ignored.

Although this might be desired behavior in some very particular cases, the intrisic database RIA service completely ignores your application domain logic.  An example can illustrate: if  in my above “classic” example, my customer table has a Filter method (e.g. only show customers from Belgium), this rule will be completely ignored in my intrinsic database RIA Service. In other words when I retrieve the Customer/OrderCount DTO I get all customers, also the ones not living in Belgium.

Project compilation will trigger hundreds of compilation warning.

This is a rather annoying side-effect of including the ApplicationDataObjectContext.cs (which is one the LightSwitch auto generated files) into the RIA Service project. This is because the imported types conflict with the existing LightSwitch types.

 

Tata, a new approach: the ServiceApplicationContext RIA Service

In order to grasp the idea quickly, simply start with generating a classic Ria Service (e.g. based on my nuget package) and update the LightSwitch domain service base class (I prefer to isolate this logic in a base class)

using System.Linq;
using System.ServiceModel.DomainServices.Server;
using Microsoft.LightSwitch.Server;
using Microsoft.LightSwitch;
using Microsoft.LightSwitch.Details;

namespace RiaService
{
    public class LightSwitchDomainServiceBase : DomainService
    {
        IServerApplicationContext _context = ServerApplicationContextFactory.Current;

        public IServerApplicationContext Context
        {
            get
            {
                return _context;
            }
        }

        protected override int Count<T>(IQueryable<T> query)
        {
            return query.Count();
        }
        internal IDataServiceQueryable GetEntitySetDataService(string dataSourceName, string entitySetName)
        {
            IDataService dataService 
                = Context.DataWorkspace.Details.Properties[dataSourceName].Value as IDataService;

            var entitySet = dataService.Details.Properties.All()
                .OfType<IDataServiceEntitySetProperty>()
                .Where(n => n.Name == entitySetName).SingleOrDefault().Value;

            return dataService.Details.Properties[entitySet.Details.Name].Value as IDataServiceQueryable;
        }
    }
}

Note that you need a binary reference to Microsoft.LightSwitch.dll (from C:\Program Files (x86)\Microsoft SDKs\LightSwitch\v3.0\Client\Microsoft.LightSwitch.dll) and Microsoft.LightSwitch.Server.dll (from C:\Program Files (x86)\Microsoft SDKs\LightSwitch\v3.0\Server\Microsoft.LightSwitch.Server.dll).

So, what’s different here? Well, in a traditional RIA service we would make a connection to the intrinsic database by reusing the ObjectContext:

public ApplicationData Context 
        {
            get
            {
                if (this.m_context == null)
                {
                    string connString =
                        System.Web.Configuration.WebConfigurationManager
                        .ConnectionStrings["_IntrinsicData"].ConnectionString;
                    EntityConnectionStringBuilder builder = new EntityConnectionStringBuilder();
                    builder.Metadata =
                        "res://*/ApplicationData.csdl|res://*/ApplicationData.ssdl|res://*/ApplicationData.msl";
                    builder.Provider =
                        "System.Data.SqlClient";
                    builder.ProviderConnectionString = connString;
                      //for a lightSwitch V2 project change in the next line ApplicationData to ApplicationDataObjectContext
                    this.m_context = new ApplicationData(builder.ConnectionString);
                }
                return this.m_context;
            }
        }

In our new approach we won’t longer use this! Instead of setting up the ObjectContext we simply setup an IServerApplicationContext ! There is one helper method included in the base class, which allows us to retrieve easily a DataService based on name of an entitySet (e.g. Customers). It’s not at all mandatory to use this, the base class provides easy access to the ServerApplicationContext via the “Context” property. As a result: full access to everything, and much more than in a regular Ria Service !

So far so good. Let’s create now a domain service based on the above base class for following data transfer object:

  public class ExampleCustomerDTO
    {
        [Key]
        public int Id { get; set; }
        public string CustomerName { get; set; }
    }

The domain services goes as follows:

namespace RiaService.Services
{
    using System.Linq;
    using RiaService.DTO;
    using System.ServiceModel.DomainServices.Server;
    using Microsoft.LightSwitch;

    public class ExampleCustomerDomainService : LightSwitchDomainServiceBase
    {
        [Query(IsDefault = true)]
        public IQueryable<ExampleCustomerDTO> GetCustomersDTO()
        {
            string dataSourceName = "ApplicationData";
            string entitySetName = "Customers";
            var query = this.GetEntitySetDataService(dataSourceName, entitySetName);//as IDataServiceQueryable<Customer>;

            return GetCustomQuery(query);
        }

        private IQueryable<ExampleCustomerDTO> GetCustomQuery(IDataServiceQueryable query)
        {
            var dtoQuery = from IEntityObject c in query
                           select new ExampleCustomerDTO
                           {
                               Id = (int)c.Details.Properties["Id"].Value,
                               CustomerName = c.Details.Properties["LastName"].Value.ToString()
                           };

            return dtoQuery.AsQueryable();
        }
    }
}

Basically, rather similar to a classic domain service. We split the call in two steps:

first, we retrieve an IDataServiceQueryable based on “Customers” and secondly, we massage the data into the ExampleCustomerDTO structure.

What are the advantages of the ServerApplicationContext RIA approach?

  •  The application domain logic is fully respected: if I have a filter (row level security) on Customers, the ServerApplicationContext RIA Service is fully respect these rules !
  • Since we don’t share any generated files with the LightSwitch server project, we got rid of all compilation warnings.
  • We have full access to the Server application object: this means that we can access also other datasources inside the Application model and do aggregations between data sources !

What’s the disadvantage?

We lose strongly typed access when doing the DTO projection:

var dtoQuery = from IEntityObject c in query
                           select new ExampleCustomerDTO
                           {
                               Id = (int)c.Details.Properties["Id"].Value,
                               CustomerName = c.Details.Properties["LastName"].Value.ToString()
                           };

As you can see, we access the EntityObject by means of the weakly typed API. This can unfortunately not be solved by adding a file reference to ApplicationDataObjectContext class because the “EntityObjects” inside this class are based on material inside LightSwitchApplication.Implementation, which differ from the types present in “LightSwitchApplication”.

Nonetheless, I’m convinced there is a workaround for this.

Try it out.

Here is a sample : RiaServerApplicationContext

The sample has both the classic RIA service and the ServerApplicationContext RIA service. It has a customer table with a simple row level security rule (only customers with last name starting with ‘x’ are returned). You can easily verify the different behavior and play with it, in case you like it :)

Note that my project has still compilation warnings, but that’s because of the classic RIA service.