Tag Archives: DbContext

Unit Testing EF Core – How to Invoke the Contents of OnModelCreating

In this post, I wrote about how you can test an EF Database, using an InMemory database.

I’m guessing a few people reading this will be thinking this is stating the bloody obvious, but it certainly wasn’t to me. Imagine you have a DBContext with some seed data; for example:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {

            modelBuilder.Entity<EntityType1>().HasData(
                new EntityType1
                {
                    Id = "1",
                    Name = "Test1",
                    Description = "Testing",
                    IsTest = true,
                    SomeNumber = 1
                },

In the above linked article, this seed data will never fire; however, simply calling:

context.Database.EnsureCreated();

Once the context is created, will force the migrations to be run inside the in-memory instance, and you should end up with a system mirroring what you would see, should you run this migration against a physical database.

Caveat Emptor

When using this against EF Core 2.2 I found, what appeared to be, this issue. The error being, while I was trying to insert a record, an error returned saying that an item with the same key exists on the table. However, no such item should exist. The linked article seems to imply that this relates to a bug with the insert for the in-memory database, and that it is resolved for EF Core 3.0. I haven’t validated this, so it may, or may not work to upgrade to EF Core 3.0. Please add a comment if you can validate or negate this.

References

https://github.com/dotnet/efcore/issues/11666

https://github.com/dotnet/efcore/issues/6872

Add Entity Framework Core to an Existing Asp.Net Core Project

This is one of those posts born of the fact that I’m constantly googling this, and coming up with videos and Microsoft docs – I just want a single, quick step by step guide; so here it is.

Step One – NuGet packages

If you’re using SQL Server, then you’ll need these packages (technically, you only need the first, unless you want to actually create or run a migration yourself):

Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools

Step Two – Create DBContext (and entity classes if needed)

The minimum DBContext looks like this:

    public class MyDbContext : DbContext
    {

        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options)
        {

        }

        public DbSet<MyEntity> MyEntities { get; set; }
        public DbSet<MyOtherEntity> MyOtherEntities { get; set; }
    }

You can feed your model directly into EF. That’s much easier to do than it used to be, for example, any field called Id will automatically be inferred to be a Primary Key; however, at some point, you’re going to want to do something data specific to your model, and at that point, you lose the separation between data and business layers. A nice way that I’ve found around this is to subclass your models:

    public class MyEntity : MyModel
    {
    }

Step Three – Startup.cs

You’ll need to register the DbContext in your DI pipeline:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddDbContext<MyDbContext>(a =>
                a.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));
        }

Your connection string goes in appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "SqlConnectionString": "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

Step Four – Migration

Since you’ve installed EF Tools, you can add a migration:

Add-Migration InitialMigration -StartupProject MySolution.MyProject

The other option is to switch on automatic migrations; although, I would personally advise against that, as you can quickly lose track of what it’s doing.

That’s it – once you run:

Update-Database

You should have a working EF instance.

Step Five (optional) – Auto-Migration

You may also wish to have the system upgrade itself to the latest version of the DB. Whether you choose to do this depends on exactly how much control you need over the changes to your DB. I’ve heard that some people create the migrations as SQL Scripts and hand them to a DBA.

Should you wish to have EF manage that for you, then you can do the following:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            UpdateDatabase(app);

. . .

private void UpdateDatabase(IApplicationBuilder app)
        {
            using var serviceScope = app.ApplicationServices
                .GetRequiredService<IServiceScopeFactory>()
                .CreateScope();

            using var context = serviceScope.ServiceProvider.GetService<CourseSelectDbContext>();

            context.Database.Migrate();            
        }

That’s it – now, next time I want to find this, I can search my own website!

References

https://docs.microsoft.com/en-us/ef/core/get-started/install/

https://docs.microsoft.com/en-us/aspnet/core/data/ef-rp/intro?view=aspnetcore-3.1&tabs=visual-studio

https://blog.rsuter.com/automatically-migrate-your-entity-framework-core-managed-database-on-asp-net-core-application-start/

