Current Limitations of Entity Framework Core

Introduction

Although EF Core seems to be the most popular ORM in the .NET world in these days – and I for sure won’t contradict it –, there are still some functionality missing, specially if we compare it with other ORMs that also exist, NHibernate coming obviously to my mind. This post is a reflection about that, it is in no way a comparison with other ORMs, in particular, NHibernate, it’s more a wish list for EF Core. I still follow and love NHibernate, and it’s still, in many ways, way ahead of EF Core, but that’s not what I’ll be writing about.

Primary Key Generation

EF Core currently only supports the following primary key generation strategies:

  • Identity/autoincrement (on databases that support it)
  • GUID/uniqueidentifier (on databases that support it)
  • Sequence-based (on databases that support it)
  • HiLo (SQL Server only)
  • Manually assigned

For GUIDs, EF Core automatically generates, on the client-side, a new value before inserting, which is nice.

HiLo is a great pattern for generating primary keys, but, unfortunately, the way Microsoft implemented it, resorting to database sequences, makes it database-specific – currently, SQL Server only. If it were implemented with just standard DML commands, it could be made database-agnostic, for the benefit of all.

I dare say that EF Core is slightly biased towards SQL Server and, even more, that most SQL Server folks are used to having IDENTITY as their default primary key generation strategy. While this is fine, it does prevent batch insertions, because it requires obtaining the generated primary key after each inser (SELECT SCOPE_IDENTITY()). If we have a client-generated primary key, batch insertion is possible, which can be a great benefit.

I’d like to have a truly extensible primary key generation mechanism on EF Core. Maybe something for the future.

Collection Types

Currently, EF Core only supports one type of collection. It can be lazy loaded, in which case, you need to declare it as ICollection<T> or IList<T>, or not, but that is it. You can’t have collections of primitive types without using a value converter. Plus, you don’t have semantic collections such as sets or bags, or indexed collections such as lists or maps.

Table Inheritance Patterns

There are three canonical table inheritance patterns:

