【发布时间】:2020-07-08 20:02:09
【问题描述】:
我有一个代码优先应用程序和一个表“通知”,其中“标签”列将标签存储在一个字符串中,用“;”分隔。在上下文中,我转换为 IEnumerable,反之亦然。插入和获取数据时一切正常,但在一项服务中,我动态构建过滤器,通过添加谓词,一一添加,并添加最终谓词列表进行查询。 现在,我有一种情况,我想按标签过滤,例如,我想要所有带有标签“Tag1”和“Tag2”的通知。我尝试使用 Contains 和 Intersect,但由于无法翻译 LINQ 表达式,我经常遇到异常。有任何想法吗? 谢谢。
上下文:
builder.Entity<Notification>().Property(x => x.Tags).HasConversion
(x => string.Join(';', x),
x => x.Split(';', StringSplitOptions.RemoveEmptyEntries)
);
服务:
var filter = PredicateBuilder.True<UserNotification>();
IEnumerable<string> tagsFilter = new List<string>() { "Tag1","Tag2" };
filter = filter.And(x => x.Notification.Tags != null); // this line works
// both these lines fail (they are here as alternatives, should give the same result)
filter = filter.And(x => x.Notification.Tags.Any(r => tagsFilter.Contains(r)));
filter = filter.And(x => x.Notification.Tags.Intersect(tagsFilter).Any());
错误是(在“Where”子句中):
System.InvalidOperationException: The LINQ expression 'DbSet<UserNotification>
.Join(
outer: DbSet<Notification>,
inner: u => EF.Property<Nullable<long>>(u, "NotificationId"),
outerKeySelector: n => EF.Property<Nullable<long>>(n, "Id"),
innerKeySelector: (o, i) => new TransparentIdentifier<UserNotification, Notification>(
Outer = o,
Inner = i
))
.Where(u => True && __statuses_0
.Contains(u.Outer.NotificationStatus) && __types_1
.Contains(u.Inner.Type) && u.Inner.Tags != null && u.Inner.Tags
.Any(r => __tags2_2.Contains(r)))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
【问题讨论】:
-
您必须在客户端的标签上应用过滤器。
-
您确定在通知表中将标签连接成一个字符串是一个好的设计吗?拥有一个带有标签的单独表格,并在通知和标签之间建立多对多关系,不是更好,而且可能更容易吗?每个通知都有零个或多个标签,每个标签都被零个或多个通知使用。这样更容易更改标签:例如重命名文本,或声明它们已过时。这是代码优先,所以现在您仍然可以决定创建一个适当规范化的数据库结构。
-
嗨,谢谢大家的 cmets,@David - 我能以某种方式强制查询在客户端执行,而不会将所有未过滤的数据拉到内存中吗?
标签: c# entity-framework linq entity-framework-core