Sunday, December 28, 2014

Using Web API 2 with Entity Framework 6

I feel that the most difficult part of starting a new project is deciding what technologies to use.  Over the last several months I've been working with Web API 2 & EF 6 to create a REST service for use with a WPF application that I've been writing.  Here I'd like to detail the reasons I chose this technology combination and some of the lessons I've learned along the way.
  • The testing story is improved with EF6 DbSet<T>
  • Had used EF4 & EF5 on other projects
  • Web API has OWIN (Open Web Interface for .NET) allowing it to run locally without a web server
  • Web API is fairly straightforward in concept being based on the HTTP verbs GET,POST,PUT,DELETE.
  • Both Microsoft Technologies with lots of information floating around the web
Looking back of the last few months, I've had very little trouble with Web API 2. It has been pleasant to work with.  The majority of my issues have been with Entity Framework.  This may be due to how I chose to use the technology though I haven't done anything that radical.
The basic structure of my project followed the aggregate root concept.  Our application is basically a giant calculator, taking several input parameters and returning a calculation.  For example, when a project entity is loaded, I want to load all of its child entities otherwise I don't have the data needed for a calculation.
(AR) Fluid - Polymorphic type
        -int Id
        -string Name
        -List<FluidPointBase> FluidPoints - Polymorphic list of items
(AR) Material
        -int Id
        -string Name
        -double Density
(AR) Project
        -int Id
        -Fluid Fluid
             -int Id
             -string Name
             -List<FLuidPointBase> FluidPoints
         -Material Material
             -int Id
             -string Name
             -double Density
Testing Controllers
There is a heated debate on StackOverflow and elsewhere regarding the question of whether one should use the repository pattern or forgo it in favor of mocking the methods on the underlying ORM.  I was swayed by the argument that in EF it is possible to create testable code without a repository.  Following the patterns described below I was able to set up tests for my controllers.

  • Entity Framework 6 now has a very easy to mock implementation of DbSet<>, this technically makes it possible to test against EF, without the Repository or UOW patterns. These patterns take time to code, and maintain, and add another layer to the application.
  • Response in Web API 2 should be of type IHttpActionResult this will allow for easier testing.
  • Fiddler - HTTP debugger
Web API Considerations
I soon found that there are many things to consider when building a Web API service. 

  • What are REST best practices?
  • Does it need to be truly RESTful? If so add HATEOS
  • HATEOS (Hypermedia as the Engine of Application State)
  • Provide links when doing operations such as
  • Creating new content
  • Updating content
  • Navigation for paging
  • Can be in the form of url, rel, and method(get,post,put,delete)
  • HAL (Hypertext Application Language) & Collection + JSON are current standards
  • Will the API be public? 
  • Versioning of Web API's is important for consumer compatibility
  • CORS - Cross origin resource sharing 
  • Modern browsers support this, and there is support for it in web api2
  • No need to support JSONP if you don't need it because of CORS
  • How to handle paging?
  • Use a class as a data envelope for paging
  • Include pagination information in the header
  • Optimistic Concurrency in Web API 
  • How to handle polymorphic type serialization with JSON?
  • REST & JSON polymorphic types seem to fly in the face of REST best practices, probably not a good idea to use them on a public API.
  • Requires configuring the JSON Formatter to enable type name handling in the Web API configuration.
  • config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All;
Entity Framework Pain Points
Most of my gripes are with EF.  As I've worked on this project, EF has been an unending source of issues.  

  • No eager loading of polymorphic types 
  • This means you either have to explicitly load each derived type, or rely on lazy loading.
  • Table per Type (TPT) inheritance breaks cascade delete
  • Makes you feel crazy when the fluent API is configured correctly and your keep getting reference constraint errors!
  • Workaround by executing Sql in seed method
  • At first I manually wrote the logic to merge the graph, I got depressed writing books of code and thankfully found GraphDiff.
Without GraphDiff the update logic for one related graph in an entity would look something like this...

                // Update scalar/complex properties of parent
               // Updated related geometry items
                var geometryItemsInDb = currentWell.Geometries.ToList();
                foreach ( var geometryInDb in geometryItemsInDb)
                    // Is the geometry item still there?
                    var geometry = entity.Geometries.SingleOrDefault(i => i.Id == geometryInDb.Id);
                    if (geometry != null)
                        // Yes: Update scalar/complex properties of child
                        // No: Delete it
                foreach ( var geometry in entity.Geometries)
                    // Is the child NOT in DB?
                    if (geometryItemsInDb.All(i => i.Id != geometry.Id))
                        // Yes: Add it as a new child
With GraphDiff updating an entire graph looks like this....
//Note: GraphDiffUpdateGraph is a virtual method for mocking...  Actual method with GraphDiff is UpdateGraph()
TfContext.GraphDiffUpdateGraph(entity, f => f.OwnedCollection(p => p.Geometries)
                                                         .OwnedCollection(p => p.SurveyPoints)
                                                         .OwnedCollection(p => p.Temperatures));
Decisions for my API

  • Choose to include HATEOS in the API - though in retrospect this may have been a YAGNI violation. 
  • Used Polymorphic types, this increases the size of the JSON payload and is likely not a REST best practice
  • Used lazy loading in EF to avoid explicitly loading derived types
  • Wrapped DbContext methods such as Entry() and UpdateGraph() to make them mockable
  • Used an object for paging instead of passing the page information in the header

There are several things that are still unresolved in my mind.  RESTful API's by their very nature seem to be in conflict with the Aggregate Root concept.
Do I have an API where I hit the endpoint Project/1 and get back my Project object with all its data or do I hit get back a Project, with a link to a Fluid entity and a link to a material entity?  What about polymorphism?  If I get rid of my polymorphism in the entities, doesn't that increase the number of controllers I'll need to serve the API?
Obviously there is still much to be learned...

No comments:

Post a Comment