efcore: As of EF7 Scaffold-DbContext doesn't handle columns computed from a function

Per the EF7 breaking changes I understand Computed Columns and tables with Triggers need to be specially configured. Using Scaffold-DbContext handles the triggers and computed columns in most cases.

The one case we’ve run into that doesn’t seem to be handles is a Computed Column that is generated from a function. i.e.

CREATE TABLE [dbo].[SomeTable](
        ...
	[totalCost]  AS ([dbo].[fn_calculateCost]([Id])),
        ...
)

We are scaffolding it like this:

Scaffold-DbContext "ConnectionString" Microsoft.EntityFrameworkCore.SqlServer -StartupProject Infrastructure.Data -Project Infrastructure.Data -OutputDir Scheduling -Context SchedulingContext -Tables SomeTable -Force -NoOnConfiguring -NoPluralize

Which generates this:


    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
		modelBuilder.Entity<SomeTable>(entity =>
        {
            ...
            entity.Property(e => e.TotalCost)
                .HasComputedColumnSql("([dbo].[fn_calculateCost]([Id]))", false)
                .HasColumnType("decimal(19, 4)")
                .HasColumnName("totalUnits");
			...
        });
		
        OnModelCreatingPartial(modelBuilder);
    }

The exception is: Column ‘inserted.totalCost’ cannot be referenced in the OUTPUT clause because the column definition contains a subquery or references a function that performs user or system data access. A function is assumed by default to perform data access if it is not schemabound. Consider removing the subquery or function from the column definition or removing the column from the OUTPUT clause.

The only way I have determined to work around this for now is to manually add “Triggers” to the EF Context Mapping for the functions used by the computed columns (per this post):

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<SomeTable>(entity =>
            {
                entity.ToTable(tbl => tbl.HasTrigger("([dbo].[fn_calculateCost]([Id]))"));
            });
        }

But this solution is not ideal. Not only does it only solve this singular issue, leaving other potential issues out there, but if we ever change the column/function we would have to remember to manually go in and update this line.

Ideally, it would be nice if the Scaffold-DbContext command picked up this change like it does so well for everything else.

EF Core version: 7.0.5 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .NET 6.0 Operating system: Windows 10/Server 2016 IDE: Visual Studio 2022 17.4.5

Thank you, Jeff

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 15 (10 by maintainers)

Most upvoted comments

To me, using the partial seems like the safer option as I don’t include another set of code (the T4 templates) that I would need to maintain with each version upgrade. Or am I missing something here that would make the T4 template a superior (more dynamic choice)?

I’d generally agree with this. Note that if you want to do this systematically for all tables, the code you add to the partial method can do bulk configuration rather than specify each table (which you then need to maintain).

I’m going to go ahead and close this issue as I think all questions have been answered, but if you have any further one feel free to post back here.

Ah, right: IIRC we make no attempts to detect what’s going on inside computed columns, and never scaffold HasTrigger because of computed columns - only because of triggers.