Succinctly Books Index

This page lists all the books I wrote or reviewed for Syncfusion’s Succinctly series.

Books I wrote:

Books I reviewed:

Entity Framework Core Succinctly Released

My latest (and fifth) ebook for Syncfusion’s Succinctly collection is out: Entity Framework Core Succinctly! It covers Entity Framework Core 2.0 and you can download it for free – need to register first, though.

This book is inspired by my previous one on Entity Framework Code First, but quite a lot has changed.

Huge thanks to Jeff Boenig for the technical review and to Hillary Bowling, Tres Watkins and Jacqueline Bieringer of Syncfusion for all their support.

Entity Framework Core Pitfalls: No TransactionScope Support

Entity Framework Core, as of version 2.0, does not support enlisting in ambient transactions, like those provided by TransactionScope. It is being tracked by issue #9561. This is by design and is related to a limitation of System.Data.SqlClient, which will be fixed when .NET Core 2.1 comes out. The ticket for SqlClient is #3114.

I already talked about the hard relation between Entity Framework and TransactionScope before. This time, however, with EF Core 2.1, it seems that it will be fixed, and this is a good thing. For now, however, you will have to roll out your own transaction management strategy which includes starting your own transactions explicitly and either committing them or rolling them back at the end. Please refer to the Microsoft documentation available at https://docs.microsoft.com/en-us/ef/core/saving/transactions.

Soft Deletes with Entity Framework Core 2 – Part 2

My previous post explained how to setup soft deletes for queries automatically. This time, I’m going to talk about the other part: actually replacing a delete for an update that sets the is deleted column.

The key here is to intercept the SaveChanges method, find out all entities that are ISoftDeletable and are marked for deletion and then set their IsDeleted property and change their state to be modified instead of deleted:

public override int SaveChanges()
{
foreach (var entry in this.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted))
{
if (entry.Entity is ISoftDeletable)
{
entry.Property(_isDeletedProperty).CurrentValue = true;
entry.State = EntityState.Modified;
}
}

return base.SaveChanges();
}
This way Entity Framework will know what to do and just update them instead of deleting them. Next time, they won’t be loaded as they have been marked as deleted.

Soft Deletes with Entity Framework Core 2 – Part 1

Entity Framework Core 2, already covered here, includes a cool feature called global filters. By leveraging global filters, we can apply restrictions automatically to entities, either loaded directly or through a collection reference. If we add this to shadow properties (in the case of relational databases, columns that exist in a table but not on the POCO model), we can do pretty cool stuff.

In this example, I am going to create a soft delete global filter to all entities in the model that implement a marker interface ISoftDeletable.

public interface ISoftDeletable
{
}
We just need to override the DbContext’s OnModelCreating method to automatically scan all known entities to see which implement this interface and then create the restriction automatically:
private const string _isDeletedProperty = "IsDeleted";
private static readonly MethodInfo _propertyMethod = typeof(EF).GetMethod(nameof(EF.Property), BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(typeof(bool));

private static LambdaExpression GetIsDeletedRestriction(Type type)
{
var parm = Expression.Parameter(type, "it");
var prop = Expression.Call(_propertyMethod, parm, Expression.Constant(_isDeletedProperty));
var condition = Expression.MakeBinary(ExpressionType.Equal, prop, Expression.Constant(false));
var lambda = Expression.Lambda(condition, parm);
return lambda;
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
if (typeof(ISoftDeletable).IsAssignableFrom(entity.ClrType) == true)
{
entity.AddProperty(_isDeletedProperty, typeof(bool));

modelBuilder
.Entity(entity.ClrType)
.HasQueryFilter(GetIsDeletedRestriction(entity.ClrType));
}
}

base.OnModelCreating(modelBuilder);
}
So, for each entity known from the context we add a shadow property called IsDeleted of type bool. Of course, needless to say, it must also exist on the database. The reason I’m making it a shadow property is to avoid people tampering with the entities, by setting or unsetting its value. This way, the restriction is always performed and it is invisible to us. After we create the property, we add a restriction to the entity’s type.
Simple, don’t you think? This way, if you want to enable or disable it for a number of entities, just have them implement the ISoftDeletable interface.

Entity Framework Core Extensions: Getting Primary Keys and Dirty Properties

Two simple hacks: finding the primary key values:

public static IDictionary<string, object> GetKeys(this DbContext ctx, object entity)
{
if (ctx == null)
{
throw new ArgumentNullException(nameof(ctx));
}

if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}

