Initial POCO Design 1-Pager

Published 24 June 08 12:20 PM | dpblogs 

Here is a raw cut and paste for our POCO 1-Pager. We are currently working through the design and have some prototype work going on and we would like to hear your feedback. Note this is the "feature design 1-Pager" not the "implementation design 1-Pager" we are still looking at the details for the design and at this point would love to hear feedback on the experience plus thought around state mgmt in general. We will post additional design notes for this feature after the upcoming team design meeting.

1. Overview

Persistence Ignorance refers to being able to allow the developer to write and test domain objects in a way that is entirely independent of fundamental requirements and assumptions that may be made by the infrastructure service (in this case, the Entity Framework). Such requirements / assumptions may often include:

  • The need to implement a specific interface (for e.g., IPOCO)
  • Inheritance from a base class
  • Providing specific constructors
  • Object Instantiation/Construction requirements – use a specific factory for instance
  • The need for metadata or mapping class or property Attributes
  • The need to use specific relationship mechanisms

This amounts to being able to use Plain Old CLR Objects (POCO) so that a developer can author their domain objects free of all assumptions and requirements imposed by the framework. Using this approach, once the domain objects are ready to their satisfaction, the developer can use these classes with the Entity Framework in order for relational database access and persistence.

2. Context

While the customization of entity types has always been a goal with Entity Framework, v1 imposed several restrictions/requirements in terms of how the entity types had to be built – for instance, at the minimum, a few interfaces had to be implemented at a minimum:

  • IEntityWithRelationships
  • IEntityWithChangeTracking

Pure POCO should eliminate the need to implement these interfaces.

In addition, POCO shouldn’t require the use of attributes that are required to map object members to the C-space.

3. Design

When the Entity Framework supports Persistence Ignorance in the form of pure POCO, entity classes that are free of any infrastructure related artifacts can be authored as shown. The following example shows a Customer entity and an Order entity. There is a relationship between Customer and Order – i.e. a Customer can have many Orders.

O-Space to C-Space mapping is done by convention – i.e. the CLR types of the entities below map to the corresponding entities already defined in the Conceptual Model.

    public class Customer
    {
        string _CustomerID;
        string _ContactName;
        string _City;
        List<Order> _Orders;

        public string CustomerID
        {
            get { return _CustomerID; }
            set { _CustomerID = value; }
        }

        public string ContactName
        {
            get { return _ContactName; }
            set { _ContactName = value; }
        }

        public string City
        {
            get { return _City; }
            set { _City = value; }
        }

        public List<Order> Orders
        {
            get { return _Orders; }
            set { _Orders = value; }
        }
    }
    public class Order
    {
        int _OrderID;
        Customer _Customer;
        DateTime _OrderDate;

        public int OrderID
        {
            get { return _OrderID; }
            set { _OrderID = value; }
        }

        public Customer CustomerID
        {
            get { return _CustomerID; }
            set { _CustomerID = value; }
        }

        public DateTime OrderDate
        {
            get { return _OrderDate; }
            set { _OrderDate = value; }
        }
    }

Once the POCO classes are in place, one can program against them as first class entity objects.

For instance, query can be used to materialize POCO objects as shown:

        var query = from c in db.Customers.Include("Orders")
                    where c.City == "London"
                    select c;

ObjectContext can be used for CUD operations much like the usage patterns supported today:

        Customer customer = new Customer();
        customer.City = "London";
        customer.ContactName = "Steve";
        customer.CompanyName = "Acme";

        Order order = new Order();
        order.OrderDate = DateTime.Today;

        customer.Orders.Add(order);

        db.AddObject("Customers", customer);
        db.SaveChanges();

Tim Mallalieu
Program Manager,
Entity Framework Team

This post is part of the transparent design exercise in the Entity Framework Team. To understand how it works and how your feedback will be used please look at this post.

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Tim Mallalieu's Blog. said on June 24, 2008 3:24 PM:

We just pushed the first piece of content to the EF Design Blog . This one is a &quot;Feature Design&quot;

# rogerj said on June 24, 2008 7:54 PM:

Hi, Tim,

"Note this is the "feature1-Pager" not the "design 1-Pager" but it's title is "Initial POCO Design 1-Pager"

# Nullable said on June 24, 2008 9:13 PM:

POCO is easy to understand with base CLR objects, like string FirstName, int Age and DateTime DateOfBirth... but what's your thought behind complex types (more specifically, List<Order> Orders).

Do you plan using the constructor of the generic list to load the enumeration of "Order" objects?

The same question goes for the ObjectContext with tracking "dirty" objects... it's easy with value types, but again, what are your thoughts / plans for custom types?

Thanks,

-Timothy Khouri

# GregYoung said on June 25, 2008 12:44 AM:

I will bring up a few points where I see difficulty...

1) Lazy Loading needing a Repository reference (PI) but I reckon you already know this

