Entity Framework Core 7.0.2 for SQLite 返回 null,尽管数据存在?

huangapple go评论73阅读模式
英文:

Why Entity Framework Core 7.0.2 for SQLite is returning nulls even tho the data exists?

问题

我一直在努力弄清楚为什么在我使用Id(当前为GUID)时EF6找不到记录,即使记录存在也是如此。

在SQLite数据库中,所需字段是一个字符串,而在模型中是GUID。让我分享我的一部分代码,以使事情更清晰:

用户模型:

public class User
{
    public Guid Id { get; set; } //Todo: 根据Id筛选用户
    public string Password { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public DateTime CreationDate { get; set; } = DateTime.UtcNow;
    public int AccountActive { get; set; } = 1;
    public Role Role { get; set; } = new Role();
}

我遇到问题的仓库中的方法:

public User GetUser(string Id)
{
    if(Guid.TryParse(Id, out Guid result))
    {
        var user = _context.Users.Include(x => x.Role).SingleOrDefault(u => u.Id == result);
        if(user != null)
            return user;
    }

    throw new Exception("找不到用户!");
}

在我看来,EFCore似乎无法将GUID解析为字符串以执行正确的查询。为了查看查询的情况,我尝试将查询输出到Debug,通过在appsettings.Development.json中添加一个新的字符串,如下所示:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "Default": "Data Source=Database/PTasks.db"
  }
}

输出显示了更多信息,但即使添加了启用敏感数据记录的选项optionsBuilder.UseSqlite(connectionString).EnableSensitiveDataLogging(true),仍然未显示查询。

到目前为止,我不知道发生了什么,因为这个查询一直在SQL Server上为我工作,所以我倾向于认为这可能与EF6和SQLite的使用有关。正如您所见,我一直在尝试检查查询,但没有运气,所以现在我没有任何想法。

当我调试该方法时,即使Id参数正确解析为Guid并且Id存在于数据库中,var user = _context.Users.Include(x => x.Role).SingleOrDefault(u => u.Id == result); 最终为null。

只是为了添加一些额外的信息,我刚刚更改了方法以使用RawSql,并返回了预期的数据,但我真的想了解发生了什么。

这是新版本:

public User GetUser(string Id)
{
    if(Guid.TryParse(Id, out Guid result))
    {
        //var user = _context.Users.Include(x => x.Role).SingleOrDefault(u => u.Id == result);
        var user = _context
             .Users
             .FromSqlRaw($"select * from Users where Id = '{Id}'")
             .SingleOrDefault();

        if(user != null)
            return user;
    }

    throw an Exception("找不到用户!");
}

当然,它缺少与Roles表的连接,但这与问题无关。

英文:

I have been having a hard time trying to figure out why EF6 is not finding a record when I use the Id (which is currently a GUID) even tho the record exists.

The required field is as a String inside the SQLite database and in the model is a GUID. Let me share part of my code to make things more clear:

User Model:

public class User
{
    public Guid Id { get; set; } //Todo: Filter users by the Id
    public string Password { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public DateTime CreationDate { get; set; } = DateTime.UtcNow;
    public int AccountActive { get; set; } = 1;
    public Role Role { get; set; } = new Role();
}

The method inside the repo that I'm having problems with:

public User GetUser(string Id)
{
    if(Guid.TryParse(Id, out Guid result))
    {
        var user = _context.Users.Include(x => x.Role).SingleOrDefault(u => u.Id == result);
        if(user != null)
            return user;
    }

    throw new Exception("User not found!");
}

To me, it looks like EFCore is not able to parse the GUID back to a string to do the correct query. Trying out to see what is going on on the query side I was trying to output the query to the Debug by adding a new string to the appsettings.Development.json like this:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "Default": "Data Source=Database/PTasks.db"
  }
}

The Output is showing much more information but no queries are been shown even after adding the option for sensitive data logging with optionsBuilder.UseSqlite(connectionString).EnableSensitiveDataLogging(true)

So far I have no clues on what is going on since this query has always worked for me over SQL Server, so I tend to think that this might be an issue with the use of EF6 and SQLite. As you can see I have been trying to inspect the Query but with no luck, so right now I'm out of ideas.

When I debug the method the var user = _context.Users.Include(x => x.Role).SingleOrDefault(u => u.Id == result); ends up as null even if the Id param was correctly parsed as a Guid and the Id exists in the database.

Just to add some extra information, I just changed the method to use RawSql and it is returning the expected data, but I really want to understand what is going on.

This is the new version:

public User GetUser(string Id)
    {
        if(Guid.TryParse(Id, out Guid result))
        {
            //var user = _context.Users.Include(x => x.Role).SingleOrDefault(u => u.Id == result);
            var user = _context
                 .Users
                 .FromSqlRaw($"select * from Users where Id = '{Id}'")
                 .SingleOrDefault();

            if(user != null)
                return user;
        }

        throw new Exception("User not found!");
    }

Of course, it is missing the join to the Roles table but that is not relevant to the issue.

答案1

得分: 0

So... 我刚刚发现这确实是一个 Bug,自 2018 年以来一直存在。问题出现在当表中有一个主键 PK,而该主键是一个 GUID 时,当尝试按照主键进行过滤时,Entity Framework 不会将 Guid 转换为字符串,因此基本上查询将出错。

解决方法很简单,但不是非常“优雅”:

public User GetUser(string Id)
{
    if (Guid.TryParse(Id, out Guid result))
    {
        var user = _context
            .Users
            .Include(x => x.Role)
            .Single(u => u.Id.ToString().Equals(result.ToString()));

        if (user != null)
            return user;
    }

    throw new Exception("找不到用户!");
}

它有效... 但我讨厌这个 ToString() 解决方案!

英文:

So... I just found out this is indeed a Bug and it's been around since 2018. the issue arises when you have a PK in a table and that PK is a GUID when you try to filter by the PK, EF does not convert from Guid to String, so basically, the query will be wrong.

The workaround is simple but not very "elegant":

public User GetUser(string Id)
{
    if(Guid.TryParse(Id, out Guid result))
    {
        var user = _context
            .Users
            .Include(x => x.Role)
            .Single(u => u.Id.ToString().Equals(result.ToString()));

        if (user != null)
            return user;
    }

    throw new Exception("User not found!");
}

It works... but I hate this ToString() solution!

huangapple
  • 本文由 发表于 2023年2月19日 09:02:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75497371.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定