EF 4.0 N-Tier Support: Take 2

Following the release of Visual Studio 2010 and .NET 4.0 Beta 2, the Entity Framework team put out a second CTP for Entity Framework 4.0, which includes enhancements to self-tracking entities. As the name implies, self-tracking entities know how to track their own state, so that it can be transmitted across service boundaries in an n-tier application. The idea is similar to what we’ve been able to do for several years with datasets. Datasets know how to keep track of their change-state and the table adapter knows how to inspect a dataset to generate insert, update and delete statements. However, because datasets are a .NET-specific type, it is not appropriate to expose them from services that should be platform-neutral. What we need is a service-oriented successor to the dataset that knows how to keep track of changes while remaining ignorant of how those changes will be persisted.

About a year ago I wrote an article for MSDN Magazine outlining one possible solution to this problem. Basically, I proposed attaching a property to each entity indicating its change-state (added, modified, deleted), and I supplied a custom collection capable of setting each entity’s change-state whenever it is added, modified or deleted. On the service-side, I wrote a helper class which interrogated entities for changes and persisted changes to the database, using either LINQ to SQL or the Entity Framework.

Entity Framework 4.0 uses the same basic approach, with entities tracking their own state and helper classes for change-tracking and persistence. The difference is that EF uses code-generation via T4 templates to create the helper classes instead of incorporating them into assemblies for the client and service to reference. I have to say that, although they could have built a client-side assembly without any dependencies on the Entity Framework, I have grown to like the code-gen approach. The reason is that T4 templates are totally open-source. If I don’t like what they did or want to change it in any way, all I have to do is create my own version of the template.

Lately I’ve had the chance to take STEs for a test drive, and I’ve put together a walk-through using the CTP 2 bits. You can download the sample application I created here. For convenience I’m using the Northwind sample database.

1. Open the Server Explorer in Visual Studio 2010 and a data connection to the Northwind database.

  • You may need to attach Northwind to SQL Express using Management Studio.
  • You may wish to add a RowVersion column of type timestamp to each table to facilitate concurrency management.

2. Create a class library project using Visual Studio 2010 and name it NorthwindData.

  • Add a new item to the project, selecting "ADO.NET Entity Data Model," naming it Northwind.edmx.
  • Select "Generate from Database" and choose the following tables:
    Customers, Orders, Order_Details, Products, Categories

3. Right-click on the model design surface and select "Add Code Generation Item."

  • Under the Code category select "ADO.NET Self-Tracking Entities" and name the T4 template Northwind.tt.

4. Add another class library project to the solution and name it NorthwindModel

  • Move the Northwind.Types.tt template to the NorthwindModel project.
  • Add a reference to System.Runtime.Serialization.
  • Add a project reference for NorthwindModel in NorthwindData project.
  • Open Northwind.Types.tt and modify inputFile initialization as follows:
    string inputFile = @"..\NorthwindData\Northwind.edmx";
  • Open Northwind.Context.tt and add "using NorthwindModel" directive in two places, both following <auto-generated> comment sections.

5. Add a WCF service to the solution (this can be a console app).

  • Reference both NorthwindData and NorthwindModel projects.
  • Add an interface called INorthwindService with a [ServiceContract] attribute and add methods with [OperationContract] attributes:
[ServiceContract]
public interface INorthwindService
{
    [OperationContract]
    Product GetProduct(int productId);

    [OperationContract]
    Order GetOrder(int orderId);

    [OperationContract]
    Order SaveOrder(Order order);

    [OperationContract]
    void DeleteOrder(Order order);
}

6. The GetOrder methods should use the Include operator to eager-load order details and products.

public Order GetOrder(int orderId)
{
    using (NorthwindEntities ctx = new NorthwindEntities())
    {
        // Eager-load related entities
        var query =
            from o in ctx.Orders
                .Include("OrderDetails.Product")
            where o.OrderID == orderId
            select o;
        Order order = query.SingleOrDefault();
        return order;
    }
}

7. In the SaveOrder method call ApplyChanges on ctx.Orders, passing the incoming Order entity (inserts and updates).

  • After calling ctx.SaveChanges, you will need to call AcceptChanges on both the order and each OrderDetail.
  • It would be nice if calling AcceptChanges on an order would in turn call AcceptChanges on items in collection properties.
  • The method should return the order that was saved in order to provide database-generated values (such as identity and concurrency fields).
  • For convenience, it would be nice to have a HasChanges extension method for IObjectWithChangeTracker that checks to see if items in collection properties have been modified, added or removed.
public Order SaveOrder(Order order)
{
    using (NorthwindEntities ctx = new NorthwindEntities())
    {
        // Inform object state mgr of changes
        ctx.Orders.ApplyChanges(order);

        // Persist changes to database
        ctx.SaveChanges();

        // Accept changes for order and details
        order.AcceptChanges();
        foreach (OrderDetail od in order.OrderDetails)
            od.AcceptChanges();
        return order;
    }
}

8. In the DeleteOrder method mark order and order details as deleted, then call ApplyChanges.

  • You will need to call ToList() on order.OrderDetails and then iterate the results to mark each item as deleted, so as not to modify the property during iteration. Then mark the order as deleted.
  • It would be nice if marking an order as deleted would also mark order details as deleted.