Currently, TPH and TPT are already supported, but TPC is not. There are plans to introduce it in the future (https://github.com/dotnet/efcore/issues/3170)

Expression Columns

Currently there’s no way to map a column to a SQL expression, which could be very handly. True, we can have a whole entity coming as the result of a view, or a raw SQL, but this is a different thing. Of course, that property would not be persisted.

Expressions for CUD Operations

Another useful operation, the ability to use raw SQL – which could even be a stored procedure call – for Create, Update, and Delete (CUD) operations. This is also currently scheduled by the team for a future release (https://github.com/dotnet/efcore/issues/245).

Strongly Typed DML Operations

One thing that would be nice and already exists on other ORMs is the ability to batch perform DML operations from LINQ queries, for example, UPDATEs and DELETEs. Take this hypothetical strongly typed delete on top of a LINQ query, for example:

var deletedPosts = ctx.Posts.Where(p => p.Timestamp.Date < DateTime.Today.AddDays(-365)).Delete();

Much better than writing SQL as hardcoded strings, don’t you think? Yes, I do know that there are third-party extensions out there that already do this, but it would be nice to have this built-in. It’s being tracked for future implementation (https://github.com/dotnet/efcore/issues/795).

Lifecycle Events

Some lifecycle events – such as EF 6’s ObjectMaterialized and SavingChanges – are conspicuously missing. This can be useful, for example, to do something on an entity after it is loaded (hydrated) from the database. Fortunately, there are plans to introduce them (https://github.com/dotnet/efcore/issues/15911).

Conclusion

As a I said, these are just some thoughts on nice-haves for EF Core. Some of them are already on the EF Core team’s list, and some are not. There are many more, see the whole plan for v7.0 here and do submit your own suggestions here. As usual, I’d love to hear your thoughts on this, so let’s have them!

Changing Schema Dynamically in EF Core

Sometimes it may be necessary to change the schema for some entities based upon some criteria. This may be because of multitenancy or because you want to test something and don’t want to pollute the main schema. Here is a possible solution, going directly to the annotations that EF Core uses.

protected override void OnModelCreating(ModelBuilder modelBuilder) {

    base.OnModelCreating(modelBuilder);

//apply any IEntityTypeConfiguration<TEntity> configuration from this assembly

    modelBuilder.ApplyConfigurationsFromAssembly(typeof(DatabaseContext).Assembly);

    var model = modelBuilder.Model;

//possibly filter entities by some criteria?

    foreach (var entity in model.GetEntityTypes())

    {

        entity.RemoveAnnotation(“Relational:Schema”);

        entity.AddAnnotation(“Relational:Schema”, “FooBar”);

    } }

You need to override the OnModelCreating method, which is called by the infractructure when the model is being built by EF Core, and you let it do its job, and you then iterate through the found entities to add an annotation for “Relational:Schema”. And that’s it.

ASP.NET Core OData Part 1

Update: see the second post here.

Introduction

OData is an open standard for making an object-oriented domain model available as an HTTP REST interface.In a nutshell, it provides a specification for returning domain models as result of HTTP requests, querying them over the URL and even creating functions and actions over the domain model. In what .NET Core is concerned, it is a way by which you can expose your Entity Framework Core – or any other ORM that has a LINQ interface – to the web, without writing too much boilerplate code, as an ASP.NET Core Web API.

It is surprising that not many people know about OData, also, there are some caveats to it and not much documentation on using it with ASP.NET Core, so I decided to write a few posts on the subject. Here is the first!

Setting Up

Let’s pretend you have a domain model with just two classes, Parent and Child:

image

We should also have an Entity Framework Core context that exposes them:

public class ParentChildContext : DbContext

{

public ParentChildContext(DbContextOptions options) : base(options) { }

public DbSet<Parent> Parents { get; set; }

public DbSet<Child> Children { get; set; }

}

I won’t go into details as to explain this, I’m pretty sure you all know about Entity Framework Core contexts! Just make sure you add a reference to the Microsoft.EntityFrameworkCore NuGet package, or, if you’re using SQL Server, Microsoft.EntityFrameworkCore.SqlServer. Now register your context to the DI framework, in the ConfigureServices method:

services.AddDbContext<ParentChildContext>(options =>

{

options.UseSqlServer(“<connection string>”);

});

In your ASP.NET Core’s project you need to add a reference to the Microsoft.AspNetCore.OData NuGet package. This includes the server-side implementation of OData version 4 for ASP.NET Core.

You need to add its required services in the ConfigureServices method:

services.AddOData();

This registers the services, but now we need to add an endpoint for an actual domain model. ASP.NET Core OData now supports endpoint routing, so everything can be done smoothly:

app.UseEndpoints(endpoints =>

{ endpoints.MapODataRoute(“odata”, “odata”, GetEdmModel(app.ApplicationServices)); });

We registered this endpoint with name odata (first parameter) and also with the same prefix (second parameter), you can happily change this. As you can see, in this route, we are returning an EDM Data Model. We build one as this:

private static IEdmModel GetEdmModel(IServiceProvider serviceProvider)

{

var builder = new ODataConventionModelBuilder(serviceProvider);

builder.EntitySet<Parent>(“Parents”);

builder.EntitySet<Child>(“Children”);

return builder.GetEdmModel();

}

One thing that you’ll notice is that this is a conventions-based model builder, ODataConventionModelBuilder. this one takes care of some things for you, such as inferring the id property for each entity, that’s why you don’t have to explicitly state it. If your entity has an id property with a funny name, other than Id or EntityId, then you will need to specify it:

builder

.EntitySet<Parent>(“Parents”)

.EntityType

.HasKey(x => x.Id);

There is no need to specify the other properties, because they are all inferred automatically too from the generic parameter.

Do keep in mind the entity set name that you give, it is common to have a pluralized form of the entity name, but it is not required.

Now we need to create a controller that exposes this. At the very least, we will need two methods:

[ODataRoutePrefix(“Parents”)]

public class ParentController : ODataController

{

private ParentChildContext _ctx;

public ParentController(ParentChildContext ctx)

{

this._ctx = ctx;

}

[ODataRoute]

public IQueryable<Parent> Get()

{

return this._ctx.Parents.AsQueryable();

}

[ODataRoute(“{id}”)]

public Parent Get([FromODataUri] int id)

{

return this._ctx.Parents.Find(id);

}

}

This controller is specific to the Parents entity set, so, if you wish, you need to have another one for the Children. This is specified in the [ODataRoutePrefix] attribute.

Also notice how we are returning IQueryable<Parent> from the Get action method that does not take parameters and a single Parent from the other. You can also declare IActionResult or ActionResult<T>:

[ODataRoute]

public IActionResult Get()

{

return this.Ok(this._ctx.Parents.AsQueryable());

}

//alternative

[ODataRoute]

public ActionResult<IQueryable<Parent>> Get()

{

return this.Ok(this._ctx.Parents.AsQueryable());

}

The latter, the one that uses ActionResult<T>, has some advantages, which you can read about here.

And, of course, all of the methods can be made asynchronous too:

[ODataRoute]

public async Task<IQueryable<Parent>> Get()

{

return await this._ctx.Parents.ToListAsync();

}

[ODataRoute(“{id}”)]

public async Task<Parent> Get([FromODataUri] int id)

{

return await this._ctx.Parents.FindAsync(id);

}

We will see a problem with the implementation of the first method in the next post.

Now, the first method, the one without parameters, returns all of the entities in the database, and the second, as one would expect, returns possibly one from its id property.

Conclusion

We’re all done here, so we can now access the endpoints we just created, try to navigate to the following URLs:

  • /odata: returns information about the exposed entity sets (Parents and Children)

image

  • /odata/parents –> ParentController.Get(): returns all records for the Parent entity
  • /odata/parents(1) –> ParentController.Get(1): returns possibly one record, if it exists in the database for the given primary key of the Parent entity

This is the very basics, in the future posts we will explore other options, such as querying over the URL and creating functions and actions.

References

You can find more information in the following pages:

Accessing the HttpContext from a DbContext

Sometimes it might be necessary to access the current HttpContext from inside a DbContext, namely, from inside the OnConfiguring or OnModelCreating methods. Why? Well, for once, because of multitenancy: we may want to be able to decide the connection string to use based on the requesting or the host’s domain, the current user or some other request parameter. Here the internal dependency injection (Instance) can’t help, because it cannot be used inside these methods.

The only option we have is to inject the IHttpContextAccessor class through our DbContext class’ constructor, and, from it, get hold of the HttpContext.

First we need to register the IHttpContextAccessor service in ConfigureServices:

services.AddHttpContextAccessor();

We also need to register our context for dependency injection:

services.AddDbContext<MyContext>();

A context registered this way needs to have a special constructor that has a parameter of type DbContextOptions (or DbContextOptions<MyContext>), in our case, we just need to add an extra parameter of type IHttpContextAccessor:

public class MyContext : DbContext
{
    public MyContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options)
    {
        this.HttpContextAccessor = httpContextAccessor;
    }

    protected IHttpContextAccessor HttpContextAccessor { get; }
}

Finally, when you need to access the HttpContext, you just need to retrieve it from the IHttpContextAccessor:

protected internal override void OnConfiguring(DbContextOptionsBuilder builder)
{
    var httpContext = this.HttpContextAccessor.HttpContext;
    var tenantService = httpContext.RequestServices.GetService<ITenantService>();
    var connectionString = tenantService.GetConnectionString(httpContext);

    builder.UseSqlServer(connectionString);

    base.OnConfiguring(builder);
}

Here the hypothetical ITenantService provides a method GetConnectionString that returns a connection string for the current HttpContext, and we use it with the SQL Server provider.

Hope you find this useful! Winking smile

Succinctly Series Readers Awards

image

My e-book Entity Framework Core Succinctly was silver winner on the Succinctly Series Readers Awards!

Many thanks to all who voted for it! And congratulations to Joseph D. Booth for winning the gold award for his Natural Language Processing Succinctly and to Alessando Del Sole (@progalex) for his bronze award for Xamarin.Forms Succinctly!

https://www.syncfusion.com/awards/succinctlyseries/2018succinctlyreadersawards

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.