EntityFramework.Docs icon indicating copy to clipboard operation
EntityFramework.Docs copied to clipboard

scaffolding change in the name of navigation property since dotnet 8.0 ?

Open shikamu opened this issue 1 year ago • 4 comments

I'm working on a asp.net core backend that was on .net 6 and we're trying to migrate it to .net 8.

We have noticed a change for which we haven't yet found documentation. Given a simple Test database with the following 3 tables:

CREATE TABLE [dbo].[ItemCategory] (
    [Name]                 VARCHAR (25)     NOT NULL,
    [Description]          NVARCHAR (512)   NULL,
    CONSTRAINT [PK_ItemCategory] PRIMARY KEY CLUSTERED ([Name] ASC),
);

CREATE TABLE [dbo].[Item] (
    [Name]                   VARCHAR (40)     NOT NULL,
    [Description]            NVARCHAR (512)   NULL,
    [CategoryName]           VARCHAR (25)     NOT NULL,
    CONSTRAINT [PK_Item] PRIMARY KEY CLUSTERED ([Name] ASC, [CategoryName] ASC),
    CONSTRAINT [FK_Item_ItemCategory] FOREIGN KEY ([CategoryName]) REFERENCES [dbo].[ItemCategory] ([Name]),
);

CREATE TABLE [dbo].[SomeTable] (
    [Id]                   INT              IDENTITY (1, 1) NOT FOR REPLICATION NOT NULL,
    [DetailItemName]         VARCHAR (40) NOT NULL,
    [DetailItemCategoryName] VARCHAR (25) NOT NULL,
    CONSTRAINT [PK_SomeTable] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_SomeTable_DetailItem] FOREIGN KEY ([DetailItemName], [DetailItemCategoryName]) REFERENCES [dbo].[Item] ([Name], [CategoryName])
);

I can use the following command to generate the c# classes:

dotnet ef dbcontext scaffold "Data Source=localhost\sqlexpress;Initial Catalog=Test;Integrated Security=True;MultipleActiveResultSets=True;TrustServerCertificate=True" Microsoft.EntityFrameworkCore.SqlServer -f

if I use net6.0, namely with exactly this csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.25">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.25" />
  </ItemGroup>

  <ItemGroup>
    <Folder Include="Models\" />
  </ItemGroup>

</Project>

I get the following output for the SomeTable C# class:

using System;
using System.Collections.Generic;

namespace ClassLibrary1
{
    public partial class SomeTable
    {
        public int Id { get; set; }
        public string DetailItemName { get; set; } = null!;
        public string DetailItemCategoryName { get; set; } = null!;

        public virtual Item DetailItem { get; set; } = null!;
    }
}

However, if I switch to net8.0, using exactly this csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
  </ItemGroup>

  <ItemGroup>
    <Folder Include="Models\" />
  </ItemGroup>

</Project>

I get the following output:

using System;
using System.Collections.Generic;

namespace ClassLibrary1;

public partial class SomeTable
{
    public int Id { get; set; }

    public string DetailItemName { get; set; } = null!;

    public string DetailItemCategoryName { get; set; } = null!;

    public virtual Item Item { get; set; } = null!;
}

Notice that with net6.0 and net7.0 we get this:

        public virtual Item DetailItem { get; set; } = null!;

but with net8.0 we get this:

    public virtual Item Item { get; set; } = null!;

When I run the scaffold command in both cases I get the same output in both cases: image

We have a lot of existing code and tables and we'd like to be able to use the "old naming scheme" if possible. Is there a way to do that ?

Thanks!

shikamu avatar Dec 28 '23 13:12 shikamu

Note for triage: this is a consequence of https://github.com/dotnet/efcore/pull/29771, which stopped using common prefixes to generate navigation names.

@shikamu Custom T4 templates can be used to customize the navigation names to whatever you choose.

ajcvickers avatar Jan 03 '24 18:01 ajcvickers

Note from triage: the new behavior is preferable to the old behavior, but for cases where the old behavior was reasonable we should document this as a breaking change.

ajcvickers avatar Jan 04 '24 09:01 ajcvickers

@ajcvickers thanks for the replies. Is there a ready to use T4 template that emulates the old behaviour ? this would be quite useful to have as this is essentially a breaking change, and we would then be able to get around it in an effortless fashion.

shikamu avatar Jan 04 '24 10:01 shikamu

@shikamu There is no specific template for this. Start from the default templates and edit the code that writes the navigation names.

ajcvickers avatar Jan 04 '24 10:01 ajcvickers