public static void DeleteOrder(Order order)
{
    using (NorthwindEntities ctx = new NorthwindEntities
        (Settings.Default.NorthwindConnection))
    {
        // First mark details as deleted (from a list)
        foreach (OrderDetail od in order.OrderDetails.ToList())
        {
            od.MarkAsDeleted();
        }

        // Then mark order as deleted
        order.MarkAsDeleted();

        // Inform object state mgr of changes
        ctx.Orders.ApplyChanges(order);

        // Persist changes to database
        ctx.SaveChanges();
    }
}

9. Add a client application to the project.

  • First reference the NorthwindModel project, then add a service reference to the WCF service. This will ensure that classes generated from the STE T4 template will be used.
  • Write code that creates a new order with two order details; change the order by adding a new detail, changing an existing detail, and removing a detail; then delete the entire order.
  • It would be nice to have a GetChanges methods that obtains only items that have been added, modified or deleted, so that it is not necessary to send unchanged items to the service for updating.

10. In order to delete an order detail, it is not sufficient to simply remove it from the order. Rather, you will need to call MarkAsDeleted on the order detail.

  • This behavior appears to be a bug, because removing an item from a collection should automatically mark it as deleted, just as adding or modifying an item automatically marks it as added or modified.

If you experimented with self-tracking entities in the first EF 4.0 CTP, based on Beta 1 of .NET 4.0 and Visual Studio 2010, you may have noticed a different method signature for ApplyChanges, in which you had to pass a delegate the returned an EntityChangeTrackerAdapter. While allowed you to supply your own adapter for converting change-state, the method signature for ApplyChanges is simpler and easier to use.

CTP 2 also improved the story of self-tracking entities by taking code for relationship fix-up out of the collection and placing it in the generated entity classes, where it takes advantage of the foreign key associations. The T4 templates for STEs also provide better support for concurrency management by preserving original values for properties that specify a ConcurrencyMode of Fixed in the entity data model. And the AcceptChanges method makes it easier to reinitialize an entity to an unmodified state after it’s been updated.

Hats off to the EF team for tackling n-tier scenarios head-on and coming up with an end-to-end solution for using the Entity Framework in service-oriented applications. Keep up the good work!

About Tony Sneed

Sr. Software Solutions Architect, Hilti Global Application Software
This entry was posted in Technical. Bookmark the permalink.