Manually Adding DbContext for an Integration Test

In EF Core, there is an extension method that allows you to add a DBContext, called AddDBContext. This is a really useful method, however, in some cases, you may find that it doesn’t work for you. Specifically, if you’re trying to inject a DBContext to use for unit testing, it doesn’t allow you to access the DBContext that you register.

Take the following code:

services.AddDbContext<MyDbContext>(options =>
                options.UseSqlServer());
         

I’ve previously written about using UseInMemoryDatabase. However, this article covered unit tests only – that is, you are able to instantiate a version of the DBContext in the unit test, and use that.

As a reminder of the linked article, if you were to try to write a test that included that DBContext, you might want to use an in memory database; you might, therefore, build up a DBContextOptions like this:

var options = new DbContextOptionsBuilder<MyDbContext>()
                .UseInMemoryDatabase(Guid.NewGuid().ToString())
                .EnableSensitiveDataLogging()
                .Options;
var context = new MyDbContext(options);

But in a scenario where you’re writing an integration test, you may need to register this with the IoC. Unfortunately, in this case, AddDbContext can stand in your way. The alternative is that you can simply register the DbContext yourself:

var options = new DbContextOptionsBuilder<MyDbContext>()
                .UseInMemoryDatabase(Guid.NewGuid().ToString())
                .EnableSensitiveDataLogging()
                .Options;
var context = new MyDbContext(options);
AddMyData(context);
services.AddScoped<MyDbContext>(_ => context);

AddMyData just adds some data into your database; for example:

private void AddTestUsers(MyDbContext context)
{            
    MyData data = new MyData()
    {
        value1 = "test",
        value2 = "1"                
    };
    context.MyData.Add(subject);
    context.SaveChanges();
}

This allows you to register your own, in memory, DbContext in your IoC.

Short Walks – Data Seeding in Entity Framework Core 2

Entity Framework Core 2.1 has a nice little feature for seed data. Previously, seeding data quite often involved a series of checks and potential for a script to exist that, if run on the wrong data, would repeatedly re-generate the same seed data.

In 2.1, you can simply override the OnModelCreating function of the data context, like so:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
 
    public DbSet<ResourceType> ResourceType { get; set; }
 
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
 
        modelBuilder.Entity<ResourceType>().HasData(new Data.ResourceType()
        {
            Id = 1,
            Name = "Web Site"                
        });
    }

And the framework will calculate whether or not this needs to run to put the data into the state that you’ve requested.

References

https://docs.microsoft.com/en-us/ef/core/modeling/data-seeding

Using Entity Framework with IoC

One thing to bear in mind about using entity framework is that the DbContext object is not thread safe. This threw me when I first discovered it; it also confused why I was getting this error, as I was running in an environment that I thought was pretty much single threaded (in fact, it was an Azure function). I was using IoC and so the DbContext is shared across instances.

Because the error is based on, effectively, a race condition, your mileage may vary, but you’ll typically get one of the following errors:

System.InvalidOperationException: ‘The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe.’

System.InvalidOperationException: ‘A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.’

I had a good idea what the cause of this might be, but in order to investigate, I set-up a console app accessing Entity Framework; in this case, EF Core.

If you’re here to find a solution, then you can probably scroll down to the section labelled To Fix.

Reproducing the error

To reproduce the error, we need to introduce Unity (I imagine the same would be true of any IoC provider, as the problem is more with the concept than the implementation):

Install-Package Unity

The next step is to abstract away the data access layer, in order to provide a base for our dependency injection:

As you can see, we’re introducing a data access layer – and we’re creating an interface for our DbContext. The idea being that this can subsequently be resolved by Unity. Here are our interfaces:

public interface IDataAccess
{
    List<string> GetData();
 
    void AddData(List<string> newData);
}

public interface IMyDbContext
{
    DbSet<MyData> MyData { get; set; }
 
    void SaveChanges();
}

public interface IDoStuff
{
    void DoStuffQuickly();
}