var entry = ctx.Entry(entity);
var primaryKey = entry.Metadata.FindPrimaryKey();
var keys = primaryKey.Properties.ToDictionary(x => x.Name, x => x.PropertyInfo.GetValue(entity));

return keys;
}

This returns a dictionary because some entities may have composite primary keys.

As for getting the dirty (modified) properties’ names for an entity:

public static IEnumerable<string> GetDirtyProperties(this DbContext ctx, object entity)
{
if (ctx == null)
{
throw new ArgumentNullException(nameof(ctx));
}

if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}

var entry = ctx.Entry(entity);
var originalValues = entry.OriginalValues;
var currentValues = entry.CurrentValues;

foreach (var prop in originalValues.Properties)
{
if (object.Equals(originalValues[prop.Name], currentValues[prop.Name]) == false)
{
yield return prop.Name;
}
}
}
Picking on the last one, if we wish to reset an entity to it’s original values:
public static void Reset(this DbContext ctx, object entity)
{
if (ctx == null)
{
throw new ArgumentNullException(nameof(ctx));
}

if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}

var entry = ctx.Entry(entity);
var originalValues = entry.OriginalValues;
var currentValues = entry.CurrentValues;

foreach (var prop in originalValues.Properties)
{
currentValues[prop.Name] = originalValues[prop.Name];
}

entry.State = EntityState.Unchanged;
}

Mind you, this one will not reset collections or references, just plain properties.

Hope it helps!

This is the eight post in a series of posts about bringing the features that were present in Entity Framework pre-Core into EF Core. The others are:

  • Part 1: Introduction, Find, Getting an Entity’s Id Programmatically, Reload, Local, Evict

  • Part 2: Explicit Loading

  • Part 3: Validations

  • Part 4: Conventions

  • Part 5: Getting the SQL for a Query

  • Part 6: Lazy Loading

  • Part 7: Entity Configuration in Mapping Classes

This time I’m going to talk about the possibility to log the generated SQL to the output. In the past, we could do it by assigning a writer to DbContext.Database.Log:

ctx.Database.Log = Console.Log;
This would cause all SQL statements to be sent to the console. We can do something similar in EF Core, by leveraging the logging framework:
var loggerFactory = new LoggerFactory()
.AddDebug((categoryName, logLevel) => (logLevel == LogLevel.Information) && (categoryName == DbLoggerCategory.Database.Command.Name))
.AddConsole((categoryName, logLevel) => (logLevel == LogLevel.Information) && (categoryName == DbLoggerCategory.Database.Command.Name));

var optionsBuilder = new DbContextOptionsBuilder<MyContext>()
.UseLoggerFactory(loggerFactory)
.UseSqlServer(@"<connectionString>");

var ctx = new MyContext(optionsBuilder.Options);
We are creating a new logger factory and populating it with the console and debug providers. You will need to add the Microsoft.Extensions.Logging.Console and Microsoft.Extensions.Logging.Debug NuGet packages (you don’t need both). For each of these providers, we are filtering the output by LogLevel.Information and category DbLoggerCategory.Database.Command.Name (“Microsoft.EntityFrameworkCore.Database.Command”), which are the values used when outputting SQL.

Then, we are creating a DbContextOptionsBuilder and in it we are replacing the default logger factory with our own. We then use it to create an option which we pass to our DbContext’s constructor, or we could do the same in the OnConfiguring method.

The output will look something like this:

SELECT [p].[ProjectId], [p].[CreatedAt], [p].[CreatedBy], [p].[CustomerId], [p].[Description], [p].[End], [p].[Name], [p].[Start], [p].[UpdatedAt], [p].[UpdatedBy]
FROM [Projects] AS [p]

Hope this helps! Winking smile

 

What’s New and Changed in Entity Framework Core 2

Introduction

By now you should know that EF Core 2 was released on August 14th. It brought something new and some breaking changes too. Alas, it still does not include some of the features that used to be in pre-Core editions and are in high demand, such as lazy loading and support for group by translation. See the list here.

.NET Standard 2.0

Entity Framework Core 2 now targets .NET Standard 2.0, which was also released just now. This means that it will be useful in other scenarios, on any platform that supports it.

Improved SQL Generation

Improvements include:

  • Unneeded nested sub-queries are not created
  • Select only requested columns (projections)
  • No more creating multiple SQL queries for a single LINQ query

