Thursday, November 3, 2011

DevForce Code First with AOP

We recently released DevForce 6.1.3 whose signature feature is Code First development of a distributed entity model, implemented with Aspect Oriented Programming (AOP). Let me unpack that for you.

Code First – You write the entity classes and only the entity classes. You don’t use a visual designer. You let the Entity Framework map your classes by convention to database objects and clarify in code those mappings that can’t be inferred.

Distributed Entity Model – “Distributed” is the key word: in a DevForce application, the client may have to rely on an intermediary server to reach the database. In DevForce, you program with a single set of entity classes, the classes you wrote, on both the server and client. DevForce handles the coordination and movement of data across tiers and over the network. The client could be Windows Forms, WPF, or Silverlight. The server could run as a Windows Service, in IIS, or in Azure. The network could be a LAN or the web.

Aspect Oriented Programming – Who needs change notification, property validation, change tracking, lazy loading, client-side caching? You do … if you want to query, save, and bind a WinForms or XAML UI directly to your interrelated collections of entities. That takes infrastructure (INotifyPropertyChanged, INotifyDataErrorInfo, etc) that you should not write yourself. Where is that infrastructure hiding when your source code only says: public string Name {get; set;}?  It’s inside your entity model assembly, after we rewrote it with PostSharp. The infrastructure you need is ready at runtime.

You can read all about DevForce Code First here. There’s a six part walkthrough of a WPF sample here. Right now, I want to talk about why I am personally jazzed about the DevForce Code First approach.

Why Code First?

DevForce has long been friendly to Entity Framework. Heretofore, we integrated closely with the Entity Data Model Designer in Visual Studio and relied upon the resultant EDMX file as the basis for generating entity classes with our own T4 template (which you can customize). That approach is still enormously popular and we remain enthusiastic about it.

But I am smitten by our new Code First option. I like Code First because I think it can reduce the friction of developing and maintaining my entity model. Let me show you a Product class I wrote in Code First:
public class Product
    public int ProductId { get; set; }
    public string ProductName { get; set; }

    public int CategoryId { get; set; } // Foreign key for "Category"
    public Category Category { get; set; }

    public Guid SupplierKey { get; set; } // Foreign key for "Supplier"
    public Supplier Supplier { get; set; }

Product has six properties: the key (ProductId), a name, two foreign keys (CategoryId, SupplierId) and two “navigation properties” that return related parent entities (Category, Supplier). There’s no base class … unless I want one. The only evidence of infrastructure is that peculiar ProvideEntityAspect attribute … to which we will return.

This code is obvious and easy to write. The class conforms to Entity Framework naming conventions so there is no need for explicit mapping with attributes or the fluent API at this point. You’re welcome to add explicit mapping as you choose or need to do so. I didn’t need to … and I prefer to stick with the conventions.

I hammered out this class as it occurred to me. I didn’t fire up the EDM Designer in Visual Studio. I don’t have an EDMX file to manage. No partial classes separate the generated code from my custom entity business logic (of which there is none at the moment but it’s coming). There are no companion metadata classes for manipulating attributes of generated properties. There is no code generation for Silverlight clients as there is in RIA Services. The entity source code you see is the same source code living in all .NET server and client environments.

I can evolve this class as I go … with or without a pre-existing database. I can add validation attributes when and where I want them. Or I can add Product validation rules separately if I don’t want them buried in the entity. I can write DevForce property interceptors to inject behavior when a Product consumer gets or set any of these properties. What begins as little more than a property bag can grow to have as much (or as little) business logic as I think the entity requires … without touching the properties; they remain exactly as you see them.

I can write tests for my added business logic - including tests that exercise the navigations from Product to Category and Supplier – without ever contacting a server or a database.

I’m focused on the Product class and just what it needs to function properly in my business domain. The infrastructure is invisible … although accessible when I want it.

It’s not zero friction. But it’s as frictionless an approach to entity model development as I’ve seen. Which means I should be able to move my development forward faster, with higher quality, and a better chance of conveying my intentions clearly to the developer who picks up after I’ve left the project.

Beyond Entity Framework

At first blush, what I’ve described … the Product class I’ve shown … could come straight out of an Entity Framework tutorial. If you add .NET validation attributes to the properties, Entity Framework will perform object validation for you and reject attempts to save invalid entities. These DevForce entities actually are ready-to-go as Entity Framework entities. What value is DevForce adding that’s not already in Entity Framework?

DevForce brings two huge advantages:
  1. DevForce entities are queried and saved in n-tier deployments … such as Silverlight and cloud applications.
  2. DevForce entities are equipped to participate in Windows Forms, WPF, and Silverlight UIs.
