Total: 6 Average: 4

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

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:

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

public interface IStateObject { ObjectState State { get; set; } }

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

context.Foos.Attach(foo);

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

IStateObject entity = entry.Entity; entry.State = ConvertState(entity.State);

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:

context.UpdateGraph(root, map => map.OwnedCollection(r => r.Childs));

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

using (var ctx = new MyDbContext().With(SqlLockMode.UpdLock)) {}

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

public interface ILockContext { SqlLockMode LockMode { get; set; } MyDbContext With(SqlLockMode lockMode); } public class MyDbConfig : DbConfiguration { public MyDbConfig() { AddInterceptor(new LockInterceptor()); } } [DbConfigurationType(typeof(MyDbConfig))] public partial class MyDbContext : ILockContext { public SqlLockMode LockMode { get; set; } public MyDbContext With(SqlLockMode lockMode) { LockMode = lockMode; return this; } private static void MyDbContextStaticPartial() { } }

LockInterceptor

public class LockInterceptor : DbCommandInterceptor { public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { AddLockStatement(command, interceptionContext); } public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { AddLockStatement(command, interceptionContext); } private void AddLockStatement (DbCommand command, DbCommandInterceptionContext interceptionContext) { var lockMode = GetLock(interceptionContext); switch (lockMode) { case SqlLockMode.UpdLock: command.CommandText = SqlModifier.AddUpdLock(command.CommandText); break; } } private SqlLockMode GetLock (DbCommandInterceptionContext interceptionContext) { if (interceptionContext == null) return SqlLockMode.None; ILockContext lockContext = interceptionContext.DbContexts.First() as ILockContext; if (lockContext == null) return SqlLockMode.None; return lockContext.LockMode; } }

Regular expression may look in the following way:

public static class SqlModifier { private static readonly Regex _regex = new Regex(@"(?