Owned Entities

Complex types are back, and they are now called owned entities. Remember that a difference between a complex type and an entity is that the former does not have an identity. Think, for example, of an Address class and several properties, Personal, Work, etc; all of these properties can be mapped to this class, and they will be stored in the same table as the containing entity. It looks like this:

modelBuilder
.Entity<Customer>()
.OwnsOne(c => c.PersonalAddress);

You can also put the content for these properties in another table, and you do it like this:

modelBuilder
.Entity<Customer>()
.OwnsOne(c => c.PersonalAddress)
.ToTable(“CustomerAddress”);

Of course, you can “own” multiple properties at once. Not possible to declare owned entities through attributes at this time.

Table Splitting

You can now have different classes that point to the same physical table, Entity Framework Core will not complain. These classes will probably expose different properties.

Entity State Listener

There’s a new interface that is registered by default, ILocalViewListener, that can be used to track entity changes – not materialized entities, unfortunately:

var events = ctx.GetService<ILocalViewListener>();
events.RegisterView((entry, state) =>
{
//entry contains the entity and state its current state
});

In case you are wondering, you cannot use this to cancel changes, because it is only called after the actual event took place.

Pluralization

There is a new IPluralizer interface and a dummy implementation NullPluralizer. It can be used to pluralize table names when EF is generating the database (dotnet ef database update) or entities when generating classes from it (Scaffold-DbContext). The way to use it is somewhat tricky, as we need to have a class implementing IDesignTimeServices, and this class will be discovered automatically by these tools:

public class CustomPluralizerDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
services.AddSingleton<IPluralizer, CustomPluralizer>();
}
}

public class CustomPluralizer : IPluralizer
{
public string Pluralize(string name)
{
return ...;
}

public string Singularize(string name)
{
return ...;
}
}

Because the tools also rely on the dependency injection framework, we are providing an alternative implementation of the IPluralizer interface through it.

DbContext Pools

Normally when a DbContext is injected somewhere by the dependency injection framework, a new instance is created every time. With this, we can have a pool of instances, 128 by default. It is a performance improvement and it is configured like this:

services.AddDbContextPool<DataContext>(options =>
{
//...
}, poolSize: 256);

Attach

The Attach method, for attaching existing entities, is now more clever: if any of the entities in the graph being attached has its key set, it will be treated as unchanged, and if not, as new.

Entity Type Configuration

We can now store entity configuration in separate classes, similar to what we used to have:

public class MyEntityTypeConfiguration : IEntityTypeConfiguration<MyEntity>
{
public void Configure(EntityTypeBuilder<MyEntity> builder)
{
//...
}
}

only these classes are not discovered automatically any more:

modelBuilder.ApplyConfiguration(new MyEntityTypeConfiguration());

Global Filters

Global filters already existed, for entities, not collections, in pre-Core EF. They are useful in essentially two scenarios:

  • For multitenant apps
  • For soft deletes

Its configuration goes like this for soft deletes:

modelBuilder
.Entity<Post>()
.HasQueryFilter(p => !p.IsDeleted);

Or, for a multi-tenant:

modelBuilder
.Entity<Blog>()
.HasQueryFilter(p => p.TenantId == this.TenantId);

It will apply filtering to any entities loaded as the result of a query (including eager loads) or from a one-to-many collection, but it will not filter a query by id, a one-to-one or many-to-one.

You can explicitly disable any existing filters by calling the IgnoreQueryFilters extension method:

ctx
.Blog
.IgnoreQueryFilters()
.ToList();

Disabling Client-Side Evaluation

You may be aware that EF Core can do client-side evaluation of methods that it does not know about, that is, cannot be translated to database calls. This happens transparently and may turn into a performance issue. Should you need to disable this, you now can by configuring the logging infrastructure to throw an exception when client evaluation occurs:

var builder = new DbContextOptionsBuilder<DataContext>()
.ConfigureWarnings(options =>
{
options.Throw(RelationalEventId.QueryClientEvaluationWarning);
options.Default(WarningBehavior.Log);
});

Like

We now have support for SQL’s LIKE function, although in the past we also supported something similar, through the String.Contains method. It goes like this:

