WCF RIA Services Support for EF 4.1 (and EF Code-First)

I am pleased to announce that WCF RIA Services now supports EntityFramework 4.1 (including EF Code-First). This will be added to the WCF RIA Services Toolkit, but we are releasing it today as a NuGet package.

Background

In April 2011, EntityFramework released version 4.1 of their product, which contained 2 important updates –

  1. DbContext based API
  2. Code-First support

You can get more information about the EF 4.1 release on the ADO.NET team blog. We are adding support to these 2 features in the WCF RIA Services V1.0 SP2 release. This consists of the following additions / changes –

  • A new domain service that supports DbContext and Code-First known as DbDomainService.
  • Tooling support to generate code for the custom DbDomainService, just like the LinqToEntitiesDomainService.

This will allow you to use EF DbContext in either the data first, model first or Code First modes with WCF RIA Services.

Download

Pre-requisites

    – You will need WCF RIA Services V1.0 RTM, SP1 or SP2 installed.

NuGet package – The WCF RIA Services runtime support for EF 4.1 is in the assembly Microsoft.ServiceModel.DomainServices.EntityFramework.dll. You can get it here-

RIAServices.EntityFramework NuGet Package

DbDomainService CRUD Methods Code Snippet – For the benefit of anyone trying this preview, I have put together code snippets that will generate CRUD methods for the custom DbDomainService. Please note that these code snippets will not be released/supported officially. They are there only to make the process of trying out this preview as easy as possible. You can download the code snippets here –

C# Snippet / VB Snippet

What is not included?

1. Wizard support – The wizard support for DbDomainService is embedded in the product and cannot be added until we are ready with the next release. For now, you will have to manually code a DbDomainService or use the Code Snippets provided

2. Database Initializer settings – The EF Database Initializer needs to be tweaked depending on whether the context is runtime or design-time. This logic is also in the product and not available as a part of this preview. So for this preview, you have to set the initializer manually for design-time as I explain in the walkthrough below.

Getting started

Here I will walk you through how you can get started with using EF Code-First in a WCF RIA Services App. Let us assume that you have WCF RIA Services installed. If not, you can install it from here.

1. Create a WCF RIA Services Application

Create a Silverlight Application with the Enable RIA Services option enabled or any other WCF RIA Services project (like the Silverlight Business Application).

2. Add the RiaServices.EntityFramework NuGet package to the project.

To do that, go to Package Manager Console and type “Install-Package RIAServices.EntityFramework”.

This should automatically install the EntityFramework 4.1 for you, if you don’t have it already installed. If you already have it installed, add a reference to EntityFramework.dll to the project. You will also need to add a reference to System.Data.Entity.dll.

3. Add an EntityFramework Model using the Code-First approach to the web project.

So let us define an entity called Product, with some properties on it. We will also define a  DbContext called ProductDbContext that has a DbSet of Products on it. (You can find more information on how to use EF Code-First here). So the code looks like this –

namespace EFCFSample.Web
{
    public class Product {
        public int ProductID { get; set; }
        public string ProductName { get; set; }
        public int? CategoryID { get; set; }
        public Decimal? UnitPrice { get; set; }
        public bool Discontinued { get; set; }
    }

    public class ProductDbContext : DbContext {
        public ProductDbContext()
        {
            if (HttpContext.Current == null)
            {
                Database.SetInitializer<ProductDbContext>(null);
            }
        }

        public DbSet<Product> Products { get; set; }
    }
}

Notice the code I have in the ctor of the ProductDbContext? That piece of code checks if the HttpContext is null and if it is, sets the database initializer to null. Here is why we need it.

Additional Code Changes Needed to Work with the Preview –

EntityFramework initializes the database when you create a DbContext. instance. You want that to happen at runtime but not at design time. WCF RIA Services will initialize the DbContext at design time as well (only for EF Code-First). So we need to prevent the database from being initialized at design time. The logic to to do this is embedded in the product that will be released with the RIA Services V1.0 SP2.

So, until then, you will have to manually prevent the database from being initialized at design time. To do that, we identify if the execution context is design time context (by checking if the HttpContext.Current is null) and if it is, we set the database initializer to null. Note that this will not be required with the actual release bits of WCF RIA Services V1.0 SP2 and Toolkit.

4. Add a DbDomainService.

Alright, now that the DbContext has been set up, we can go ahead and define a class deriving from DbDomainService<ProductDbContext> (similar to any other RIA Services DomainService) like this –

namespace EFCFSample.Web
{
    [EnableClientAccess]
    public class DomainService1 : DbDomainService<ProductDbContext>
    {
    }
}