2) As Nullable brought up Value Objects in the DDD sense especially considering they are generally assumed to be immutable

3) The use of strings ... What happens when an object gets renamed? I would much prefer to see strongly typed prefetch paths

# colinjack said on June 25, 2008 6:41 AM:

All the issues Greg has raised are important and I wanted to add one.

As far as possible we should avoid having to add associations in the domain simply to allow EF to behave correctly. So if I wanted Order to know about Customer but not the reverse then this should be supported.

# timmall said on June 25, 2008 8:51 AM:

>>> On Associations

Yep - agreed, you should not need to add associations. We are working on this and will share our thoughts on the blog

>>> On Greg's points

Yep - we are chatting with Greg in email. We expect to be able to support members that are not just scalars so Value Objects (our Complex Types) are expected. It is doubtful that in V2 we will be able to fit support for collections of complex types though. So a reference to a value object would exist a collection of value objects likely wont.

Tim M

# colinjack said on June 25, 2008 1:29 PM:

@timmall

My view is whether you support, in V2, something like a collection of value objects is not important. Longer term supporting all the complex mappings that NHibernate and the other mature ORMSs have will be important, but for now I think it should be all about creating a product that encourages good design/testing.

# JimmyBogard said on June 25, 2008 2:37 PM:

Maybe others have touched on this...

You should think about changing the name of the CustomerID property on Order, it's throwing me off, as it's of type Customer.

Typically, I wouldn't expose Customer.Orders as List<T>.  I would Encapsulate Collection, and only expose operations I support, like Customer.AddOrder.  I shouldn't have to have public getter/setter pairs for it to work.

At the least, I'll only expose a getter.

# jeffders said on June 25, 2008 9:10 PM:

In regards to materializatoin of relationships...

POCO collections are a particularly interesting issue. Ideally we'd like to support many types of collections such as IList<T>, ICollection<T>, List<T>, etc. In these cases, we wouldn't know what the actual concrete collection type is so we'd need to materialize a List<T> and populate that. We could also materialize a type of wrapped collection (like an EntityCollection<T>) that would provide better hooks into relationship change tracking.

Another option is to be able to map methods that help access collections. It would be the entity's responsiblity to create the underlying collection, and the materializer would call the mapped methods (such as "AddToOrders()") rather than requiring the entity to expose the collection as a read-write property.

Jeff Derstadt

Developer

Entity Framework Team

# jeffders said on June 25, 2008 9:15 PM:

>> with tracking "dirty" objects... it's easy with value types, but again, what are your thoughts / plans for custom types?

We are working on different strategies to help tracking changes in custom types, complex types, and relationships. You are right that tracking scalar (value) propeties is easier because you can just call Equals, but it gets harder, especially with relationships. One idea we had was to keep a snapshot of relationships in the context prior to a bunch of changes, and then when SaveChanges is called on the ObjectContext we'd do a graph diff to try to sort out what relationships have been added or removed. This has the downside of requiring a larger resource footprint to store the entire original state of relationships, but it would completely remove any change tracking requirement from the entities.

An alternative is to call a specific API to tell the context when things have changed. This could be done outside of the entity. We are also experimenting with events, but we havent' quite found a common event that works in enough cases.

Jeff Derstadt

Developer

Entity Framework Team

# Nullable said on June 25, 2008 9:49 PM:

Hey Jeff, thanks for the reply on the change tracking. I completely feel your pain when you mentioned doing "a graph diff to try to sort out what relationships have been added or removed"... right away I cringed at the thought of the footprint, but as you mentioned, this would be a complete solution.

One thing I do know is that I hate the idea of leaving it up to the developer, and I hate even more *assuming* a dirty state. ASP.NET Profiles does this if you make your own complex type for a property - this caused me a lot of pain (that I don't feel like explaining in this tiny box).

Dare I ask this question... but is providing a transparent proxy to the POCO object out of the question? That way you can wrap what is needed? (I feel like just by asking this question someone is going to jump out from behind a tree and smite me).

Thanks,

-Timothy Khouri

# unbornchikken said on June 26, 2008 1:55 AM:

"This has the downside of requiring a larger resource footprint to store the entire original state of relationships"

My opinion is that RAM is far more cheaper than our time. And I dont think there will be any ObjectContext ever which is holding thousands and thousands of objects at a time. ;)

# serega said on June 26, 2008 4:14 AM:

Why do you need to specify the C-space name "Customers" in this code:

db.AddObject("Customers", customer);

Is it possible to map one O-entity to many C-entities in the scope of a single context? What for?

Usually it is the other way around, in which case the C-type is overhead.

# colinjack said on June 26, 2008 7:06 AM:

> POCO collections are a particularly interesting issue.

> Ideally we'd like to support many types of collections

> such as IList<T>, ICollection<T>, List<T>

This would be useful, especially supporting List<T>. Even better if we could use our own collections (for example to add methods/interfaces that we find useful for a particular project).

