Agile Entity Framework 4 Repository: Part 1- Model and POCO Classes

I’m going to lay out this out in a few blog posts because it’s complex. As I am not a TDD developer, I won’t be starting from the tests. I’m doing this in a fashion which is logical to me. But that will mean you’ll get some teasers along the way that will be fleshed out further down the road.

  1. Agile Entity Framework 4 Repository: Part 1- Model and POCO Classes
  2. Agile Entity Framework 4 Repository: Part 2- The Repository
  3. Agile EF4 Repository: Part 3 -Fine Tuning the Repository
  4. Agile EF 4 Repositories Part 4: Compiled LINQ Queries
  5. Agile Entity Framework 4 Repository Part 5: IObjectSet and Include
  6. Agile Entity Framework 4 Repository: Part 6: Mocks & Unit Tests

I should be working on my 2nd Edition of Programming Entity Framework, but it’s driving me crazy that this information is so hard to find even though I’ve been doing conference and live meeting presentations on it.  The info in this post is also in the book, just laid out [very] differently.

A tiny bit of background

Entity Framework 4 supports POCO classes that do not inherit from Entity Object.

This is possible in two flavors.

Flavor 1: Snapshot Notification
Rather than depending on the EntityOBject to notify the context of changes to scalar property values or relationships, the context is now responsible for getting a snapshot of the entity state using ObjectContext.DetectChanges. You can call this method yourself or just let SaveChanges call it for you (a default).

Note that you don’t get lazy loading support with snapshot, however, without IRelatedEnd.Load available, you’ll need to use the context’s new LoadProperty method to do an explicit load.

Flavor 2: Proxies

If you mark EVERY property of your POCO class as virtual (Overrideable in VB), then EF will perform a neat trick. It will spin up proxy EntityObjects on behalf of the POCO class which will do what EntityObjects already know how to do – notify the context of changes to properties & relationships as well as allow lazy loading.

You can find more in-depth details on these on the team blog here:

POCO in the Entity Framework: Part 1 – The Experience

POCO in the Entity Framework : Part 2 – Complex Types, Deferred Loading and Explicit Loading

POCO in the Entity Framework : Part 3 – Change Tracking with POCO

Also, I’m waiting for my live meeting recording on EF4 POCOs (from www.notatpdc.com) that I presented last night to be available. Will blog when it is.

The Model

So first, here is what my model looks like.

image

My classes need to expose (public or private actually) a property for every property in the entity. YOu can have additional properties and methods if you want.

Here are two of my classes. Customer which is extremely simple, looks almost like a DTO, and Reservation which includes a method to return PaymentStatus and some validation logic as well.

Notice that the Customer properties are not virtual but the Reservation properties are. I have different requirements for these classes within my app.

CUSTOMER
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace POCOBAGA.Classes
{
  public class Customer
  {
    public int ContactID { get; set; }
    public int CustomerTypeID { get; set; }
    public Nullable<System.DateTime> InitialDate { get; set; }
    public Nullable<int> PrimaryDestinationID { get; set; }
    public Nullable<int> SecondaryDestinationID { get; set; }
    public Nullable<int> PrimaryActivityID { get; set; }
    public Nullable<int> SecondaryActivityID { get; set; }
    public string Notes { get; set; }
    public byte[] timestamp { get; set; }
    //navigation properties
    public Contact Contact { get; set; }
    public ICollection<Reservation> Reservations { get; set; }
  }
}
RESERVATION
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace POCOBAGA.Classes
{
    public enum ePaymentStatus
    {
        UnPaid = 1,
        PartiallyPaid = 2,
        PaidInFull = 3,
        OverPaid = 4,
        NotAvailable=5
    }
    public class Reservation
    {
        public virtual int ReservationID { get; set; }
        public virtual System.DateTime ReservationDate { get; set; }
        public virtual Nullable<int> ContactID { get; set; }
        public virtual Nullable<int> TripID { get; set; }
        public virtual byte[] TimeStamp { get; set; }
        //navigation properties
        public virtual Customer Customer { get; set; }
        public virtual Trip Trip { get; set; }
        public virtual ICollection<Payment> Payments { get; set; }
        //other logic
        public ePaymentStatus PaymentStatus()
        {
            int TripCost = Trip.TripCostUSD.Value;
            decimal PaymentSum = Payments.Sum(p => p.Amount);
            if (PaymentSum == 0)
            {
                return ePaymentStatus.UnPaid;
            }
            else
            {
                if (TripCost > PaymentSum)
                { return ePaymentStatus.PartiallyPaid; }
                else
                {
                    if (TripCost == PaymentSum)
                    { return ePaymentStatus.PaidInFull; }
                    else
                    { return ePaymentStatus.OverPaid; }
                }
            }
        }

        public bool ValidateBeforeSave(out string validationError)
        {
            bool isvalid = true;
            validationError = "";
            if (TripID == null)
            {
                isvalid = false;
                validationError = "Trip";
            }
            if (ContactID == null)
            {
                isvalid = false;
                validationError += ",Contact";
            }
            if (ReservationDate == null)
            {
                isvalid = false;
                validationError += ",Date";
            }
            if (validationError != "")
                validationError = "[ReservationID " + ReservationID.ToString() + ": " + validationError + "]";
            return isvalid;
        }
    }
}