5. Add CRUD Methods to the DbDomainService

Now, we will use the C# code snippet to generate the CRUD methods for us. So download the code snippet to the disk. Then use the VS Code Snippet Manager to import it to Visual Studio. For more information on how to do that, refer to this msdn documentation. I would recommend importing it to My Code Snippets (which will be typically under c:\Users\<username>\Documents\Visual Studio 2010\Code Snippets\Visual C#\My Code Snippets)

When that is done, from within the class declaration, use the shortcut “dbcrud” to get your DbDomainService CRUD methods defined for you.

Note that this code snippet has 3 variables that you need to supply –

  1. Name of the Entity (in this case, Product)
  2. Name of the DbSet property corresponding to the entity (Products)
  3. Name of the entity (this is used to generate the names of the CRUD methods – like GetProducts, etc.).

So our DbDomainService looks like this-

namespace EFCFSample.Web
{
    [EnableClientAccess]
    public class DomainService1 : DbDomainService<ProductDbContext>
    {
        public IQueryable<Product> GetProducts()
        {
            return this.DbContext.Products;
        }

        public void InsertProduct(Product entity)
        {
            DbEntityEntry<Product> entityEntry = this.DbContext.Entry(entity);
            if ((entityEntry.State != EntityState.Detached))
            {
                entityEntry.State = EntityState.Added;
            }
            else {
                this.DbContext.Products.Add(entity);
            }
        }

        public void UpdateProduct(Product currentProduct)
        {
            this.DbContext.Products.AttachAsModified(currentProduct, this.ChangeSet.GetOriginal(currentProduct), this.DbContext);
        }

        public void DeleteProduct(Product entity)
        {
            DbEntityEntry<Product> entityEntry = this.DbContext.Entry(entity);
            if ((entityEntry.State != EntityState.Deleted))
            {
                entityEntry.State = EntityState.Deleted;
            }
            else {
                this.DbContext.Products.Attach(entity);
                this.DbContext.Products.Remove(entity);
            }
        }

    }
}

Again, the SP2 release bits will have full wizard support for DbContext. So you should not need to do any of this dance with the code snippet. But till then, this should be easy enough for you to do.

5. Use the DbDomainService in your App

With this, you have a RIA Services DomainService talking to a EF Code-First database that you can use, just like in any other RIA Services application. Of course, you can add/modify the methods in the generated DomainService or add more CRUD methods for other entities in your EF Model.

In short, your EF Code-First model is ready for use in the WCF RIA Services app! 

If you are interested in trying using EF Code-First or simply DbContext with WCF RIA Services, try out this preview and let us know how you find it. Any feedback from you will be highly appreciated!

OperationTag–Its Demise and Workarounds

In the WCF RIA Services V1.0 SP2 Preview (April 2011) release, we introduced a new feature called OperationTag. It allowed you to tag server requests with a Client/Session/Operation Id known as an OperationTag, for client/session/operation tracing/tracking respectively. We have decided to cut this feature from the next release of the V1.0 SP2. I intend to explain the reasons for our decision and other alternatives that you could use instead, in this post.

Original Design and Implementation

As I mentioned before, the OperationTag was intended to be used to tag server requests with an ID for per client/session/operation tracking.  It is strictly metadata and not a part of the Data sent over to the server.

So the options we had to send this additional info were –

  1. As part of the message body

    This would work, technically. However, there were 2 problems with this approach –
    a. The OperationTag is not really part of the “Data” being sent to the server, it is really “metadata”. The data being sent in the body is expected to be consumed on the client. The OperationTag doesn’t quite fit into that category.
    b. And more importantly, currently, the data is sent directly in the body of the message. To be able to send additional metadata in the body, we would have to introduce some sort of enclosing tags to differentiate it form the data. That would be a breaking change.

  2. As a Query parameter on the URL

    Again, this could have worked, technically. But it has problems similar to the one above, viz-
    a. It is semantically incorrect – a query parameter is not the right place to put additional (client tracking) metadata.
    b. And more importantly, this would only work for Queries. For Invoke and Submit Operations, it would not work.

  3. Custom HTTP Header

    HTTP allows adding custom headers that are intended to carry any metadata that needs to be sent across requests and responses. This seemed like the best option to implement the OperationTag. It is correct semantically, it would work for all types of requests (query/invoke/submit) and not break any existing scenarios.
    For these reasons, we decided to go with the HTTP Header approach. That was the implementation we released in the WCF RIA Services V1.0 SP2 Preview (April 2011).