Finally, we can implement the interfaces:

public class DataAccess : IDataAccess
{
    private readonly IMyDbContext _myDbContext;
 
    public DataAccess(IMyDbContext myDbContext)
    {
        _myDbContext = myDbContext;
    }
    public void AddData(List<string> newData)
    {
        foreach (var data in newData)
        {
            MyData myData = new MyData()
            {
                FieldOne = data
            };
            _myDbContext.MyData.Add(myData);
        }
        _myDbContext.SaveChanges();
    }
 
    public List<string> GetData()
    {
        List<string> data = 
            _myDbContext.MyData.Select(a => a.FieldOne).ToList();
 
        return data;
    }
}

The DbContext:

public class MyDbContext : DbContext, IMyDbContext
{
    public DbSet<MyData> MyData { get; set; }
 
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        string cn = @"Server=.\SQLEXPRESS;Database=test-db . . .";
        optionsBuilder.UseSqlServer(cn);
 
        base.OnConfiguring(optionsBuilder);
    }
 
    void IMyDbContext.SaveChanges()
    {
        base.SaveChanges();
    }
}

Let’s register a class to actually start the process:

public class DoStuff : IDoStuff
{
    private readonly IDataAccess _dataAccess;
    private readonly ILogger _logger;
 
    public DoStuff(IDataAccess dataAccess, ILogger logger)
    {
        _dataAccess = dataAccess;
        _logger = logger;
    }
 
    public void DoStuffQuickly()
    {
        _dataAccess.AddData(new List<string>()
        {
            "testing new data",
            "second test",
            "last test"
        });
 
        Parallel.For(1, 100, (a) =>
        {
            List<string> data = _dataAccess.GetData();
            foreach(string text in data)
            {
                _logger.Log(text);
            }
        });
    }
}

Now let’s register the types in out IoC container:

static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
 
    UnityContainer container = new UnityContainer();
    container.RegisterType<IDataAccess, DataAccess.DataAccess>();
    container.RegisterType<IMyDbContext, MyDbContext>();
    container.RegisterType<IDoStuff, DoStuff>();
    container.RegisterType<ILogger, ConsoleLogger>();
 
    container.Resolve<IDoStuff>().DoStuffQuickly();
}

(Console logger is just a class that calls Console.WriteLine()).

Now, when you run it, you’ll get the same error as above (or something to the same effect).

To fix

One possible fix is to simply instantiate a DbContext as you need it; for example:

public List<string> GetData()
{
    using (var myDbContext = new MyDbContext())
    {
        List<string> data =
            myDbContext.MyData.Select(a => a.FieldOne).ToList();
        return data;
    }
    
}

However, the glaring problem that this creates is unit testing. The solution is a simple factory class:

public interface IGenerateDbContext
{
    IMyDbContext GenerateNewContext();
}

public class GenerateDbContext : IGenerateDbContext
{
    public IMyDbContext GenerateNewContext()
    {
        IMyDbContext myDbContext = new MyDbContext();
 
        return myDbContext;
    }
}

We’ll need to make the DbContext implementation disposable:

public interface IMyDbContext : IDisposable
{
    DbSet<MyData> MyData { get; set; }
 
    void SaveChanges();
}

And, finally, we can change the data access code:

public void AddData(List<string> newData)
{
    using (IMyDbContext myDbContext = _generateDbContext.GenerateNewContext())
    {
        foreach (string data in newData)
        {
            MyData myData = new MyData()
            {
                FieldOne = data
            };
            myDbContext.MyData.Add(myData);
        }
        myDbContext.SaveChanges();
    }
}
 
public List<string> GetData()
{
    using (IMyDbContext myDbContext = _generateDbContext.GenerateNewContext())
    {
        List<string> data =
            myDbContext.MyData.Select(a => a.FieldOne).ToList();
        return data;
    }
    
}

Now we can use IoC to generate the DBContext, and therefore it is testable, but we don’t pass the DbContext itself around, and therefore there are no threading issues.