The PaymentStatus method presumes that the Reservations have been loaded and doesn’t care how that’s taken care of. Because I have set up the Reservations class (and the Payments class which is not shown) to allow lazy loading, I know that the ObjectContext will take care of it for me if necessary. (There are a few caveats around this that I’m not going to dig into since this is enough to build up the example and make the points I want to make.)

The ValidateBeforeSave method is available so that I can let a Reservation be responsible for its own validation when it’s time to save. I’ll still have to call it from somewhere and that will be the same place I call SaveChanges. You’ll see.

The Project for my POCO Classes

The classes are in their own project.

Note that this project does *not* have a reference to System.Data.Entity.

image

That’s it for the classes.

The Model Repository

Now for what I call the Model Repository.

This is where the Entity Framework lives, my model, my context and interaction with the database.

There are two critical items in this project: my model and a class which inherits from ObjectContext.

image

The project has a reference to System.Data.Entity and to the project with the classes.

The Model (again)

The key to my model is that it does *not* do code generation. Notice that in the model’s File Properties, the “Custom Tool” property is empty. I have removed the default,  “CustomTool=EntityModelCodeGenerator”.

image

I have constructed my classes by hand as well as the context which will manage my classes. You could use T4 to generate your classes. I’m not in this scenario.

11/24: Adding some more emphasis re: T4. In this blog post my goal is for you to see what the classes look like, not how to create them. As we go through the posts it will become more apparent that code generation can be enormously helpful to automatically generate not only these classes, but the repostories and the context, from the model.

The Context

The classes have no clue about EF. So we need EF to be aware of the classes. The context’s job is to enable querying against the entities/classes, to keep track of their state and to persist them back to the data store. When you use the default code gen, an ObjectContext class is created to do this. You *must* do the same for your POCO classes. I’ve built my context class by hand.

ObjectSet is new to EF and critical to POCO support and agile development. ObjectSet is the strongly typed class that represents an EntitySet in the model. ObjectSet implements IObjectSet and inherits from ObjectQuery. If I weren’t building a repository, I would just return ObjectSet’s from my context just like the default generated context does. However, I need more flexibility so I am going to return an IObjectSet, e.g. any class that implements IObjectSet. Just make note of this and you’ll see where it fits in further on in a latter part of this blog post series.

Another notable  piece of this context, also that you’ll see come into play later on, is the fact that my class not only inherits from ObjectContext, as it needs to, but that it implements another interface, IBAGARepostiroy. This is my own custom interface and I’ll show it when we get to the testing part of this series. Just keep it in the back of your mind.

For now pay attention to the fact that I expose the IObjectSets of the various classes that I want to query. This means that with this class, BAGAContext, I can write LINQ to Entities queries such as context.Customers, or Context.Reservations.Where(r=>r.ReservationID=3). Because I’m quering from an ObjectContext, the objects that are returned from the query will be managed by the context as well. Even my POCO objects.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Objects;
using POCOBAGA.Classes;
using POCOBAGA.Repository.Interface;

namespace POCOBAGA.Repository.Model
{
    public class BAGAContext : ObjectContext, IBAGARepository
    {
        private IObjectSet<Customer> _customers;
        private IObjectSet<Trip> _trips;
        private IObjectSet<Reservation> _reservations;
        private IObjectSet<Contact> _contacts;
        private IObjectSet<Payment> _payments;

        public BAGAContext()
            : base("name=BAEntities", "BAEntities")
        {
            ContextOptions.LazyLoadingEnabled = true;
            _contacts = CreateObjectSet<Contact>();
            _customers = CreateObjectSet<Customer>();
            _trips = CreateObjectSet<Trip>();
            _reservations = CreateObjectSet<Reservation>();
            _payments = CreateObjectSet<Payment>();
        }
        public IObjectSet<Contact> Contacts
        {
            get
            {
                return _contacts;
            }
        }
        public IObjectSet<Payment> Payments
        {
            get
            {

                return _payments;
            }
        }
        public IObjectSet<Customer> Customers
        {
            get
            {
                return _customers;
            }
        }
        public IObjectSet<Trip> Trips
        {
            get
            {
                return _trips;
            }
        }
        public IObjectSet<Reservation> Reservations
        {
            get
            {
                return _reservations;
            }
        }
        public IEnumerable<Reservation> ManagedReservations
        {
            get
            {

                IEnumerable<ObjectStateEntry> oses =
                    this.ObjectStateManager.GetObjectStateEntries
                    (EntityState.Added | EntityState.Deleted |
                     EntityState.Modified | EntityState.Unchanged);
                return oses
                    .Where(entry => entry.Entity is Reservation)
                    .Select(entry => (Reservation)entry.Entity);
            }
        }

    }
}