(Note: We also considered some other options, but they were all rejected for various reasons. I will not enumerate all of them, for brevity.)
In the HTTP Headers based implementation, the user could specify the OperationTag on the client which would then be sent to the server on subsequent server calls using a custom HTTP Header. The RIA Services server code would access it from the HTTP header and surface it via the OperationTag property on the DomainServiceContext.

(Note: In this post, the term “OperationTag” is used to denote the Client ID, Session ID or the Operation ID used for tracking purposes).

Demise of the OperationTag

    The HTTP Header based implementation of the OperationTag discussed above, though seemingly straightforward and full-proof, failed in a number of scenarios.

1. Cross-Domain Calls

Custom HTTP Headers are not supported for Cross Domain Calls by the browser. This means you cannot use Cross Domain calls with RIA Services if you use this feature – a restriction we certainly would not want to impose on RIA Services customers.

2. Custom HTTP Headers cause Firefox to crash with Default settings

Firefox crashes if you have custom HTTP Headers in Silverlight and the OOPP (Out of Process Plug-ins) flag turned on (it is on by default). It is a bug external to RIA Services and is being tracked in Bugzilla. The problem goes away if you manually turn the OOPP switch off. But we do want to support all browsers in their default configurations for RIA Services. Not doing that would cause frustration to a large number of RIA Services users out there who may be unaware of this problem.

3. HTTP Headers encoding

HTTP Headers allow only ASCII values. Since the OperationTag is set by the user, it is likely that the user would  use something other then ASCII characters, which would cause the HTTP stack to throw an exception. To get rid of that, we could use some sort of encoding for the OperationTag value. But that would mean 2 things –
– The OperationTag would be less readable “On the wire”.
– Since the Server would always use that encoding to decode the incoming OperationTag value, anyone implementing their custom clients (presumably for the JSON/SOAP endpoints), would have to implement similar encoding schemes for the OperationTag.
Both these might be unnecessary complications for someone intending to send plain ASCII tags.

In short, the current implementation would not work in all scenarios cleanly. Also there didn’t seem to be an alternate solution available right now that would fit all our requirements. Because of these reasons, we decided to pull out the OperationTag feature from the RIA Services codebase.

Workarounds

While we could not find a means to provide a framework level solution for this feature, we have identified some workarounds that many of you who wanted to use this feature, may benefit from. These should be relatively easy to implement in your application. But please be aware of their limitations; these workarounds are provided “as is” with no warranties.

1. Cookies

You can use cookies for client tracking on the server. In this approach, on the first call from the client, the server sets a cookie identifying that particular client on the outgoing response. Thereafter, all the calls from that client will contain that cookie, which the server can use to identify the client.

So on the server, you can use methods like these to set / get the cookie –

private static void SetCookie(string clientID)
{
    HttpContext context = HttpContext.Current;
    if (context != null && context.Response != null)
    {
        if (context.Response.Cookies["ClientIDCookie"] == null)
        {
            context.Response.Cookies.Add(new HttpCookie("ClientIDCookie") { Value = clientID });
        }
    }
}

private static string GetCookie()
{
    HttpContext context = HttpContext.Current;
    string cookieValue = string.Empty;
    if (context != null && context.Request != null)
    {
        HttpCookie clientCookie = context.Request.Cookies["ClientIDCookie"];
        if (clientCookie != null)
        {
            cookieValue = clientCookie.Value;
        }
    }
    return cookieValue;
}

(Note that you can have more sophisticated implementations for cookies. I have used a simple one just as a sample).

Then,  the first time a client connects to the server, you set a cookie on the HttpResponse with a generated ClientID using SetCookie(). After that, all the calls from that client will contain the cookie with the ClientID that you can obtain using GetCookie().

Advantages –

  • The implementation is simple and straightforward and should work in most of the cases.
  • No changes are required on the client.

Disadvantages –

  • Since this approach is implemented by the server, you can only implement Client/Session level tracking with this approach. Operation level tracking is not possible.

2. Implementing the OperationTag via HTTP Headers on top of RIA Services

You can have your own implementation of OperationTag based on HTTP Headers, similar to the one released in WCF RIA Services V1.0 SP2 Preview (April 2011).

To implement this, you need to insert the OperationTag in an HTTP Header on the client. You can do it in a number of ways. One of them is to use a IClientMessageInspector. You would hook up an IClientMessageInspector to the to your DomainContext using a WebHttpBehavior. The inspector would add the OperationTag to a HTTP Header on the outgoing message.

You can set the value of the OperationTag from your main page using a Thread Local Storage property on the MessageInspector.

So on the client, the code would look something like this –

