Entity Framework 6 Extensions You Might Be Unaware Of

Entity Framework 6 was and still remains a ‘workhorse’ for data access in corporate .NET-based applications primarily because of its stability, low barrier of entry and wide renown. Therefore, I hope this article will still be useful.

Contents

  1. Database First without EDMX.
  2. Working with detached graphs.
  3. SQL modification.
  4. Data caching beyond the boundaries of the DbContext life-cycle.
  5. Retry during errors from SQL Server
  6. Overriding DbContext, isolating from the real DB.
  7. Quick insert.

 

Database First Without EDMX

I really don’t want to go into the old debate on Code First vs. Database First. Instead, I’d better write a few words on how to make your life easier, if you prefer Database First. Many developers who prefer this approach complain about inconveniences when working with the tedious EDMX file. This file can turn team development into real hell: it significantly slows down the merge of parallel changes because of the permanent ‘mixing’ of its internal structure. As for the models with several hundreds of instances (a typical legacy monolith), you can face a strong speed slowdown of any action when working with the standard EDMX designer.

The solution seems to be quite obvious – you need to abandon EDMS and prefer an alternate mean of the POCO generation and metadata storage. Well, it looks like a simple task, and EF has the Generate Code First From Database feature that is viable in Visual Studio (inVS2015 for sure). But in real life, rolling database changes onto the received model is very inconvenient with this tool. Furthermore, everyone working with EF for a long time remembers the Entity Framework Power Tools extension, that solves similar problems, but unfortunately, this project seems to be almost dead (you cannot install it on VS2015 without hacking), and a part of its developers now works in the EF team.

When everything looked so bad, I found EntityFramework Reverse POCO Generator. It is a T4 template for the POCO generation on the basis of the existing DB with a large number of settings and the open source code. It supports all basic EDMX features and includes a range of additional tips: generation of FakeDbContext/FakeDbSet for unit testing, attribute coverage for models (e.g. DataContract/DataMember) and others. Also, T4 provides a full control over code generation. To sum up: it works consistently, the team enjoys it, and migration of existing projects goes smoothly.

Working with Detached Graphs

Usually, attaching a new object or an object that was previously generated in other context is a simple task. Problems begin when it comes to graphs, that is entities with links: EF ‘out of the box’ does not track changes in the content of navigation properties of an entity reattached to the context. To track changes, the corresponding entry must exist (an object with service information, including info about the state of entity – Added, Modified, Deleted, etc) for each object entity during the life-cycle of the context. You can fill entries for adding graph in the following 2 ways:

  1. You can store the state within the entities and track changes on your own. Thus, our detached graph will contain all information required for connection.
  2. You can do nothing beforehand, and when you attach a graph to the context, you need to pull up the source graph from DB and set the entity states basing on the comparison of two graphs.

An example of solution #1 can be found in the Pluralsight course from Julie Lerman, a renown EF expert. You will need to take a large number of steps for its implementation. All entities must implement the IstateObject interface:

One way or another, we need to ensure the relevancy of the State values after manual addition of each graph entity to the context

in order to pass through all entries by editing their states:

In this case, we won’t need additional DB calls, but solution turns out to be too jumbo, fragile, and potentially not working for the many-to-many relations. Besides, it lumbers models (by the way, the requirement of interface implementation can be extended with modification of the T4 templates from the previous section of this article).

Let’s consider solution #2 briefly:

This call will add the root entity to the context. At that, it will update the navigation property with the Childs objects collection by means of a single SELECT to DB. It is now possible owing to the GraphDiff library. The author of the library has made all the dirty work and fixed basic bugs.

SQL Modification

Generation of the seemingly simple SELECT… FROM Table WITH (UPDLOCK) statement is not supported by EF. Instead, it has interceptors allowing to modify the generated SQL in any suitable way. For example, with help of regular expressions. Let’s add UPDLOCK to each generated SELECT within the life-cycle of the context (of course, granularity is not a necessary context, it depends on your implementation).

For this, let’s declare the With method within the context and register the interceptor:

LockInterceptor

Testing of our regular expression:

Data Caching Beyond the Boundaries of the DbContext Life-Cycle

EF caches such things, as:

  • Query Plan.
  • Metadata.
  • Compiled Queries.

Data caching is possible within the boundaries of the context life-cycle (recall the Find method). Moreover, it can hardly be called a full-featured cache. So, how can we organize a managed cash in the process memory that would be universal for all contexts? We will use EntityFramework.Plus, or EntityFramework.Cache, its “poor” alternative:

It is enough to run SQL profiler to ensure that the second call of SelectWithCache() does not affect DB. Lazy calls will also be cached.Moreover, you can integrate EF with a distributed cache. For instance, through the self-written cache manager on the basis of Sytem.Runtime.Caching.ObjectCache that is connected to EntityFramework.Plus. NCache supports integration with EF  ‘out of the box’ (I cannot go into detail – I haven’t tested this cache).

Retry during errors from SQL Server

SqlAzureExecutionStrategy – this strategy is supported in EF6 (it is disabled by default). During its usage, getting of the specific error code in a response from SQL Server leads to resending of SQL statement to the server.Error Codeas for SqlAzureExecutionStrategy

Interesting nuances:

  • You can write your own strategy on the basis of the SqlAzureExecutionStrategy source code by redefining the error codes leading to retry.
  • Usage of the retry strategies, inducing SqlAzureExecutionStrategy, imposes a series of restrictions. The most serious of them is incompatibility with user transactions. For explicit declaration of the transaction, we need to disable it through the call to System.Runtime.Remoting.Messaging.CallContext.
  • Strategy may be covered with integration tests (again, thanks to Julie Lerman, who kindly replied to this question).

Overriding DbContext, Isolating From the Real DB

For the testing purposes, let’s override DbContext for the invoking code transparently, and fill the fake DbSet with test data. I will provide several examples of solution of this problem:Method #1 (long): we need to create stubs for IMyDbContext and DbSet manually, and to describe the required behavior explicitly. It may look in the following way (using the Moq library):MockDbSet

There is a basic article in MSDN related to this subject: Entity Framework Testing with a Mocking Framework (EF6 onwards). There was a time when I was so excited with this method, that I created the whole demo project on GitHub (with the usage of EF6 DbFirst, SQL Server, Moq, Ninject). By the way, the above mentioned Entity Framework in the Enterprise course features the entire chapter dedicated to testing.Method #2 (short): usage of the already mentioned Reverse POCO Generator that creates stubs for your DbContext’s and all DbSet’s by default (there will be a normal in-memory collection inside FakeDbSet).

Quick Insert

To insert thousands of new records in SQL DB simultaneously, it is effective to use the BULK operations instead of the standard row-by-row INSERT. Here are ready-to-use solutions on the basis of SqlBulkCopy:

That’s it. Share your tips and tricks in the comments below.

Ilya Chumakov

Ilya is a lead C# developer at bi.zone (Moscow, Russia). He is involved in enterprise development, including distributed systems and web services.

Latest posts by Ilya Chumakov (see all)

Ilya Chumakov

Ilya is a lead C# developer at bi.zone (Moscow, Russia). He is involved in enterprise development, including distributed systems and web services.

  • Excellent post, May I translate to Spanish your article on my blog?