> rather than requiring the entity to expose the

> collection as a read-write property.

I take it you wouldn't really need to expose the collection, you could write to the field I'd assume?

> dirty checking

Have you discussed this with people on the NHibernate (and other ORM) team because they already have  good solutions to these issues.

# jeffders said on June 26, 2008 11:25 AM:

Timothy,

Returning proxy entities is not out of the question, but it isn't something we have completely explored. There are defintely advantages, as we could provide several services that would be hidden from the basic entity. How has the experience been with returning proxies for entities and serialization?

Jeff

# jeffders said on June 26, 2008 11:34 AM:

Colinjack,

> woudn't really need to expose the collection, you could just write to the field

We could write to the field for scalar properties or for these collections if we knew a concrete type (eg the field wasn't IList<T>). If we couldn't directly see which concrete type to use, we would have to use a default (such as List<T>). In the case of relationships, we could materialize directly to the "AddXYZ()" method(s) that are exposed on your entity. For example, if we are materializing an Order that is part of the "Customers.Orders

" conceptual collection, and we are mapped to the Customers.AddOrder() method, we could just call this to add the Order to the Customer colleciton rather than creating a collection for Orders and setting it as well.

Jeff

# jeffders said on June 26, 2008 11:49 AM:

Serega,

The reason that you need to specify the "Customers" string in AddObject is because EF supports multiple EntitySets per type. This means that the Customer CLR type (and conceptual type), can be mapped to multiple backend sets such as "ActiveCustomers" and "ArchivedCustomers". These sets might represent some horizontal partitioning you have done in your store. You can read more about it here:

http://msdn.microsoft.com/en-us/library/bb738537.aspx

Jeff

# JorgeLeo said on June 27, 2008 3:23 PM:

To the association annotation on the class topic: I think that is very hard to avoid it. If there is a single relation between to classes, then at list we need a way to annotate its cardinality.

But what I find more problematic is in the case of the scenario of people and meetings. The meeting relates to different people for different reasons (participants vs. coordinator) and different meetings relate to the same people in different manners (participant in one meeting but coordinator of another). Since the idea is to push the abstraction of the model, rather than having a class representation of the database (as a side note, once I was asked “Why use classes and an ORM if you already have typed recordsets?”), I need a way to say in the model: “These two entities relate, and they can be related for different purposes”.

I know that we can create another class that represents the meeting participation between a person and a meeting, and put there the role… then again; we would be bringing the database to the class domain. I would expect that the framework would be able to annotate and resolve into a scheme representation on its own… Persistent Ignorance

Now, the change tracking… It would be much simpler if the C# team would enhance the language with some concepts from AOP…

# Iga lahendus tekitab uusi probleeme ehk alati võib leida veel ühe bugi. said on July 1, 2008 4:49 AM:

.Net -i arendajate kommuuni poolt on postitatud väga asjalik "Vote of no confidence" artikkel ADO.NET

# Eric and the .NET Framework said on July 8, 2008 9:31 AM:

The ADO.NET Entity Framework is very strategic to Microsoft - but a) it is a V1.0 technology (although

# Tim Mallalieu's Blog. said on July 18, 2008 2:31 PM:

We just wrapped up our first iteration of V2. We are shooting to get another iteration in before PDC

# jdevonshire said on July 31, 2008 4:54 PM:

Jeff,

You stated:

>> We are working on different strategies to help tracking changes in custom types, complex types, and relationships. You are right that tracking scalar (value) propeties is easier because you can just call Equals, but it gets harder, especially with relationships. One idea we had was to keep a snapshot of relationships in the context prior to a bunch of changes, and then when SaveChanges is called on the ObjectContext we'd do a graph diff to try to sort out what relationships have been added or removed. This has the downside of requiring a larger resource footprint to store the entire original state of relationships, but it would completely remove any change tracking requirement from the entities.

An alternative is to call a specific API to tell the context when things have changed. This could be done outside of the entity. We are also experimenting with events, but we havent' quite found a common event that works in enough cases. <<

Wouldn't changes be easier to track if System.ValueType supported an OnChange event?  The event would also facilitate rippling changes across a computed value graph allowing flexibility in the conceptual model.

Implementing the event in the base Framework shouldn't be too intrusive to merit consideration.

# system.data.objects dev guy said on August 10, 2008 4:09 AM:

Entity Classes &amp; Architecture Patterns Part of the Entity Framework FAQ . 2. Architecture and Patterns

# system.data.objects dev guy said on August 10, 2008 4:12 AM:

Entity Classes &amp; Architecture Patterns Part of the Entity Framework FAQ . 2. Architecture and Patterns

# Simon says... said on January 7, 2009 2:25 PM:

Artykuł opisuje propozycję implementacji zasady Inversion of Control (IoC) w systemach OLTP zbudowanych

Leave a Comment

(required) 
(optional)
(required) 
Page view tracker