ctx
.Posts
.Where(x => EF.Functions.Like(x.Title, “%NET%”)
.ToList();

Unfortunately, Microsoft didn’t make Like an extension method, which I think would be easier to use.

Calling Scalar Functions

Support for calling scalar functions is back too, with some minor caveats:

  • These functions need to be static and declared on the context class
  • They can only return and take as parameters scalar values

An example, first, the declaration, using the T-SQL built-in SOUNDEX function, through the [DbFunction] attribute:

[DbFunction]
public static string Soundex(string name)
{
throw new NotImplementedException();
}

Or by code:

modelBuilder.HasDbFunction(this.GetType().GetMethod(“Soundex”));

In either case you can specify both the schema or the function’s name, if it is different from the method to be used:

[DbFunction("FuncName", Schema = "dbo")]

modelBuilder.HasDbFunction(this.GetType().GetMethod("FuncName"), options =>
{
options.HasName("FuncName");
options.HasSchema("dbo");
});

And its usage:

var sounds = ctx
.MyEntity
.Select(x => x.Soundex(x.Name))
.ToList();

String Interpolation Support

Now, the FromSql and ExecuteSqlCommand methods support interpolated strings, and will happily produce parameters as needed. You do not have to worry about those nasty SQL injection attacks and performance issues due to query plan creation! It goes like this:

ctx
.Database
.ExecuteSqlCommand($"UPDATE Record SET Value = {value} WHERE Id = {id}");

Explicitly Compiled Queries

Entity Framework Core included query caching since version 1, but there is still some overhead associated with calculating the key from the query and getting it from the cache. Therefore, version 2 introduced a capability that existed in LINQ to SQL and Entity Framework pre-Core: explicit query compilation and execution. By this, we are able to pre-compile a query and use it in whatever context we want (of a compatible type, of course). We can even eagerly fetch associated collections or entities:

static readonly Func<MyEntityContext, int, IEnumerable<MyEntity>> 
CompiledQuery = EF.CompileQuery<MyEntityContext, int, MyEntity>((ctx, id) =>
ctx.MyEntities.Where(x => x.Id == id).Include(x => x.Others).OrderBy(x => x.Name));

As you can see, it returns a delegate that we can invoke passing it the proper parameters – in this example, a context and a parameter, but you can have up to 8 parameters, of different types:

var results = CompiledQuery(ctx, 100).ToList();

Breaking Changes

The IDbContextFacfory<T> interface was replaced by IDesignTimeDbContextFactory<T>. The signature of the CreateDbContext method changed also:

public class DummyContextFactory : IDesignTimeDbContextFactory<DummyContext>
{
    public DummyContext CreateDbContext(string[] args)
    {
        var builder = new DbContextOptionsBuilder<DummyContext>();
        builder.UseSqlServer("…");
        return new DummyContext(builder.Options);
    }
}

UseInMemoryDatabase now needs a name:

optionsBuilder.UseInMemoryDatabase("MyDatabase")

Package Microsoft.EntityFrameworkCore.SqlServer.Design is deprecated in favor of Microsoft.EntityFrameworkCore.Design (now provider-agnostic).

Only 2.0 providers will work, so any existing providers that target EF Core 1.x will need to be rewritten.

Logging event IDs have changed from the previous version and they are now identical to those used by corresponding ILogger messages. The logger categories now come from subclasses of DbLoggerCategory, such as DbLoggerCategory.Database.Command, DbLoggerCategory.Migrations, DbLoggerCategory.Infrastructure, etc, all of which offer a Name property.

What’s Still Not Here

Still missing are (not a complete list, mind you):

You can find a more thorough list here: https://weblogs.asp.net/ricardoperes/missing-features-in-entity-framework-core.

Conclusion

Still a long way to go; especially, GroupBy translation, many to many and lazy loading seems to be taking forever, both are scheduled for the next version (2.1) though. Non-relational providers are also nowhere to be seen. This new version has interesting new stuff and Microsoft seems to be going in the right direction, but it strikes me as odd that such high demand features are still absent. Let’s see how things evolve.

References

https://blogs.msdn.microsoft.com/dotnet/2017/08/14/announcing-entity-framework-core-2-0

https://docs.microsoft.com/en-us/ef/core/what-is-new

https://github.com/aspnet/EntityFrameworkCore/wiki/Roadmap

https://github.com/aspnet/EntityFrameworkCore/issues?q=is%3Aopen+is%3Aissue+milestone%3A2.0.0

https://weblogs.asp.net/ricardoperes/missing-features-in-entity-framework-core

Implementing Missing Features in Entity Framework Core – Part 7: Entity Configuration in Mapping Classes

This is the seventh post in a series of posts about bringing the features that were present in Entity Framework pre-Core into EF Core. The others are:

  • Part 1: Introduction, Find, Getting an Entity’s Id Programmatically, Reload, Local, Evict

  • Part 2: Explicit Loading

  • Part 3: Validations

  • Part 4: Conventions

  • Part 5: Getting the SQL for a Query

  • Part 6: Lazy Loading

This time I’m going to cover automatic entity configuration in mapping classes, that is, outside of the DbContext.OnModelCreating method. If you remember, Entity Framework Code First supported having classes inheriting from EntityTypeConfiguration in the same assembly as the context, and these classes would be loaded automatically. This made it much simpler to add new mapping classes to a project without touching the context.

This functionality hasn’t been ported to Entity Framework Core yet, but it is being developed for the next version, and is tracked by ticket 2805. My implementation finds mapping classes in the same assembly as the context automatically, or it can load configuration explicitly from a mapping class. Here is my contract for the mapping class:

public interface IEntityTypeConfiguration<T> where T : class
{
void Configure(EntityTypeBuilder<T> entityTypeBuilder);
}

As you can see, this needs to be implemented in a concrete generic class and bound to a specific entity type.

The actual implementation goes like this:

public static class EntityTypeConfigurationExtensions
{
private static readonly MethodInfo entityMethod = typeof(ModelBuilder).GetTypeInfo().GetMethods().Single(x => (x.Name == "Entity") && (x.IsGenericMethod == true) && (x.GetParameters().Length == 0));

private static Type FindEntityType(Type type)
{
var interfaceType = type.GetInterfaces().First(x => (x.GetTypeInfo().IsGenericType == true) && (x.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)));
return interfaceType.GetGenericArguments().First();
}

private static readonly Dictionary<Assembly, IEnumerable<Type>> typesPerAssembly = new Dictionary<Assembly, IEnumerable<Type>>();

public static ModelBuilder ApplyConfiguration<T>(this ModelBuilder modelBuilder, IEntityTypeConfiguration<T> configuration) where T : class
{
var entityType = FindEntityType(configuration.GetType());

dynamic entityTypeBuilder = entityMethod
.MakeGenericMethod(entityType)
.Invoke(modelBuilder, new object[0]);

configuration.Configure(entityTypeBuilder);

return modelBuilder;
}

public static ModelBuilder UseEntityTypeConfiguration(this ModelBuilder modelBuilder)
{
IEnumerable<Type> configurationTypes;
var asm = Assembly.GetEntryAssembly();

if (typesPerAssembly.TryGetValue(asm, out configurationTypes) == false)
{
typesPerAssembly[asm] = configurationTypes = asm
.GetExportedTypes()
.Where(x => (x.GetTypeInfo().IsClass == true) && (x.GetTypeInfo().IsAbstract == false) && (x.GetInterfaces().Any(y => (y.GetTypeInfo().IsGenericType == true) && (y.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)))));
}

var configurations = configurationTypes.Select(x => Activator.CreateInstance(x));

foreach (dynamic configuration in configurations)
{
ApplyConfiguration(modelBuilder, configuration);
}

return modelBuilder;
}
}

