簡體   English   中英

如何解析 EF Core 3 查詢中的 int?

[英]How to Parse an int in an EF Core 3 Query?

升級到 EF Core 3 后,我在以下代碼中收到以下錯誤:

System.InvalidOperationException: '無法翻譯 LINQ 表達式 'DbSet .Max(c => Convert.ToInt32(c.ClaimNumber.Substring(c.ClaimNumber.Length - 6)))'。 以可翻譯的形式重寫查詢,或通過插入對 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的調用顯式切換到客戶端評估。 有關詳細信息,請參閱https://go.microsoft.com/fwlink/?linkid=2101038

var maxId = Db.Claims
    .Select(c => c.ClaimNumber.Substring(c.ClaimNumber.Length - 6))
    .Max(x => Convert.ToInt32(x));

我也嘗試過使用 int.Parse 而不是 Convert.ToInt32,它會產生相同的錯誤。 我理解錯誤信息。 但是,讓 SQL Server 使用 CAST 或 CONVERT 將 T-SQL 中的字符串解析為 int 是微不足道的,我希望有一種簡單的方法來編寫查詢,以便將其轉換為服務器端操作,對嗎?

更新在克勞迪奧的出色回答之后,我想我應該為下一個出現的人添加一些信息。 我認為解析是上述代碼的問題的原因是因為以下運行沒有錯誤並產生正確的結果:

var maxId = Db.Claims
    .Select(c => c.ClaimNumber.Substring(c.ClaimNumber.Length - 6))
    .AsEnumerable()
    .Max(x => int.Parse(x));

但是,我深入挖掘,發現這是 EF 正在從該代碼執行的 SQL 查詢:

SELECT [c].[ClaimNumber], CAST(LEN([c].[ClaimNumber]) AS int) - 6
FROM [Claims] AS [c]
WHERE [c].[ClaimNumber] IS NOT NULL

顯然沒有像我想要的那樣做任何事情,因此,克勞迪奧是正確的,對Substring的調用實際上是問題所在。

免責聲明:雖然可行,但我強烈建議您不要在查詢中使用類型轉換,因為會導致查詢性能嚴重下降。

事實是Convert.ToInt(x)部分不是這里的問題。 這是c.ClaimsNumber.Substring(c.ClaimNumber.Length - 6) ,EF Core 翻譯器無法在 T-SQL 中進行翻譯。

盡管 Sql Server 中存在RIGHT函數,但您將無法在當前版本的 EF Core 中使用它(我正在編寫的最新版本是 3.1.2)。 獲得所需內容的唯一解決方案是創建一個 Sql Server 用戶函數,將其與 EF Core 映射並在查詢中使用它。

1) 通過遷移創建函數

> dotnet ef migrations add CreateRightFunction

在新創建的遷移文件中放置以下代碼:

public partial class CreateRightFunctions : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql(@"
CREATE FUNCTION fn_Right(@input nvarchar(4000), @howMany int)
RETURNS nvarchar(4000)
BEGIN
RETURN RIGHT(@input, @howMany)
END
");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql(@"
DROP FUNCTION fn_Right
");
    }
}

然后運行數據庫更新:

dotnet ef database update

2) 將函數映射到 EF Core 上下文

在您的上下文類 [DbFunction("fn_Right")]

public static string Right(string input, int howMany)
{
    throw new NotImplementedException(); // this code doesn't get executed; the call is passed through to the database function
}

3) 在查詢中使用函數

var maxId = Db.Claims.Select(c => MyContext.Right(c.ClaimNumber, 6)).Max(x => Convert.ToInt32(x));

生成的查詢:

SELECT MAX(CONVERT(int, [dbo].[fn_Right]([c].[ClaimNumber], 6)))
FROM [Claims] AS [c]

同樣,這遠非最佳實踐,我認為您應該考慮在表中添加一個 int 列來存儲這個“數字”,無論它在您的域中代表什么。

此外,第一次 ClaimNumber 的最后 6 個字符包含非數字字符,這將不再起作用。 如果 ClaimNumber 是由人類輸入的,遲早會發生這種情況。

您應該編碼和設計您的數據庫和應用程序以獲得健壯性,即使您非常確定這 6 個字符將始終代表一個數字。 他們不能永遠這樣做:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM