Entity Framework Code First Optimization
Optimizations for EF Code First, especially at startup.
NGen
EF 6+ is released by Nuget, and so not NGened. Optimizing the 5Mb dll is an easy fix (run as Admin, this is 64bit).
%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\ngen install EntityFramework.dll
No Migrations / Hard-code ManifestToken
The migrations code is easy in development, but startup will be slow in production. See Rowan Miller on the CF database calls. As he explains, the database version is generally know too, so you can eliminate all the calls with this DbConfiguration:
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
namespace Data
{
/// <summary>
/// DbConfiguration
/// </summary>
public class CustomConfiguration : DbConfiguration
{
public CustomConfiguration()
{
SetDatabaseInitializer<CustomContext>(null);
SetManifestTokenResolver(new CustomManifestTokenResolver());
}
internal class CustomManifestTokenResolver : IManifestTokenResolver
{
private readonly IManifestTokenResolver _defaultResolver = new DefaultManifestTokenResolver();
public string ResolveManifestToken(DbConnection connection)
{
var sqlConn = connection as SqlConnection;
//for SQLServer, 2008 or 2012
return sqlConn != null ? "2012" : _defaultResolver.ResolveManifestToken(connection);
}
}
}
}
If the DbConfiguration is not in the same assembly as the DbContext, add
<entityFramework codeConfigurationType="Data.CustomConfiguration, Data">
or...
[DbConfigurationType(typeof(CustomConfiguration))]
public class CustomContext : DbContext
Transient SQL errors
In EF 6.1, this only exists for Azure errors in the DbConfiguration:
SetExecutionStrategy("System.Data.SqlClient", () => new System.Data.Entity.SqlServer.SqlAzureExecutionStrategy());
There is no in-the-box support for non-Azure errors (like deadlocks).
Lazy loading
Can be good or bad...
- You can avoid N+1 problems with LazyLoading with Includes
- You can turn off LazyLoading with context.Configuration.LazyLoadingEnabled = false;
- You can disable LazyLoading by forgetting to make association properties virtual
- Too many Includes make big, complex queries.
- Preload the associations with dbSet.Load(), then do the query and reply on EF fixing up the associations.
Project queries (use DTOs)
dbSet.AsNoTracking() creates entities without change tracking (or context.dbSet.MergeOption = MergeOption.NoTracking; )
Otherwise, consider using query projection into a DTO (with Linq .Select). Don't load what you don't need.
For maximum efficiency, use rawSql, via the DbContext.Database or Dapper:
var products = context.Database.SqlQuery<Product>(
"select * from Products where Price>@p0", 2);
EntityFramework.Extended
The nuget package EntityFramework.Extended (github) includes batch updates and deletes, .Future() and .FromCache() (uses memory cache or provider).
Multi-tenant DbContext
Generally simpler, smaller models are much more efficient. If you don't use migrations (use a Database null initializer) you can have true multi-tenant DbContexts. Since EF 6.1, we can have multiple DbContexts per database. In the migration configuration, there is a ContextKey which is by default the FQN of the DbContext.
Then when creating the migration specify which you are using.
enable-migrations -contextTypeName:OrderContext -MigrationsDirectory:OrderMigrations enable-migrations -contextTypeName:productContext -MigrationsDirectory:ProductMigrations add-migration -configuration:Data.OrderMigrations.Configuration "Initial" add-migration -configuration:Data.ProductMigrations.Configuration "Initial" update-database -configuration:Data.OrderMigrations.Configuration
Other
- DbContext should be short-lived (in asp, per-request). In longer-lived console batches, a single open DbContext is a bad idea.
- See MSDN for info on pre-generated views etc.