EF Core 3.1 无法查询 Json 序列化对象
Posted
技术标签:
【中文标题】EF Core 3.1 无法查询 Json 序列化对象【英文标题】:EF Core 3.1 Fail to query on Json Serialized Object 【发布时间】:2021-01-20 01:07:39 【问题描述】:我使用 json 序列化将列表存储在字段中的 id 上
型号:
public class Video
public int Id get; set;
public string Name get; set;
public virtual IList<int> AllRelatedIds get; set;
上下文:
modelBuilder.Entity<Video>(entity =>
entity.Property(p => p.AllRelatedIds).HasConversion(
v => JsonConvert.SerializeObject(v, new JsonSerializerSettings NullValueHandling = NullValueHandling.Ignore ),
v => JsonConvert.DeserializeObject<IList<int>>(v, new JsonSerializerSettings NullValueHandling = NullValueHandling.Ignore )
);
);
它工作正常,添加、编辑、删除项目很容易,并且在 SQL 数据库中它以 json 格式存储,如 [11000,12000,13000]
一切都很好但是!只要想在这个列表上查询,我就会得到奇怪的响应。
地点:
_context.Set<Video>().Where(t=>t.AllRelatedIds.contains(11000))
返回 null 但是,如果我要求返回所有 AllRelatedIds 项,则某些记录的值 exp 为 11000。
计数:
_context.Set<Video>().Count(t=>t.AllRelatedIds.contains(11000))
返回无法翻译。以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。
EF Core 有什么问题?我什至测试了t=>t.AllRelatedIds.ToList().contains(11000)
,但没有任何区别
我该怎么办?我不想有更多的表,我使用了数百次这种方法,但似乎从未对它们进行查询。
【问题讨论】:
我想问问这个数据库是怎么回事?您是否尝试将 ID 存储在 JSON 字段而不是多对多表中?这是一个明显的数据库设计错误。 EF是一个ORM,它将对象映射到关系结构,它不能做数据库中不可能的事情。使用多对多表很容易、干净且易于管理。将所有内容都填充到一个字段中既不是,因为它违反了最基本的设计规则——一个字段,一个值。现在不可能使用 SQL 来连接、过滤或查询该字段 如果您使用 SQL Server 2016 及更高版本,您可以使用 JSON 函数(JSON_QUERY、JSON_VALUE)来使用该字段,但您仍然会得到非常糟糕的性能,因为这些值可以'不被索引。如果您有像 Oracle 这样支持阵列的数据库,您可以使用VARRAY
,但性能仍然会受到影响
I don't want to have more tables,
为什么? as soon as want to query on this list
你没有列表,你有一个很长的不透明字符串,一些函数可以解析。如果数据库设计得当,您甚至可以使用SQL Server's graph features 跨多个级别查找相关视频。通过使用那个长字符串,您甚至无法在 SQL 中查询单个值
@PanagiotisKanavos 我不想在不重要的项目上使用太多的关系并进行 sql 查询,只是让它们像逗号分隔似乎就足够了
@PanagiotisKanavos 你是对的,我必须将它们存储在数据库中
【参考方案1】:
Json 序列化/反序列化发生在应用程序级别。 EF Core 将IList<int>
对象序列化为值[11000,12000,13000]
之前 将其发送到数据库进行存储,并将值[11000,12000,13000]
反序列化为IList<int>
对象之后 检索它从数据库。数据库内部什么也没有发生。您的数据库无法将 [11000,12000,13000]
作为数字集合进行操作。对数据库来说,它是一条数据。
如果您尝试以下查询 -
var videos = _context.Set<Video>().ToList();
var video = _context.Set<Video>().FirstOrDefault(p=> p.Id == 2);
你会得到预期的结果,EF Core 完美地完成了它的工作。
问题是,当你查询类似 -
_context.Set<Video>().Where(t=> t.AllRelatedIds.Contains(11000))
EF Core 将无法将 t.AllRelatedIds.Contains(11000)
部分转换为 SQL。 EF Core 只能序列化/反序列化它,因为您告诉它(以及如何)。但正如我上面所说,您的数据库不能将[11000,12000,13000]
作为整数集合进行操作。因此 EF Core 无法将 t.AllRelatedIds.Contains(11000)
转换为对数据库有意义的任何内容。
解决方案是获取所有视频的列表,以便 EF Core 可以将 AllRelatedIds
反序列化为 IList<int>
,然后您可以对其应用 LINQ -
var allVideos = _context.Set<Video>().ToList();
var selectedVideos = allVideos.Where(t=> t.AllRelatedIds.Contains(11000)).ToList();
但是从性能的角度来看,不是每次都获取所有视频是不必要的/矫枉过正或效率低下吗?是的当然。但正如 cmets 所暗示的,您的数据库设计/使用方法存在一些缺陷。
【讨论】:
以上是关于EF Core 3.1 无法查询 Json 序列化对象的主要内容,如果未能解决你的问题,请参考以下文章