Native Entity Framework is a strictly 2-tier technology. The client must have line-of-sight to the database. You can’t serialize and de-serialize entity graphs across the web. You can’t compose LINQ queries on a remote client. You can’t query asynchronously and manage exceptions remotely. You can’t prepare change-sets remotely and save them remotely. Such capabilities become possible only if you write a ton of difficult infrastructure … or use DevForce.

You are welcome to march into the wilderness on your own. Go write and maintain the myriad services that shuffle data into and out of DTOs (Data Transfer Objects). Try that on today’s line-of-business application with its hundreds of interrelated entities and see how much time you waste on those subterranean layers while the UI languishes and your customer taps his toes.

If you stay 2-tier and you’re pushing entity data into an ASP Web Form, the native Entity Framework entity may be good enough. Your UI controls can translate the validation attributes to JavaScript. You have little use for property change notification. You’re web server has line-of-sight to the database and can run EF on its full .NET stack.

That is not good enough for Windows Forms, or WPF, or Silverlight … the preferred client environments for line of business applications. These client technologies favor bi-directional data binding. Their controls listen for the PropertyChanged event to fire when something sets the property of a bound entity object. Many grid controls respond to hints they discover in attributes decorating the entity properties. Many controls can cancel and roll back user changes automatically if the entity supports IEditableObject. They can light up with validation error information when the entity supports IDataErrorInfo or INotifyDataErrorInfo.

Entity Validation in WPF

In this WPF sample screen shot, the “Supplier” text box is bound to the Product.Supplier.CompanyName property. DevForce navigates from the Product to the Supplier instance in cache. The user entered a value in mixed case (“All Fine Foods has a name that is far too long”). A “get-interceptor” converts the value to uppercase before returning it to the text box. The property validation mechanism immediately applies the property length validation rule and records the rule violation. The binding, which detects that Product implements IDataErrorInfo, displays the validation message.

Developers want their entities to provide this kind of support automatically. They don’t want to write it. They don’t want to see it. Developers are really tired of writing … or wading through … pages of property definitions such as

public string ProductName
    get { return _productName; } 
        if (_productName != value)
            if (Validate(value))
                _productName = value;
private string _productName;

That’s all noise and no business value. It should be quieter and simpler:
public string ProductName { get; set; }

Let’s push it a bit farther and think about what must be going on inside a property that returns a related entity. Here is how you write the Product’s Category property in DevForce Code First:

public Category Category { get; set; }

It is as plain, expressive, and simple as ProductName. We have the same need for validation and notification. We have two additional requirements, neither of them met by Entity Framework: (a) we must have the machinery for retrieving, caching and saving the related Category entity on a remote client and (b) we must plug that machinery into the property’s get and set methods at runtime. 

The apparent innocence of the ProductName and Category properties is possible thanks to some nifty work behind the curtain.

Aspect Oriented Programming (AOP)

DevForce makes writing properties as simple as shown while endowing them with the capabilities necessary for participation in the .NET client UI technologies. Clearly the “property as shown” cannot be the actual implementation at runtime. Somehow the infrastructure code has to insinuate itself into the property.

DevForce uses PostSharp and Aspect Oriented Programming (AOP) to inject the infrastructure into your classes. It re-writes the compiled model assembly at build time. That ProvideEntityAspect attribute adorning the top of the Product class tells DevForce that Product is a kind of entity and should implement the behaviors that all entities exhibit. DevForce and PostSharp replace the get and set methods of entity properties with code that ties into DevForce infrastructure. The revised Product class inherits from and implements the interfaces that light up the UI controls. It also acquires support for querying, caching, change-tracking, and saving product data. The Product class, after re-write, is an enriched version of the original Product class you wrote. The Product objects that you “new” at runtime are instances of the enriched Product class. When a binding reflects into an instance of Product, it finds the interfaces it’s looking for.

Many frameworks, including Entity Framework and NHibernate, use an alternative approach called “dynamic proxies.” They leave the original compiled classes alone. Instead, they rely on an external component (e.g., a “Context” or “Session”) to encase new and queried entities in a wrapper class that derives from your entity class. This wrapper overrides your entity’s properties with implementations that delegate (“proxy”) to the framework’s infrastructure.

That infrastructure, if expanded appropriately, could do all of the interception, routing, and notification that DevForce does. You’d have to adapt your entity and workflow to play along. All of your entity properties would have to be virtual. You couldn’t construct them directly with “new”; in fact, you’d have to be careful always to reference the proxied object. You’d also have to get used to debugging with strange wrapper types, sporting bizarre long names.

We like the AOP approach better. We think most people will find it more natural to work with a class that is fully baked at build time rather than maneuver to consume proxies that are emitted dynamically at runtime.

Could this be magic?

I don’t trust all that magic!'” That’s a common early reaction to Code First, AOP (and dynamic proxies). When I rely on Entity Framework naming conventions to map class and property names to tables and columns, how do I know what it did or did not map? When AOP is injecting logic into my classes that I can’t see, how do I know what it is doing?

I push the button on my coffee maker and good, hot brown stuff pours into my cup. That’s magic to me. The compiler turns my auto-property into two get and set methods with a backing field. That’s magic. Then it becomes IL code which becomes machine code which makes the hardware jump. That’s magic.

The difference is that coding by convention and AOP are new magic for most of us. We’re used to the old magic. We probably never truly understood it; we just stopped worrying about it. Before, when the property code was visible and the entity-data map was in an EDMX file, we felt the visceral comfort of touching the code and displaying the XML if we wanted to. But we rarely wanted to. When we did look, we were as often confused by what we saw as not.

What matters most is good diagnostics and easy debugging. I need a clear explanation when things go wrong and I need to know what to do about it. Fortunately, the Entity Framework mapping error messages are pretty good. I think the AOP debugging experience will feel familiar to you. You breakpoint your custom code exactly as you did before. You step through your code as you did before. You test your code as you did before.

I am keen to know what you think after you’ve tried it. We’re eager to hear your suggestions. IdeaBlade issues DevForce updates every six-to-eight weeks so we can respond quickly.

What about existing databases?

I often hear that Code First is only for “greenfield” development and small database schemas. Perhaps half of our customers (re)build existing applications for existing databases with hundreds of tables some of which have hundreds of columns. Heaven help them.

Such “brownfield” development presents at least three challenges:
  1. Getting started … because no one wants to write all those entities by hand.
  3. Evolving the entity model and a production database as requirements change.
  5. Keeping the model and the database in sync without the assistance of the EDM Designer.
We meet the first challenge with an approach we call “Code Second”. Speaking schematically, you aim the EDM Designer at the database and generate DevForce “Code First” classes. Then you throw away the EDMX file and proceed in Code First style.

I’m optimistic but less confident in my response to the second and third challenges. I know developers in other communities (NHibernate in particular) have confronted these demons and wrestled them to the ground. They’ve survived without Database First tooling as far as I can tell. I’m betting they’ve discovered successful practices we can learn from and adapt.

Code First style entails evolving the model and existing database in small doses, supported by tests. Runtime mapping exceptions are sufficiently informative in the context of small changes; repairing small breaks should be easy. You shouldn’t wake up in shocked surprise one morning to find that the model or the database have been massively transformed. That’s a disaster for Database First as much as Code First; it indicates far more fundamental flaws in your development process.

A new module may require many new entity types to support its scenarios. The new entity types can be related to other types, new and existing. The new model ultimately translates to new tables and foreign key relationships to new and existing tables. I think I would develop a new module as I would a “greenfield” project with stubbed stand-ins for the existing entities and tables.

It may be wise to preserve this separate model and separate database … forever. But if that’s not in the cards, when the new project matures, I’d fold my Code First model into the existing model. I’d script database changes from the generated Code First design database and apply them to the existing database.

I readily admit that I do not have the concrete experience to back up these suggestions. I expect to have more to say on this subject in future posts. One of our customers hired us to help them rebuild their 500 table application in DevForce Code First. I promise to report back on do’s and don’ts.

What next?

I’d love to know what you think. Is Code First attractive to you? Do you have pros and cons to share? Would you explore our walkthrough and give me your feedback? Is there something that bothers you about what I’ve said? Anything you want me to expand upon?

I’m here for you.


Anonymous said...

Do I need a PostSharp licence (or can I even do it at all), if I want to use, say, Rob Eisenberg's version of NotifyPropertyChanged instead of .NETs in my entity object's setters "now hidden goop"? Do you have a CM/AF specific version avail? I love the idea if I can fully customize it. If I can not, then I do not.

Ward Bell said...

PostSharp license? Great question. And I have a great answer - we took care of that for you. We licensed it for our product; you get an anonymous license that grants you the right to trigger PostSharp to do it's thing under our control.

If you want to add your own AOP, that's a different story; you'll need your own PostSharp license. But you don't need one to benefit from what we are providing via PostSharp.

I didn't understand your comment about what you want from Rob's NotifyPropertyChanged. The UI thread marshalling perhaps?

I haven't needed that but let's suppose you did. You can replace our implementation of the UI interfaces that we support, including INPC, as explained here. That gives you the freedom to wire in his INPCEx if you like.

Let me know if you're still wondering or if you can refine your question ... perhaps with an example.

Thx, W

Anonymous said...

Thanks Ward - the link you provided cleared up my confusion - I am still investigating the Code First approach. Appreciate your commentary.

Unknown said...

I want to know what are the differences between DevForce and DataObjects .net that also implements functionalities of PostSharp.

Thanks in advance.

Ward Bell said...

Francesco - I don't know If you have specific questions about specific features I can answer those.