- Data Services on the Server
- Data Services on the Client
- On Beyond Query: Create, Update and Delete
- Data Services + Entity Framework = Joy
- WPF Data Binding
- Paging
- Interceptors
- Authorization
- Functions
- JSON
- Debugging
- Where Are We?
Data Services + Entity Framework = Joy
Out of the box, our container class passed to the DataService base class is enough to provide read-only access to our data, but not write access. To support writes, the DataService class requires several things, which are embodied in the IUpdatable interface:
namespace System.Data.Services { public interface IUpdatable { void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded); void ClearChanges(); object CreateResource(string containerName, string fullTypeName); void DeleteResource(object targetResource); object GetResource(IQueryable query, string fullTypeName); object GetValue(object targetResource, string propertyName); void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved); object ResetResource(object resource); object ResolveResource(object resource); void SaveChanges(); void SetReference(object targetResource, string propertyName, object propertyValue); void SetValue(object targetResource, string propertyName, object propertyValue); } }
To support writing as well as reading, you have three choices: The first is that you can implement IUpdatable based on the semantics of your container class, i.e. where you store your data, who generates IDs and how, how concurrency is managed, etc.[14] The DataServices class will use your IUpdatable implementation along with CLR Reflection to expose your data (this combination is called the “Data Services Reflection Service Provider” for that reason).
The Reflection Provider is an excellent choice for simple data sources. However, if you don’t know the shape of your data at compile-time, you’ll want to implement your own custom service provider. That’s a big job and beyond the scope of this book[15].
Now, the good news is that you don’t have to implement your own custom service provider over one popular data source – SharePoint – because that one’s already handled by the nice people on the SharePoint team[16]. Similarly, if your data is stored in a relational database with an EF provider, you’re similarly covered. Like the Reflection Provider, EF is supported by Data Services out of the box. If your data lives in a relational database, than your third choice should be your first choice: your container should be implemented using the Entity Framework.
If you take a second look at the IUpdatable interface, you’ll notice that it has a lot in common with the kinds of things that the EF context object knows how to do, i.e. expose collections, create, read, update and delete from those collections and flush changes made in memory back to the store. This makes Data Services an excellent mate for EF: EF provides the conceptual model, the storage implementation and the mapping between them while Data Services provides the way to expose an EF context using OData as illustrated in Figure 10. In fact, Data Services was initially developed to only work with EF (and custom data providers); IUpdatable was added later.
Figure 10: The relationship of Entity Framework to Data Services
EF is first among equals when it comes to Data Services, so let’s see how we can make that work. Using the techniques of our EF chapters, I took the liberty of pushing our data model into an Entity Data Model called TinyLink.edmx and creating the corresponding database for it (Figure 11).
Figure 11: The TinyLink data model imported into EDM
With the model from Figure 11 in place, we can drop the original .NET object model, since EF will use our EDM to generate that code along with the database access layer so that we can support creates, updates and deletes as well as queries. With the EDM in place and the EF code generated, our Data Services endpoint code remains exactly the same:
using System; using System.Data.Services; using System.Data.Services.Common; using TinyLinkAdmin.Models; // Now generated from our EDM using System.ServiceModel; namespace TinyLinkAdmin { public class TinyLinkService : DataService<TinyLinkContainer> { public static void InitializeService(DataServiceConfiguration config) { config.SetEntitySetAccessRule("TinyLinks", EntitySetRights.All); config.SetEntitySetAccessRule("TinyUsers", EntitySetRights.All); config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2; } } }
Our Data Services endpoint works just the same as it used to except now it knows exactly how to map create, update and delete calls onto the underlying EF context class in the same way that it would if we’d have implemented IUpdatable[17]. Recall our program demonstrating CRUD via the Data Services proxy class we generated in the previous section[18]:
// Create a new user and link var bill1 = new TinyUser() { Name = "Bill" }; service.AddToTinyUsers(bill1); var link1 = new TinyLink() { IsActive = true, Url = "http://sempf.net", User = bill1 }; bill1.Links.Add(link1); service.AddToTinyLinks(link1); // Link the TinyLink and TinyUser together service.AddLink(bill1, "Links", link1); // Flush the changes service.SaveChanges(SaveChangesOptions.Batch); var billId = bill1.Id; var linkId = link1.Id; // Find and update a link var link2 = service.TinyLinks.Where(l => l.Id == linkId).First(); link2.IsActive = false; service.SaveChanges(SaveChangesOptions.Batch); // Check the links var links = from link in service.TinyLinks.Expand("User") where link.IsActive orderby link.Url descending select link; foreach (var link in links) { Console.WriteLine("Url= {0}, IsActive= {1}, User.Name= {2}", link.Url, link.IsActive, link.User == null ? "<none>" : link.User.Name); } // Find and delete the link and the user var link3 = service.TinyLinks.Where(l => l.Id == linkId).First(); var bill2 = service.TinyUsers.Where(u => u.Id == billId).First(); service.DeleteObject(link3); service.DeleteObject(bill2); service.SaveChanges(SaveChangesOptions.Batch);
Now that our server supports writes via the EF Provider, our writes work just as well as our reads and the output is now what we’d like it to be (Figure 12).
Figure 12: Reading and writing to an OData service endpoint via the WCF Data Services client
Of course, a console application isn’t quite the be-all, end-all. What if my co-authors want to write something fancy to manage their tiny links? For that, we want Data Services to support data binding.
Footnotes
[14] You can see how to implement IUpdatable in “How to: Create a Data Service Using a LINQ to SQL Data Source (WCF Data Services)” at http://msdn.microsoft.com/en-us/library/ee373841.aspx (http://tinysells.com/144).
[15] For more information, see “Custom Data Services Providers,” Alex James, Jan, 2010 – Mar, 2010, http://blogs.msdn.com/b/alexj/archive/2010/01/07/data-service-providers-getting-started.aspx (http://tinysells.com/145).
[16] You can find articles about SharePoint and OData here: http://www.odata.org/developers/articles (http://tinysells.com/146).
[17] Although, ironically, the EF ObjectContext class doesn’t actually implement IUpdatable for historical reasons.
[18] If you’re following along and updating the client that used to work against the Reflection Provider-based code, you’ll want to right-click on the service reference, e.g. TinyLinkService, in the Solution Explorer and choose Update Service Reference. The EDM model is slightly different and that difference causes a minor change in the metadata, requiring an updated proxy to be generated.