Skip to content

fix: Resolve context disposed exception in MsSqlContextFactory and AspNetCoreSample#336

Open
benedict-odonovan wants to merge 1 commit intoVahidN:masterfrom
benedict-odonovan:fix_design_time_factory
Open

fix: Resolve context disposed exception in MsSqlContextFactory and AspNetCoreSample#336
benedict-odonovan wants to merge 1 commit intoVahidN:masterfrom
benedict-odonovan:fix_design_time_factory

Conversation

@benedict-odonovan
Copy link
Copy Markdown

Hi, I'm looking at adding a custom provider but discovered an issue with the sample/example site while trying to build the project locally.

I believe when the project was updated to support .Net 10 various usings were added that may have broken the migration scripts. This PR removes the offending using from MSSqlContextFactory and regenerates the migrations so AspNetCoreSample can be run up.

PR Checklist

  • The commit message follows Conventional Commits
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

  • Bugfix

What is the current behavior?

MsSqlContextFactory implements IDesignTimeDbContextFactory<ApplicationDbContext>, which
EF Core's design-time tooling (e.g. dotnet ef migrations add, dotnet ef database update)
uses to create a DbContext instance when no application entry point is available.

The factory builds a full ServiceCollection — including AddEFSecondLevelCache and
AddConfiguredMsSqlDbContext — then resolves ApplicationDbContext from it. Because
ApplicationDbContext is registered with a scoped lifetime, using var buildServiceProvider
disposes the ServiceProvider (and all its scoped services) before the returned context can
be used. This causes an ObjectDisposedException when running EF design-time commands.

Closes #

What is the new behavior?

Removed using from var buildServiceProvider = services.BuildServiceProvider() so the
ServiceProvider is not eagerly disposed before the ApplicationDbContext is returned to
the EF tooling. The SQL Server migrations are also regenerated as a result of the fix
allowing the tooling to run successfully.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

N/A

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request upgrades the test data layer to Entity Framework Core 10.0.0, updates the migration scripts, and marks several entity properties as required. It consolidates the previous migration history into a single new migration, which is noted as a breaking change for existing databases that may require documentation or a different approach to maintain synchronization. Additionally, the service provider initialization in MsSqlContextFactory.cs was modified to remove a using declaration.

Comment on lines +1 to +317
using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional

namespace EFCoreSecondLevelCacheInterceptor.Tests.DataLayer.Migrations
{
/// <inheritdoc />
public partial class V_04_2026_1102 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "BlogData",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false),
SiteUrl = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
});

migrationBuilder.CreateTable(
name: "Blogs",
columns: table => new
{
BlogId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Url = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Blogs", x => x.BlogId);
});

migrationBuilder.CreateTable(
name: "DateTypes",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
AddDate = table.Column<DateTime>(type: "datetime2", nullable: true),
UpdateDate = table.Column<DateTime>(type: "datetime2", nullable: false),
AddDateValue = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
UpdateDateValue = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
RelativeAddTimeValue = table.Column<TimeSpan>(type: "time", nullable: true),
RelativeUpdateTimeValue = table.Column<TimeSpan>(type: "time", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_DateTypes", x => x.Id);
});

migrationBuilder.CreateTable(
name: "EngineVersions",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Commercial_Major = table.Column<int>(type: "int", nullable: false),
Commercial_Minor = table.Column<int>(type: "int", nullable: false),
Commercial_Revision = table.Column<int>(type: "int", nullable: false),
Commercial_Patch = table.Column<int>(type: "int", nullable: false),
Retail_Major = table.Column<int>(type: "int", nullable: false),
Retail_Minor = table.Column<int>(type: "int", nullable: false),
Retail_Revision = table.Column<int>(type: "int", nullable: false),
Retail_Patch = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_EngineVersions", x => x.Id);
});

migrationBuilder.CreateTable(
name: "Tags",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Tags", x => x.Id);
});

migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
UserStatus = table.Column<int>(type: "int", nullable: false),
ImageData = table.Column<byte[]>(type: "varbinary(max)", nullable: false),
AddDate = table.Column<DateTime>(type: "datetime2", nullable: true),
UpdateDate = table.Column<DateTime>(type: "datetime2", nullable: true),
Points = table.Column<long>(type: "bigint", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false),
ByteValue = table.Column<byte>(type: "tinyint", nullable: false),
CharValue = table.Column<string>(type: "nvarchar(1)", nullable: false),
DateTimeOffsetValue = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
DecimalValue = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
DoubleValue = table.Column<double>(type: "float", nullable: false),
FloatValue = table.Column<float>(type: "real", nullable: false),
GuidValue = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TimeSpanValue = table.Column<TimeSpan>(type: "time", nullable: true),
ShortValue = table.Column<short>(type: "smallint", nullable: false),
ByteArrayValue = table.Column<byte[]>(type: "varbinary(max)", nullable: false),
UintValue = table.Column<long>(type: "bigint", nullable: false),
UlongValue = table.Column<decimal>(type: "decimal(20,0)", nullable: false),
UshortValue = table.Column<decimal>(type: "decimal(20,0)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});

migrationBuilder.CreateTable(
name: "Posts",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: false),
UserId = table.Column<int>(type: "int", nullable: false),
BlogId = table.Column<int>(type: "int", nullable: false),
post_type = table.Column<string>(type: "nvarchar(13)", maxLength: 13, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Posts", x => x.Id);
table.ForeignKey(
name: "FK_Posts_Blogs_BlogId",
column: x => x.BlogId,
principalTable: "Blogs",
principalColumn: "BlogId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Posts_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});

migrationBuilder.CreateTable(
name: "Products",
columns: table => new
{
ProductId = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ProductNumber = table.Column<string>(type: "nvarchar(30)", maxLength: 30, nullable: false),
ProductName = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Notes = table.Column<string>(type: "nvarchar(max)", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false),
UserId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Products", x => x.ProductId);
table.ForeignKey(
name: "FK_Products_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});

migrationBuilder.CreateTable(
name: "TagProducts",
columns: table => new
{
TagId = table.Column<int>(type: "int", nullable: false),
ProductProductId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TagProducts", x => new { x.TagId, x.ProductProductId });
table.ForeignKey(
name: "FK_TagProducts_Products_ProductProductId",
column: x => x.ProductProductId,
principalTable: "Products",
principalColumn: "ProductId",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_TagProducts_Tags_TagId",
column: x => x.TagId,
principalTable: "Tags",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});

migrationBuilder.InsertData(
table: "Blogs",
columns: new[] { "BlogId", "Url" },
values: new object[,]
{
{ 1, "https://site1.com" },
{ 2, "https://site2.com" }
});

migrationBuilder.InsertData(
table: "Tags",
columns: new[] { "Id", "Name" },
values: new object[,]
{
{ 1, "Tag4" },
{ 2, "Tag1" },
{ 3, "Tag2" },
{ 4, "Tag3" }
});

migrationBuilder.InsertData(
table: "Users",
columns: new[] { "Id", "AddDate", "ByteArrayValue", "ByteValue", "CharValue", "DateTimeOffsetValue", "DecimalValue", "DoubleValue", "FloatValue", "GuidValue", "ImageData", "IsActive", "Name", "Points", "ShortValue", "TimeSpanValue", "UintValue", "UlongValue", "UpdateDate", "UserStatus", "UshortValue" },
values: new object[] { 1, null, new byte[] { 1, 2 }, (byte)1, "C", null, 1.1m, 1.3, 1.2f, new Guid("236bbe40-b861-433c-8789-b152a99cfe3e"), new byte[0], true, "User1", 1000L, (short)2, null, 1L, 1m, null, 0, 1m });

migrationBuilder.InsertData(
table: "Posts",
columns: new[] { "Id", "BlogId", "Title", "UserId", "post_type" },
values: new object[,]
{
{ 1, 1, "Post1", 1, "post_base" },
{ 2, 1, "Post2", 1, "post_base" }
});

migrationBuilder.InsertData(
table: "Products",
columns: new[] { "ProductId", "IsActive", "Notes", "ProductName", "ProductNumber", "UserId" },
values: new object[,]
{
{ 1, false, "Notes ...", "Product4", "004", 1 },
{ 2, true, "Notes ...", "Product1", "001", 1 },
{ 3, true, "Notes ...", "Product2", "002", 1 },
{ 4, true, "Notes ...", "Product3", "003", 1 }
});

migrationBuilder.InsertData(
table: "TagProducts",
columns: new[] { "ProductProductId", "TagId" },
values: new object[,]
{
{ 1, 1 },
{ 2, 2 },
{ 3, 3 },
{ 4, 4 }
});

migrationBuilder.CreateIndex(
name: "IX_Posts_BlogId",
table: "Posts",
column: "BlogId");

migrationBuilder.CreateIndex(
name: "IX_Posts_UserId",
table: "Posts",
column: "UserId");

migrationBuilder.CreateIndex(
name: "IX_Products_ProductName",
table: "Products",
column: "ProductName",
unique: true);

migrationBuilder.CreateIndex(
name: "IX_Products_UserId",
table: "Products",
column: "UserId");

migrationBuilder.CreateIndex(
name: "IX_TagProducts_ProductProductId",
table: "TagProducts",
column: "ProductProductId");

migrationBuilder.CreateIndex(
name: "IX_TagProducts_TagId",
table: "TagProducts",
column: "TagId");
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "BlogData");

migrationBuilder.DropTable(
name: "DateTypes");

migrationBuilder.DropTable(
name: "EngineVersions");

migrationBuilder.DropTable(
name: "Posts");

migrationBuilder.DropTable(
name: "TagProducts");

migrationBuilder.DropTable(
name: "Blogs");

migrationBuilder.DropTable(
name: "Products");

migrationBuilder.DropTable(
name: "Tags");

migrationBuilder.DropTable(
name: "Users");
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Deleting all previous migration files and replacing them with a single regenerated migration is a breaking change for any existing databases that have already applied the previous migrations. This will cause the __EFMigrationsHistory table to be out of sync with the codebase. If this repository is intended to support persistent databases, consider maintaining the migration history or explicitly documenting this as a breaking change in the release notes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've restored the old migration files and added a new migration file to bring the model snapshot up to date.

@benedict-odonovan benedict-odonovan force-pushed the fix_design_time_factory branch from a964b6d to b0c9888 Compare April 7, 2026 12:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant