Living in the Tech Avalanche Generation

A practitioner’s introspective on technology
Archive for March 13th, 2009

Entity Framework and LINQ To SQL Entities are not Messages!

I’m sorry but I just don’t understand why the idea of serializing domain entities for transmission over the wire is that important. Should we be using the ‘data’ present in entities as messages, regardless of what technology stack and transport protocols are involved, ASMX, WCF, HTTP, TCP etc. Domain entities should live up to their title and be exactly what their name implies and exhibit behaviour, which is something that falls outside the purview of messages. This practice of transporting anemic entities has been fueled by  a plethora of demoware that begat production software, where the data representations of business objects became separated from their behaviours and where behavior got down grounded to lowly ‘Manager’ objects that did their work behind remote endpoints or in some other part of a layered Client Server architecture. We .NET folks can somewhat thank our J2EE brethren in part for popularizing this approach. For example in WCF we often see this kind of RPC Cruddy kind of methods:
 
[OperationContract()]
public Customer UpdateCustomer(Customer _customer)
{
    //update the customer here in the database
    //…………………..
    return _customer;
}
 

What is it about the customer that should be interesting? Some event regarding a customer surely took place? Perhaps that event indicates to us that we should consider a more descriptive name for the message and a less generic data structure as the message? Surely to update a customer we are necessarily updating everything about that customer? Perhaps UpdatingCustomerPersonalDetails would be more appropriate? If I was wearing my publish and subscribe hat, I might think that this method and service are not alone in having an interest in an UpdatingCustomerPersonalDetails message. Perhaps some other business service is interested in specific changes to the state of a customers details? There has been a bit of talk around of late regarding the role of DTO’s and how often and where data representations should or shouldn’t be duplicated. I personally have been an practicing MVC, MVP kinda guy for quite a long time now and this issue always seemed to get a bit of airplay when discussing ‘what goes in the Model‘ and ‘should Models be shared‘. People would ask, why support data structures of two types at different points in my architecture? Simple answer is dependency / coupling. I have yet to come across an instance that supports the notion that my UI is interested in displaying all my Customer data (customer as an example entity). If my Customer data structures change everyone changes with them, regardless of whether my UI or my business layer is concerned with the change. And, this all naturally leads to the question, is the RPC styled UpdateCustomer() method (above) a demonstration in tight coupling? I would say yes it is a fragile dependency and any change to this shared anaemic artifact will ripple through an architecture. If however we consider for a moment that our services should ‘handle‘ messages where they have a registered or known explicit handler for any given messages, then we can quickly dispense with the whole shared anaemic model. In a previous post I highlighted (in somewhat more detail) this approach below.

public class TestHandler : IMessageHandler<TestMessage>
{
    public void HandleMessage(TestMessage message)
    {
        Console.WriteLine(string.Format(“The Test Message name was “ +
            “{0} and it’s ID is {1}”,
            message.NameOfMessage, message.ID));
    }
}

 

Where do my ORM’s come into this?

So when it comes to the common usage or ‘classic‘ approach with ASMX / WCF Service layers, that follow the anaemic anti pattern as described nicely in our UpdateCustomer() example, you may very well encounter a domain entity defined as either an Entity Framework or LINQ To SQL entity. If you have seen the code generated by Entities in both those technologies, you will have noticed that such entities are decorated with appropriate attributes aimed at supporting serialization over the wire (further encouragement to tread that path). Let’s take our customer as an example in both LINQ To SQL and Entity Framework:

LINQ To SQL

[Table(Name="dbo.Customers")]
[DataContract()]
public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged
{
    private string _CustomerID;   

    [Column(Storage="_CustomerID", DbType="NChar(5) NOT NULL",
        CanBeNull=false, IsPrimaryKey=true)]
    [DataMember(Order=1)]
    public string CustomerID
    {
        get
        {
            return this._CustomerID;
        }
        set
        {
            if ((this._CustomerID != value))
            {
                this.OnCustomerIDChanging(value);
                this.SendPropertyChanging();
                this._CustomerID = value;
                this.SendPropertyChanged(“CustomerID”);
                this.OnCustomerIDChanged();
            }
        }
    }
}

Turning on the Serialization Mode to Unidirectional for the LINQ To SQL DataContext and it will add decorate with a DataContract attribute for Entities and the DataMember attribute for their Properties, so we can easily transport these entities over wire with WCF and re-serialize them at the receiving end.

l2s_context_wcf

Entity Framework

[global::System.Data.Objects.DataClasses.
     EdmEntityTypeAttribute(NamespaceName="NorthwindModel", Name="Customer")]
[global::System.Runtime.Serialization.DataContractAttribute(IsReference=true)]
[global::System.Serializable()]
public partial class Customer :
    global::System.Data.Objects.DataClasses.EntityObject
{
    /// <summary>
    /// Create a new Customer object.
    /// </summary>
    /// <param name=”customerID”>Initial value of CustomerID.</param>
    /// <param name=”companyName”>Initial value of CompanyName.</param>
    public static Customer CreateCustomer(string customerID, string companyName)
    {
        Customer customer = new Customer();
        customer.CustomerID = customerID;
        customer.CompanyName = companyName;
        return customer;
    }
    //…..ETC ETC
}

You can see here too that the Entity Framework has setup Customer as a DataContract (ready for WCF wire serialization) and it’s Orders collection shown below is decorated as a DataMember.

[global::System.Data.Objects.DataClasses.
    EdmRelationshipNavigationPropertyAttribute
    ("NorthwindModel", "FK_Orders_Customers", "Orders")]
[global::System.Xml.Serialization.XmlIgnoreAttribute()]
[global::System.Xml.Serialization.SoapIgnoreAttribute()]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public global::System.Data.Objects.DataClasses.EntityCollection<Order> Orders
{
    get
    {
        return
            ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)
            (this)).RelationshipManager.GetRelatedCollection<Order>
            (“NorthwindModel.FK_Orders_Customers”, “Orders”);
    }
    set
    {
        if ((value != null))
        {
            ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)
                (this)).RelationshipManager.InitializeRelatedCollection<Order>
                (“NorthwindModel.FK_Orders_Customers”, “Orders”, value);
        }
    }
}

Both LINQ To SQL and Entity Framework take this approach expressly to enable us in serializing the data representations of entities across the wire for distributed computing. That’s great if you need to return your entities from your web services and all this makes that simple, but not necessarily good practice nor a coupling price I am prepared to pay.

I will confess that I have been guilty of using this pattern in the past, but for now entities are entities, having taken back the behaviors that rightfully belonged to them before being hijacked.

There seems also to have to been some discussion (worth checking out) on this topic and around DTO’s recently in Seattle.

Share/Save/Bookmark

No comments

Creative Commons Attribution-ShareAlike 2.5 Australia
Creative Commons Attribution-ShareAlike 2.5 Australia