Entity Framework is fast becoming the default data access method for small to medium sized apps in .NET. Whilst it offers a lot of benefits in terms of productivity and security, working with EF is often very different from working with the standard ADO.NET data-access objects and poses several common issues or ‘gotchas’:
Using Different DBContext objects
This is probably the most common ‘gotcha’ when starting development with EF. When an entity is loaded using a DBContext object it can only be tracked by that DBContext object and attempting to perform work on the entity using a different DBContext object will give rise to a variety of errors.
For example:
public void LoadUser(AppUser user) { var db = new FilingContext() ; user = db.AppUsers.Find("username"); UpdateUser(user); } private void UpdateUser(AppUser user) { var db = new FilingContext(); user.Email = "newEmail"; db.Entry(user).State = EntityState.Modified; db.SaveChanges(); }
This will raise the error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
This is a very simplistic example (especially since opening two contexts would be a bad idea for performance reasons) but as the model and data access code grows this issue can often arise.
It is normally best practice in development to limit each method to performing a single task. However, as in the above case, if an entity is created and then passed to another method for additional processing an error will be throw as it is being tracked by more than one DBContext.
The cleanest solution to this is to use AsNoTracking() when loading an entity and then later manually Attach the entity to the DBContext object. This informs EF that the change tracking features are not required and hence the DBContext object does not need exclusive control over the object:
public void Test(AppUser user) { var db = new FilingContext() ; user = db.AppUsers.AsNoTracking() .Where(x => x.UserName == "userName").Single(); UpdateUser(user); } public void UpdateUser(AppUser user) { var db = new FilingContext(); db.AppUsers.Attach(user); user.Email = "newEmail"; db.SaveChanges(); }
As a general point you should consider using AsNoTracking() when loading entities which you know will not be modified, as there is a performance overhead in the DBContext having to track the changes of entities.
Lazy Loading
Lazy loading is when EF determines which related entities need to be loaded and automatically loads these at run-time. For example, if an Employee
object contained a Job
entity you would not explicity have to request the loading of the Job entity when loading the Employee – just attempt to access the Job as a property of the Employee and EF will automatically load the Job.
Lazy loading looks extremely efficient and convenient but in practice it is totally impractical for most applications. It is a shame that so many of the code samples from Microsoft feature lazy loading, and hence developers typically start off by relying on it. However, to use lazy loading the DBContext which loaded the object needs to still be available and if any form of a Data Access Layer is implemented it has likely been destroyed.
Most experienced EF users recommend wrapping the DBContext object in a using
block to ensure it is destroyed after performing its work, in such a circumstance lazy loading could only be used with the using
block.
The most common head-scratcher for beginners is that lazy loading is turned off for validation. This leads to run-time errors which an entity has related entities which are marked as [Required] as below:
public class Employee { public virtual int ID { get; set; } public virtual string Name { get; set; } [Required] public virtual Job Job { get; set; } } public class Job { public virtual int ID { get; set; } public virtual string JobName { get; set; } } ...... var emp = db.Employees.Find(primaryKey); emp.Name = "New Name"; db.SaveChanges(); //throws an error at run-time
The above sample looks fine at first sight (and more importantly compiles without errors), but it throws a runtime error since the SaveChanges() method calls into EF Validation which notes the Required entity is not loaded and throws an error.
The simple solution to this is to an an Include statement to explicitly load the required entity:
var emp = db.Employees.Where(x => x.ID == primaryKey) .Include(x => x.Jobs);
In general a good alternative to marking entities as [Required] is to expose a non-nullable foreign key in the model as below. This avoids the common scenario above when EF throws an exception on validation due to a Required entity not being loaded.
public class Employee { public virtual int ID { get; set; } public virtual string Name { get; set; } public virtual int JobID { get; set; } public virtual Job Job { get; set; } }
SQL Generation From LINQ
LINQ is extremely powerful for querying objects, however, SQL is less so and since EF has to translate LINQ into SQL when a database is queried not all LINQ queries can be successfully executed.
One notable issue is using methods on the DateTime object. The Add() as well as the ToShortDateString()/ToLongDateString() methods of DateTime are commonly used in LINQ however there is no equivalent in SQL. Using these will therefore cause run-time errors. To rectify this you should use methods such as these outside of the LINQ statement.
//Below code causes an error as AddDays can't be translated to SQL for the query. var employees = db.Employees.Where(x => x.CreateDate > DateTime.Now.AddDays(-7)).ToList(); //Below code executes fine as AddDays is outside the LINQ query. var oneWeekCutoff = DateTime.Now.AddDays(-7); var employees = db.Employees.Where(x => x.CreateDate > oneWeekCutoff ).ToList();
Another common issue is using the Contains() method for checking if an object which implements IEnumerable (such as a List) contains another object. For example this method checks if a user is already stored:
public bool UserExists(AppUser user) { using (var db = new MyContext()) { return db.AppUsers.Contains(user); } }
This will compile fine, but will throw the below error at runtime:
Unable to create a constant value of type 'AppUser'. Only primitive types or enumeration types are supported in this context.
The reason is that LINQ-to-entities cannot translate Contains into a SQL Statement when an object is passed in.
A common solution to this is to use the Any() method and use a lamba to check for if any objects in the context have the primary key of the object you are testing for:
public bool UserExists(AppUser user) { using (var db2 = new FilingContext()) { return var bool1 = db2.AppUsers.Any(x => x.ID = user.ID); } }
Automatic Database Creation
EF can automatically update the database or create it if one does not exist – at least that is the headline. It would be more accurate to say that EF can automatically create or update the database when the first attempt is made to access the database.
This distinction can lead to a lot of confusion when a developer creates or updates the EF model, then loads the home page and checks the database which is mysteriously unchanged. The solution is to load a page which attempts to access some of the EF entities, this will then trigger the database creation/update.
Even adding an initializer in Application_Start()
as below will not change this behaviour since this only specifies the strategy for when the first access attempt is made on the database.
protected void Application_Start() { Database.SetInitializer(new CreateDatabaseIfNotExists()); //.... }
To trigger creation of the database upon startup, create an instance of your Context and then run its Initialize method:
protected void Application_Start() { Database.SetInitializer(new CreateDatabaseIfNotExists()); var db = new YourContext(); db.Database.Initialize(true); //.... }
Using EF with ASP.NET Membership
This is more of a headache than a ‘gotcha’ since EF makes no pretense of supporting or implementing the commonly used ASP.NET Membership system. It does however help to know that there is no easy solution to this problem – one very bad idea is to have the ASP.NET Membership tables in the same database as the EF entities since the EF database is likely to be dropped and recreated.
If you are looking to have a Foreign Key in an EF entities pointing at note that the primary key for users in ASP.NET Membership is the UserID not UserName and can be programatically accessed as below:
MembershipUser currentUser = Membership.GetUser(); string currentUserID = currentUser.ProviderUserKey.ToString();
Also note that having a ASP.NET Membership foreign key in your EF model leaves your app vulnerable to any changes Microsoft might make to the ASP.NET Membership schema in future as noted in this critique which alas doesn’t provide any clean solutions (since there really are none!).
There are a lot unique issues in EF, but these are the ones that I find are most frequently encountered when getting started using EF. Let me know in the comments if there are any other common gotchas you have come across.
Using Entity Framework With An Existing SQL Server Database (Code Second)
Entity Framework originally shipped with two modes – Model First which allowed for the entity model to be created using the designer UI and Database First which created the EF model from a database.
EF 4.1 introduced Code-First development, enabling developers to simply generate the EF model and database directly from code. This method rapidly became the most popular method for using EF, since it is relatively unobtrusive and developers can simply write POCOs (Plain Old CLR Classes) and have these seemlessly persisted to SQL Server.
Code-First did not however reverse engineer POCOs from a database schema.
The workaround was to use Database First to generate the edmx files and then generate the POCOs from the edmx files.
With Entity Framework Power Tools it is possible to reverse engineer a POCO model from an existing database (sometimes called ‘Code Second’). Simply install EF Power Tools (download here).
Prior to generating the POCOs you may wish to customize the code generation. This step is best skipped if this is the first time you are working with EF for reverse engineering code.
To customize the code creation, open the project you wish to use with EF, right click on the project name and select Entity Framework > Customize Reverse Engineer Templates:
This option does not perform any mode generation, but adds the T4 templates which are used for the model generation to you project. The templates are added in the Code Templates > ReverseEngineerCodeFirst folder. There are three T4 template, one for customizing the mappings, one for the context and one for the entities themselves. The mostly likely candidate for customization is the Entity.tt file for generating the entities, the Context.tt will just generate the context and this will be easy to edit it after creation, if the mappings need editing it will probably be on a case-by-case basis and so post generation editing would be fine.
In the Entity.tt file you can, for example, edit the constructors for the entities – by default the generator adds code to initialize all ICollection objects to new Lists to avoid run-time errors when attempting to access a list with no added entities, but you may wish to modify this or add custom code in the constructor:
One thing I like to do is remove virtual
from all properties in an entity. This will prevent lazy loading (see Entity Framework Gotchas for some of the issues with lazy loading) and set change tracking to Snapshot Change Tracking which is more straightforward to work with.
Next, to generate the POCOs. Right-click on the project and select Entity Framework > Reverse Engineer Code First:
Then simply select the server and database from which to generate the classes using the familiar connection / database dialog:
Click OK to start the model generator and the model classes should now appear in your project in the Models folder:
The POCOs will each be created in a separate file, in addition each entity will have an associated mapping information file in the Mappings folder describing how the properties in the entity will relate to eachother and to other entities in the database. When EF creates the database the mappings files will be used to create the schema, if no mappings files exist then EF can still create the database but only using its default object mappings. Note that the mapping files are not required to use EF code first (and you could avoid them being generated by deleting the Mapping.tt file prior to running the model generation but they are very useful to have as they will provide a great starting point for customizing the model later).
The context class itself may require some attention. The context class inherits from the EF DbContext class and all access to the database will be through a method exposed by this class or one of its DBSet properties (a DBSet corresponds to a table in the database).
By default the context class will be given the name of the database with ‘Context’ appended – ie [yourDBName]Context.cs . The first thing to note in the context class is the Initializer. By default the initializer is set to null meaning that EF will not automatically recreate or modify your database in the event of a change in the model:
static yourDBNameContext() { Database.SetInitializer<yourDBNameContext>(null); }
This is a very normal setup since EF Migrations does an excellent job of handling the updates to the database. However, for rapid development at the start of a project it is very efficient to have EF automatically drop and recreate the database without generating un-necessary migration files, if so you can choose to set this to DropCreateDatabaseIfModelChanges as below. There are several other less useful initialization options such as DropCreateDatabaseAlways which drops and recreates the database everytime the app is loaded.
Whether you are using EF Migrations or DropCreateDatabaseIfModelChanges
you may want to consider a Seed method. The seed method runs when the database is created or updated to add some data which may be required for development and testing.
protected override void Seed(yourDBNameContext db) { //Hard code data to be saved to database on db creation db.Employees.AddOrUpdate( x => x.FullName, new Employee { FullName = "Jude OKelly" }, new Employee { FullName = "Diego Maradona" } ); }
Note above the use of the AddOrUpdate method which checks if the record exists previous to adding it.
In the Context class also note the call to the base constructor which passes in the connection string:
public yourDBNameContext() : base("Name=yourDBNameContext") { }
There will be an entry in the <connectionStrings> node web.config for the actual string.
That’s all that is required. Overall it is a very simple process, and even with a bit of customization and testing it should take less than half hour to have a fully functioning EF model.