18 Responses to EF 4.0 N-Tier Support: Take 2

  1. Vincent THAVONEKHAM says:

    Hi Tony,

    Concerning EF4 CTP2, did you get any answers from your question :
    “Could you please explain more about support for Silverlight 3 and how generated entity types can be compiled to target Silverlight 3?”

    (http://blogs.msdn.com/adonet/archive/2009/11/04/ado-net-entity-framework-community-technology-preview-released.aspx)

    Indeed, I am also really interrested in it ??

    Thanks,

    Vincent

  2. Thanks for the great article.

    I do have a problem however when using the tt files from another folder than the one containing the edmx file:{“Mapping and metadata information could not be found for EntityType ‘Nais.Time.Model.Admin.Eventlog’.”}

    When using the non-STE version it works fine.

    Could it have someting to do with not using the ModelNamespace?

    kr,
    Michel.

  3. On further testing, it does not seem to have to do anything with splitting context and type, your sample does this too.

    I had the same problem when the client was a unit test, the config file had to be changed so that res://*/… was replaced with res://AssemblyName/…

    However, this time this trick does not seem to work.

    I did not re-create my service-ref because my client is a Silverlight client (so I have the same request as Vincent above).

    However, I do not think this matters as I am simply doing a read, no update yet.

    kr,
    Michel.

  4. Tony says:

    Hello Vincent,

    I haven’t any more info from the EF team on Silverlight 3 support,but they will post a Silverlight walk-through in the near future.

    Tony

  5. Tony says:

    Hello Michael,

    You need to change the template to point to where the edmx file is located. For example: To reference it from another project, open Northwind.Types.tt and modify inputFile initialization as follows: string inputFile = @”..\NorthwindData\Northwind.edmx”;

    Please let me know if this answers your question. Cheers,

    Tony

  6. Hi Tony,

    Without doing this your solution won’t even compile.

    My problem is run-time, but I found the reason, please see:

    http://blogs.msdn.com/adonet/archive/2009/11/04/ado-net-entity-framework-community-technology-preview-released.aspx?CommentPosted=true#commentmessage

    kr,
    Michel.

  7. Your bullet 4 should include a step at the end to include Project reference for NorthwindModel in NorthwindData project.
    Otherwise you will get build error on NorthwindModel namespace not found error.

  8. Tony says:

    Thanks, I’ve added the buttet point.

    Tony

  9. Ira says:

    Hello Tony,

    I am corrently working through your year old article on MSDN and have choosed Linq2SQL. My application goes live in 2-3 months so would you recomment I take the plunge and develop live with the EF CTP?

  10. Tony says:

    Hello Ira and happy new year!

    The answer to your question really depends on the type of application you’re developing and how “strategic” you want to be. I’m actually quite a big fan of LINQ to SQL, so if it’s not a critical line-of-business app and you’re sure of always having SQL Server on the back end, then L2S is a good choice.

    L2S, however, although not completely dead, it’s definitely on the way out. If your application is going to be around for a while, the more strategic approach is to bite the bullet and go for Entity Framework 4.0. This version is definitely ready for prime-time and CTP 2 has everything there you need (except for an updated POCO template, but you’re OK if using STE’s). Of course, as you know from my article, you should architect your app with a Data Access Layer and use Self-Tracking Entities for your POCO’s so that the persistence technology (L2S, EF or whatever) is decoupled from the rest of the app.

    Does this answer your question? I have a walk-though for STEs, which you can find here: https://blog.tonysneed.com/2009/11/12/ef-4-0-n-tier-support-take-2.

    Cheers,
    Tony

  11. Ira says:

    Happy new year to you Tony, and thanks for taking the time out to respond.

    @Does this answer your question?

    I would have to say, yes and no. Though I know Visual Studio 2010 is beta 2, with a release candidate coming out next month, the fact that EF 4.0 is still CTP 2 says to me that it will be 6 months or maybe VS 2010 SP1 before it is released.

    I like the way the change tracker works in the MSDN sample, and it should be relatively straightforward to upgrade to EF 4.0 once that is beta or RC.

    I have also had issues with WCF especially hacking the XML to allow for the change tracking if my project has modules in different .dll’s. I have moved everything into my main application for the moment, and hope the WCF team have allowed for flexible UDT collections.

    Were it to be that EF is nearly done and released soon, then I’d probably move to Dev 10.

    Regards,
    Ira

  12. Tony says:

    Hi Ira,

    I’m afraid you’re mistaken on the EF4 timeline. RTM for EF4 is synchronized with .NET 4.0 and VS 2010. Although the release date had been Mar 22, 2010, it has been delayed “a few weeks” according to the announcement. Nevertheless, they’re releasing a RC that should be almost identical to RTM and includes a go-live license. The only major feature that won’t make it into RTM is Code-Only, which will come later.

    In terms of change-tracking, STE’s have change-tracking implemented as part of the code-gen produced by the T4 template, which is fully customizable. Using that will be the most straightforward, although you could opt for DTO’s, such as what I demoed in my article. See Danny Simmon’s recent MSDN magazine article on n-tier EF4 for more info on the DTO approach vs STE’s.

    In terms of collection serialization, using STE’s would alleviate you from that concern as well, and you wouldn’t have to hack the XML, because the client is referencing the entities assembly instead of having the VS service-reference generate them (which is what I did in the article).

    I recommend again that you check out the STE walk-through I did, as well as that posted by the EF team on the ADO.NET team blog. STE’s represent I think a positive evolution of ideas I presented in my article, so they’re worth checking out. đŸ™‚

    Cheers,
    Tony

  13. Ira says:

    You make an overwhelming case, thus I feel I’ve no choice, but to download beta 2 and have a look at EF 4.0 with the new CTP.

    My fear, like any sensible person, is in not using a beta, and having to waste time in tracing hard 2 find bugs, or things start not working, so it’s “better the devil you know” in .NET 3.5.

    Thanks you again for the advice, it is very much appreciated.

    Regards,

    Ira

  14. Pingback: Tony and Zuzana’s World » Trackable DTO’s: Taking N-Tier a Step Further with EF4

  15. Chris.Go says:

    There is no Northwind.Types.tt template when you say to move this file to NorthwindModel project. I am using .NET4.0 and VS2010 did something change?

  16. Tony Sneed says:

    @Chris.Go: You seem to be referring to “Self-Tracking Entities”, which are generated by a T4 template that comes with VS 2010. My presentation shows an alternative implementation, which I call “Trackable DTOs”.

  17. Chris.Go says:

    Thanks for the information. Anyway you can do a detailed walk-through for the noobies such as myself with N-Tier and EF with a real-world presentation layer. Northwind DB would be fine. I’m not sure how I could repay you but I really want to learn more about these technologies.

    Side note: I have in the past just created my own data access layers and corresponding class/objects. I feel using all this .NET canned stuff paints you in a corner. I have never once had to create an application that was so cut and dry as to use data grids and repeaters… does using all this make custom applications difficult? I guess should I even bother if I am not in a position to build apps off the same company database all day long?

  18. aay says:

    I just down load sample and try to run it does not build it gave me error
    Error 1 Compiling transformation: The type or namespace name ‘TemplateFileManager’ could not be found (are you missing a using directive or an assembly reference?) c:\test\Hello Self-Tracking\NorthwindData\Northwind.Context.tt 11 1
    Error 2 Compiling transformation: The name ‘TemplateFileManager’ does not exist in the current context c:\test\Hello Self-Tracking\NorthwindData\Northwind.Context.tt 11 35
    Error 3 A namespace cannot directly contain members such as fields or methods C:\test\Hello Self-Tracking\NorthwindData\Northwind.Context.cs 1 1 NorthwindData

    any thougjht

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.