Initial POCO Design 1-Pager


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.

Comments (26)

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

  2. rogerj says:

    Hi, Tim,

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

  3. Nullable says:

    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

  4. GregYoung says:

    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

  5. colinjack says:

    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.

  6. >>> 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

  7. colinjack says:

    @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.

  8. JimmyBogard says:

    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.

  9. jeffders says:

    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

  10. jeffders says:

    >> 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

  11. Nullable says:

    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

  12. unbornchikken says:

    "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. 😉

  13. serega says:

    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.

  14. colinjack says:

    > 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.

  15. jeffders says:

    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

  16. jeffders says:

    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

  17. jeffders says:

    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

  18. JorgeLeo says:

    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…

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

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

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

  22. jdevonshire says:

    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.

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

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

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

  26. Tarique says:

    Just one question.

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

    Why CUD and why not CRUD ?

Skip to main content