问题的一些背景:
我们最近从 2.2 升级到 .NET Core 3.1,并且还从 2.2 升级到 EF Core 3.1,并且很少有 API 出现故障或出现一些奇怪的问题,而这些问题是迄今为止才出现的。我需要帮助来理解这些问题以便解决它们。
需要注意的一些观察点:
描述
错误类型:SqlException 错误消息:无效的列名称“__roleName_0”
public async Task<List<UserView>> GetUsersAsync(string roleName, int offset, int? limit)
{
var query = _dbContext.UserView.AsNoTracking().Where(u => u.UserRole.Any(ur => ur.Role.Name == roleName));
var userResult = await query.ToListAsync();
return userResult;
}
正如您在
GetUsersAsync
函数中看到的,它有一个数据库操作:
这是从我们的日志中生成的用于以下操作的 SQL:
通过属于该角色的谓词过滤用户。
Executed DbCommand (177ms) [Parameters=[@__roleName_0='?' (Size = 4000)]
SELECT <ColumnNames>
FROM
(
SELECT <ColumnNames>
FROM [V_User] AS [v]
WHERE (EXISTS (
SELECT 1
FROM [UserRole] AS [u]
INNER JOIN [Role] AS [r] ON [u].[RoleId] = [r].[Id]
WHERE [v].[Id] = [u].[UserProfileId]) AND ([r].[Name] = @__roleName_0)
)
由于某种原因,EF Core 将参数 __roleName_0 视为列,这似乎是问题所在。我们尝试了多种资源,包括阅读 EF Core 3.0/3.1 中引入的重大更改,但无法了解此问题的原因是什么。请帮忙。
其他技术细节:
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.2">
<PackageReference Include="IdentityServer4.EntityFramework" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.2" />
Dockerfile:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.2 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY <ProjectName>/<ProjectName>.csproj <ProjectName>/
RUN dotnet restore <ProjectName>/<ProjectName>.csproj
COPY . .
WORKDIR /src/<ProjectName>
RUN dotnet build <ProjectName>.csproj -c Release -r ubuntu.16.04-x64 -f netcoreapp3.1 -o /app
FROM build AS publish
RUN dotnet publish <ProjectName>.csproj -c Release -r ubuntu.16.04-x64 -f netcoreapp3.1 --self-contained -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "<ProjectName>.dll"]
与 EF Core 2.2 相比,此行为可能与 EF Core 3.1 处理查询转换和参数命名的方式有关。您可以尝试的一种潜在解决方法是在构造查询时显式指定参数类型。以下是包含此更改的 GetUsersAsync 方法的更新版本:
public async Task<List<UserView>> GetUsersAsync(string roleName, int offset, int? limit)
{
var parameter = new SqlParameter("@roleName", roleName); // Explicitly specify parameter name and type
var query = _dbContext.UserView.AsNoTracking()
.FromSqlInterpolated($"SELECT * FROM UserView WHERE EXISTS (SELECT 1 FROM UserRole WHERE UserView.Id = UserRole.UserProfileId AND UserRole.RoleId = Role.Id AND Role.Name = {parameter})")
.Include(u => u.UserRole);
var userResult = await query.ToListAsync();
return userResult;
}
在此,我使用 FromSqlInterpolated 构建 SQL 查询,并使用 SqlParameter 显式指定参数名称和类型。当 EF Core 的查询翻译行为导致意外问题时,此方法有时会有所帮助。
我希望这能起作用。