You can see that it uses some dynamic magic to make things simpler, otherwise we’d need to have even more reflection. Dynamics take care of these things quite nicely.

The code essentially looks at the entry assembly and finds all non-abstract public types that implement IEntityTypeConfiguration<T>. For each of those, it creates an instance, extracts the template argument and creates an EntityTypeBuilder<T> from calling the Entity<T> method of the ModelBuilder class and calls the IEntityTypeConfiguration<T>.Configure method of the instantiated mapping class passing it the EntityTypeBuilder<T> which allows it to supply mapping configuration for the mapped entity (T).

We need to explicitly call this extension inside DbContext.OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.UseEntityTypeConfiguration();
base.OnModelCreating(modelBuilder);
}

And it takes care of everything for us. Or, if we want to load a single mapping class explicitly, we can also do so:

modelBuilder.UseEntityTypeConfiguration<MyEntityTypeConfiguration>();

Finally, a simple mapping class might be:

public class MyEntityTypeConfiguration : IEntityTypeConfiguration<MyEntity>
{
public void Configure(EntityTypeBuilder<MyEntity> entityTypeBuilder)
{
entityTypeBuilder.ToTable("MyEntity");
entityTypeBuilder.Property(x => x.MyEntityId).HasColumnName("Id");
}
}

In case you’re interested, this feature is similar to the one being implemented for .NET Core, except that it doesn’t find mapping classes automatically. The IEntityTypeConfiguration<T> interface is exactly the same.

What’s New in Entity Framework Core 1.1

Introduction

Entity Framework Core 1.1 was released last November. With it, besides some bug fxes and semi-transparent improvements, came along a few goodies. If you read my previous post on features missing in Entity Framework Core 1.0, you’ll be please to know that a few have been addressed.

New API Methods

The Find method, for returning an entity from its primary key, is back (I had provided a workaround here):

var e1 = ctx.DbSet<MyEntity>().Find(1);

New is GetDatabaseValues, which goes to the database and fetches the current values for the current entity and primary key:

var dbProperties = ctx.Entry<MyEntity>(e).GetDatabaseValues();

Reload and Explicit Load

It is now again possible to reload an entity, causing it to be re-hydrated with the current values from the database, through the Reload method (also available as a workaround here):

ctx.Entry<MyEntity>(e).Reload();

And it is also possible to force load a not-loaded collection ():

ctx.Entry<MyEntity>(e).Collection(x => x.MyColl).Load();

As well as entity references (one-to-one, many-to-one):

ctx.Entry<MyEntity>(e).Reference(x => x.MyRef).Load();

Connection Resiliency

Connection resiliency made it way to version 1.1 as well:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer("my connection string", opt => opt.EnableRetryOnFailure());

base.OnConfiguring(optionsBuilder);
}

The EnableRetryOnFailure method is just a wrapper around ExecutionStrategy passing it SqlServerRetryingExecutionStrategy:

optionsBuilder
.ExecutionStrategy(x => new MyExecutionStrategy(x));

This one allows you to provide your own strategy for retries, by implementing IExecutionStrategy.

Configurable Change Tracking

Now, this is something that could have been really cool, but, as it is now, I find it somewhat crippled… you can now tell Entity Framework Core how should it find out if an entity has changed – the common change tracker functionality. But, the only supported techniques are the built-in (default, based on snapshots) or the use of INotifyPropertyChanged/INotifyCollectionChanged. This is not really that extensible, as you only have these two options. Here is how you configure it:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>()
.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangedNotifications);

base.OnModelCreating(modelBuilder);
}

If you want to use this approach, your entity must implement INotifyPropertyChanged and all of its collections must implement INotifyCollectionChanged. If any of the properties or collections in it changes, you must raise the PropertyChanged or CollectionChanged events, otherwise EF will not know that it is modified.

This can be set as the default for all entities, by the way:

modelBuilder
.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangedNotifications);

Using Fields

A most welcome addition, that was never previously available, is mapping to fields! This better supports a pure Domain Driven Design approach. It needs to be configured using code mapping:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<MyEntity>()
.Property(b => b.MyProp)
.HasField("_myField");

base.OnModelCreating(modelBuilder);
}

IEnumerable Collections

Another handy improvement is the ability to map collections declared as IEnumerable<T>, whereas in the past this was only possible for ICollection<T> (and derived classes, of course). The configuration is the same:

modelBuilder
.Entity<MyEntity>()
.HasMany(x => x.Children);

Of course, the concrete collection class must itself implement ICollection<T>, otherwise Entity Framework would have no way to populate it:

public class MyEntity
{
public IEnumerable<MyChild> Children { get; } = new HashSet<MyChild>();
}

Support for SQL Server In Memory Tables

In case you are using Hekaton, you can now tell Entity Framework that your entity is persisted as a memory-optimized table:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>()
.ForSqlServerIsMemoryOptimized();

base.OnModelCreating(modelBuilder);
}

Switching Services

Last, but not least, EF Core 1.1 makes it much easier to replace one of the services that EF uses internally:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.ReplaceService<IEntityStateListener, CustomEntityStateListener>();

base.OnConfiguring(optionsBuilder);
}

Conclusion

It’s nice to see things progressing, but there’s still a long way to go. In particular, as I said in my post about the missing features, there are quite a few features that still didn’t make it. In particular, I still miss:

  • Group By translation
  • Lazy loading
  • Date/Time operations
  • Support for custom SQL functions
  • Many-to-many relations
  • Command and query interception

We just have to wait for the next priorities of the team.