namespace BusinessApplicationNamespace
{
    /// <summary>
    /// Custom client WebHttpBehavior to add our CustomMessageInspector to the runtime.
    /// </summary>
    internal class ClientCustomBehavior : WebHttpBehavior
    {
        public ClientCustomBehavior()
            : base()
        {
        }

        public override void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            base.ApplyClientBehavior(endpoint, clientRuntime);
            clientRuntime.MessageInspectors.Add(new CustomMessageInspector());
        }
    }

    /// <summary>
    /// Custom MessageInspector that will add the OperationTag to the outgoing message.
    /// </summary>
    internal class CustomMessageInspector : IClientMessageInspector
    {
        // Thread specific storage to hold the OperationTag value.
        [ThreadStatic]
        public static string OperationTag { get; set; }

        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
        }

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
        {
            if (OperationTag != null && OperationTag.Length > 0)
            {
                this.AddOperationTagToHttpHeader(ref request);
            }
            return null;
        }

        private void AddOperationTagToHttpHeader(ref Message message)
        {
            // HTTP Header size is typically 4k.
            if (OperationTag.Length > 4096)
            {
                throw new InvalidOperationException("Header too big");
            }

            // First check if the outgoing message has a HttpRequestMessageProperty and use that. Else create a new one.
            HttpRequestMessageProperty httpRequestMessageProperty;
            if (message.Properties.ContainsKey(HttpRequestMessageProperty.Name))
            {
                httpRequestMessageProperty = (HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name];
            }
            else
            {
                httpRequestMessageProperty = new HttpRequestMessageProperty();
                message.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessageProperty);
            }

            // Add the OperationTag to the header with the name "OperationTag".
            httpRequestMessageProperty.Headers["OperationTag"] = OperationTag;
        }
    }
}

In the main code, you can hook up the CustomWebHttpBehavior to the DomainContext by accessing the ChannelFactory on the DomainClient. You also set the OpertionTag value using the public property on the MessageInspector.

public partial class MainPage : UserControl
{
    DomainService1 ds;
    public MainPage()
    {
        InitializeComponent();

        ds = new DomainService1();

        ChannelFactory factory = ((WebDomainClient<BusinessApplicationNamespace.Web.DomainService1.IDomainService1Contract>)ds.DomainClient).ChannelFactory;
        factory.Endpoint.Behaviors.Add(new ClientCustomBehavior());

        CustomMessageInspector.OperationTag = "Client1";
    }
}

On the server, you can check if the incoming message has a HTTP Header containing the OperationTag. If it does, then you access its value to get your client/session/operation ID.

/// <summary>
/// Get the OperationTag from the HTTP Header if there exists one.
/// </summary>
/// <returns>The OperationTag value is there is one, else null.</returns>
private static string GetOperationTag()
{
    string operationTag = null;
    OperationContext operationContext = OperationContext.Current;
    if (operationContext != null)
    {
        HttpRequestMessageProperty httpRequestMessageProperty = (HttpRequestMessageProperty)operationContext.IncomingMessageProperties[HttpRequestMessageProperty.Name];
        if (httpRequestMessageProperty != null)
        {
            operationTag = httpRequestMessageProperty.Headers["OperationTag"];
        }
    }
    return operationTag;
}

Advantages –

  • Unlike the Cookie based approach, you can implement operation level tracking (along with client/session level tracking) with this approach.

Disadvantages  –

  • This approach has all shortcomings noted above, because of which we could not implement it in the WCF RIA Services product itself. However, for those who can live with them (for eg. if you don’t care about cross domain calls or supporting Firefox default config), then this implementation could work very well for you.

3. Tweaking the DomainService

Now if you want to have operation level tracking and also not have to deal with the shortcomings of the HTTP Headers, then you could modify the domain service itself to carry the OperationTag to the client. This needs to be done differently for each type of server call –

a. Query methods – Add an OperationTag parameter. You can then pass the OperationTag as the query parameter and access the parameter on the server.

b. Submit operations – Add a dummy entity type to your solution that contains a dummy property called, say, OperationTag. Then add this entity to the changeset being submitted. Then on the server, you can retrieve the OperationTag by accessing the property on the dummy entity.

c. Invoke Operation – Add an OperationTag parameter, just like the query method.

Advantages –

  • This approach should work in all the scenarios RIA Services currently supports.

Disadvantages –

  • Unlike the other approaches, it does not sit on top of RIA Services – you need to modify your DomainService to implement this.
  • This solution is “not pretty”. Smile

However, as I said before, all these implementations are fairly simple and straightforward to implement. Hopefully, using one of these, it should not be too heavy to implement the client/session/operation tracking on top of your domain service. If you find any approaches better than these, please let me know!

Happy Coding!