There’s one last point to make about this class. I expose a property called ManagedReservations. I love this property. It gives me back all of the reservation objects that are currently being managed by the context. Why ask the developer to know how to do that or even know that it’s a bit of a challenge to get them. Many people would accidently execute another query against the database when they want just to see what’s in memory. This little method deserves its own blog post. :)

Okay, so that’s the end of Part 1.

In Part 2, we’ll look at the other “half” of the repository, the place where your queries and other logic necessary to interact with the context live. For example, GetReservationsByID and Save.

17 thoughts on “Agile Entity Framework 4 Repository: Part 1- Model and POCO Classes

  1. I’m STARTING to possibly think about paying attention to EF now that it looks like it will support POCOs and PI.

    But I’m still QUITE worried about the performance overhead and bloat. Some of the metrics I’ve seen in terms of times that it takes to load objects are frightful at best. And I’ve heard/seen way too many people complain about how much even Visual Studio stutters under the load of a system with a decent number of tables/entities…

    But keep up the great posts Julie. One of these days I may actually take EF for a test-drive ;)

  2. By the way, I’m aware that you could use T4 (as you pointed out), but I’m just wondering if Microsoft provided any templates with Visual Studio 2010 Beta 2 (or greater).

  3. There was a MS-built POCO template in the first Feature CTP but not in the 2nd (just cuz it wasn’t ready) but should be in the next.There are some in the community. Check Matthieu Mezil’s blog. He has one in french and one in english (engligh one is on MSMVPS blog site)

  4. I had a question regarding POCO inheritance and Model design. If I have lets say I have an abstract base class, Provider, and then I have two concrete classes, Mobile Provider, Fixed Provider. When creating the model in the designer how would one approach this, and ultimately what would my generated DB look like?

  5. In my EF model I have object identifiers but when I implement the POCO classes it seems those properties have public setters. I need my object identifiers to be private for obvious reasons. Is there a way to do this with EF 4 and POCO classes.

  6. Hey, Julie! Great post. However, it seems I am stuck at the very beginning with something that seems basic, but can’t find the answer to.

    I have implemented EF4 with POCO objects and the collections for related objects in by data objects are NOT virtual. I don’t want any proxies, nor do I want lazy loading, and I have specified this clearly in the context options.

    Now, I want to load from an entity from EF WITHOUT the related objects. I want to see the collection of related objects as an empty collection, no matter if they exist or not. Instead, I get the entire graph of objects, basically the entire database, from a single object query, since all its parent/children properties are filled.

    Can you suggest a solution? Thank you.

  7. Michael, William and Siderite,

    I’m doing my best to help everyone in need but am really slammed right now trying to get the book done and keeping other commitments in tact. If you can possible look elsewhere, that would be great. If not, I *will* come back to these but just can’t any time very soon. Sorry.

  8. My "problem" is solved. For anyone else getting into the same problem, the "solution" is not to use the same context as the one you just inserted all the objects from. I wasn’t loading the entire hierarchy, but getting it from memory from a previous insert query.

  9. Hi Julie,

    Did you ever write that post on ObjectStateManager.GetObjectStateEntries?

    I thoroughly agree it needs some light on it.

    I was amazed to discover that querying an ObjectSet did not return the uncommitted objects. I had always imagined an ObjectContext should be an Atomic Unit of work, like a SQL transaction where I could pass it around and query it for the work in progress through *one simple point* -the ObjectSet.

    Instead we have to use ObjectSet *and* ObjectStateManager. What’s more is that one can be cast to IQueryable and the other can only return IEnumerable so we cannot even union a query against them both.

    Anyway, I would be very interested in hearing more about this area as it makes implementing a clean IQueryable repository pattern much harder :)

  10. @simon – what you are looking for is not the ObjectSet’s job really. I could try to explain but would end up re-writing my 900 page book in the comment. ;)

  11. Julie,

    Great article, one of many I’ve read on POCO, but I’m not yet able to find an answer to the below question I asked on another site along with several others that have not received a response so I’m hoping you can provide some insight. I understand in your example that you hand coded your POCO objects rather than using the code generator…

    Question:

    No problems following the process on my own project and I’m able to compile. However as I’ve seen 2-3 other posts on this also go unanswered I’m going to ask the same question again…

    Why does the system not generate new Entities, etc once the Model.tt is removed and placed into the business layer?

    The Intent here is to have persistent ignorant objects which is achieved, however any future maintenance does not seem possible with this structure…

Leave a Reply

Your email address will not be published. Required fields are marked *


× eight = 8

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>