Sunday, January 6, 2013

Separating Entity Framework POCO Classes Generated From T4 Template in VS.NET 2012

The Entity Framework has come a long way and has some really nice features out of the box when using EF5 with VS.NET 2012. Specifically, if you are using a Database First approach, the generated entities from your .edmx are already POCOs. No more are the classes auto generated and inheriting from ObjectContext as in past versions. You could achieve this affect either manually or via POCO generation extensions in VS.NET 2010, but it is done by default now in VS.NET 2012.

So the 1st thing you might want to do is separate the POCOs into their own layer, separate from the .edmx. You would want to do this to keep your entities free from being tightly coupled to it's persistence details. After all there is no reason the POCO classes can't be used independently and outside of EF all together, and is why POCO support was such a big deal back when EF4 arrived.

There are a few things you need to do to separate the POCOs generated under the .tt branch under the .edmx file in VS.NET. As a side note, remember that the .tt file is just to support deign time tooling and is not compiled with the project. You don't need to have any heartburn with having this file reside near the POCO classes as it is not an indication of the classes being only used with EF; it brings along with it no dependencies to EF.

You may have already tried to drag the .tt file and associated POCO classes in Solution Explorer into a different layer and noticed it did not work. There are (2) ways to solve this issue: delete the .tt file and recreate in a new layer using a new 'EF 5.x DbContext Generator' item, or remove the dependency in the project's metadata file and then move it. Both will work, and I'm going to show you how to remove the dependency in the metadata file and then drag the files to the new layer. I don't get nervous modifying the .csproj files and this is a 1 time change. If you are not comfortable, then just add a new 'EF 5.x DbContext Generator' item to the separate layer and then follow the same steps below. I personally want the ability to move items around and want to break this dependency.

First make sure to build the solution to have all the metadata generated in the .csproj file, and then close VS.NET. Navigate to the project containing the .edmx file, and open the .csproj file for the containing project using notepad. We need to remove the XML element that creates a dependency between the .edmx file and the .tt template file. Search for the line below and remove it:

Reopen the solution and we now have the ability to drag the .tt and associated POCO classes into another layer. Remember, the .tt file is a design time tool and adds no dependencies or references to EF, so when we move these POCO classes they really are independent of any persistence details. Go ahead and drag the object in solution explorer as shown below. Once finished, add a reference to the layer that contains the .edmx to the new layer that contains the POCO classes.


There are (3) more steps to complete the move. 1st, we must update the path to the .edmx in the .tt file. Look for the following line near the top of the project and update as shown below:


The next step is to update the namespace for which the .Context.tt file under the .edmx is using. Because we have now moved the POCO classes to another layer, we must provide the namespace of the new location. If you do not do this, your Model.Context.cs file will have errors upon building. You might be tempted to add a 'using' statement to the namespace to fix the issue, but remember that you are dealing with auto-generated code. Every time the model is updated this class will be overwritten, so we don't want to modify it if possible. The proper way is to update the Custom Tool Namespace property on the Context.tt properties within VS.NET as displayed below:


The final step is to delete the original .tt file containing the POCO classes from the layer containing the .edmx as shown below:


The one step we must remember now is to run the Model.tt file manually now when we want our POCO classes to reflect an updated model. This will not happen automatically anymore as we have moved the POCO classes away from the .edmx. However it is as simple as right-clicking on the .tt file where the POCO classes are located and select 'Run Custom Tool' as shown below. Test it out by making a change to the .edmx model (i.e. change a property name), and then running the T4 template to recreate the POCO classes. Inspect the class to make sure the changes were reflected. If they were, then everything has been updated correctly.


Rebuild the solution and run, and everything should work as before. However, we have separated out POCO entities from the layer containing any persistence mechanism which is a good practice and follows the guidelines of SoC along with aligning our application with many of the designs and architectures we are familiar with using.

20 comments:

  1. Thank you very much for this post! Ive been searching for hours on how to best separate the POCO layer from the EF project, this is exactly what I needed!

    ReplyDelete
  2. Great! Glad it was helpful. There is so much fragmented information, so it's nice to have an end to end solution in one spot.

    ReplyDelete
  3. Hi again, do you have any idea what might stop the POCO classes from being generated when I run custom tool from the context.tt file? I have the correct namespace in the custom tool namespace and its on the context.tt under the edmx.
    Thanks
    Rick

    ReplyDelete
  4. This is exactly what I was looking for. Thanks a lot.

    ReplyDelete
  5. Nice tho you dont have to exit the VS environment to the fs and use notepad. You can just right click on the project and Select Edit Project File, make the change and then reload when done.

    ReplyDelete
  6. Thanks, that was needed

    ReplyDelete
  7. Hi Allen, my name is Darwin I'm developing a simple wpf application with EF, in order to learn more about the EF and POCO. I've separated the POCO entities in another project and added two more projects (Logica and UI). I compiled the solution and I have no error, however, I can not display data in the UI. Would you please help me determine what else I need. This is the project: http://sdrv.ms/18i0yZ1

    Thanks for your help!.

    P. D. Sorry for my English, :)

    ReplyDelete
  8. Nice article. However, I disagree the changing of "Custom Tool Namespace" : with the default EntityModelCodeGenerator, it will affect the namespace of your "AdventureWorksEntities" auto-generated class.
    Instead, change the .Context.tt by adding appropriate using clause referencing your new poco project namespace.

    ReplyDelete
  9. I agree with pYTh but it´s necessary avoid this "Once finished, add a reference to the layer that contains the .edmx to the new layer that contains the POCO classes" part after moved .tt file. You must to do a reference to entity project class in ef project

    ReplyDelete
  10. Are you going to update the POCO manually after separating the .edmx?

    ReplyDelete
  11. Very good article. A year later, it is still very useful and helped me sort things out. I did run into the same issue as pYTh. When I changed the Custom Tool Namespace, my DBcontext ended up in my MyProject.POCO namespace. I removed the entry from the Custom Tool Namespace and added "using MyProject.POCO;" to the MyModel.Context.tt file and things worked as I expected. Updating the .edmx model from the database did not overwrite the added using statement. Running "Run Custom Tool" from MyModel.tt within the MyProject.POCO refreshed my POCO classes.

    ReplyDelete
  12. Thanks, this was very helpful. I had no problem following the steps as stated.

    ReplyDelete
  13. Well explained and nicely illustrated. Thanks

    ReplyDelete
  14. Very good article. A simple and well focused explaination.
    Now in my EF cook book :D

    ReplyDelete
  15. Nice article...
    Excellent... Thank You... :)

    ReplyDelete
  16. You also can use LoopGen to generate POCO classes and DbContext from your existing database. It's super simple and you don't need EDMX or t4 templates

    search for LoopGen in Nuget or go to http://loopgen.com/code to learn more.

    ReplyDelete
  17. Thanks for the great post Allen!

    I would just like to point out that you can modify the "AdventureWorksModel.Context.tt" file with a text editor, and add your Model namespace there, so when the tool is run it will automatically add your Model namespace to the cs file.

    ReplyDelete