简体   繁体   English

在 WebApi 中过滤 EF-Core 和 TPT 继承中的某些子类型

[英]Filtering for certain sub-types in EF-Core and TPT-inheritance in WebApi

I am using the EF-Core and have a data model like that (just an example with the animal to keep things simple).我正在使用 EF-Core 并有一个类似的数据模型(只是一个动物的例子,以保持简单)。 The inhertitance is mapped in the database via TPT (table per type).继承通过 TPT(每种类型的表)映射到数据库中。

public abstract class Animal { }

public class Monkey : Animal { }

public class Bear : Animal { }

public class Dog: Animal { }

On my frontend I am displaying all animals.在我的前端,我展示了所有的动物。 So I create a query of the base-type (abstract) and display all animals on the frontend.所以我创建了一个基本类型(抽象)的查询并在前端显示所有动物。 But now I want to add a filter via query-string to only display a certain type-set of animals (eg Dog and Monkey).但现在我想通过查询字符串添加一个过滤器,只显示某种类型的动物(例如狗和猴子)。

What's the best practice to do so?这样做的最佳做法是什么?

With TPT unfortunately I don't have no discriminator to have a handle to filter?不幸的是,对于 TPT,我没有鉴别器来过滤? How is this done?这是怎么做到的?

Currenty I am receiving the selected types in my WebApi-Conroller via a string array, eg:目前,我正在通过字符串数组在我的 WebApi-Conroller 中接收选定的类型,例如:

public IActionResult(string[] types) {
    // ...
}

An idea would by to map this string array to Type and then go with .OfType.一个想法是将这个字符串数组映射到 Type,然后使用 .OfType。 But then I have the problem that I am not able to chain OfType, because the result-set is always ZERO when chaining.但是我遇到了无法链接 OfType 的问题,因为链接时结果集始终为零。

And it would be perfect that the filtering is done on the database directly, so I want to avoid to that in memory for performance reasons.直接在数据库上进行过滤是完美的,所以出于性能原因,我想避免在内存中进行过滤。

Any ideas?有任何想法吗?

Actually you don't need access to discriminator.实际上你不需要访问鉴别器。 All inheritance models (TPH, TPT and TPC when added) use type based query conditions for filtering the base set.所有继承模型(添加时的 TPH、TPT 和 TPC)都使用基于类型的查询条件来过滤基本集。 OfType is just one of them, in general the C# operator is T is used for filtering and as T or cast (T) for access. OfType只是其中之一,通常 C# 运算符is T用于过滤, as T或 cast (T)用于访问。

There is no out of the box method for applying Type based filter, but it can be created with Expression class - the corresponding method for C# is T is Expression.TypeIs , which in combination with Expression.OrElse allows you to build dynamically the desired predicate.应用基于Type的过滤器没有开箱即用的方法,但可以使用Expression类创建它 - C# 的对应方法is TExpression.TypeIs ,它与Expression.OrElse结合允许您动态构建所需的谓词.

For instance:例如:

public static Expression<Func<T, bool>> MakeFilter<T>(this IEnumerable<Type> types)
{
    var parameter = Expression.Parameter(typeof(T), "e");
    var body = types.Select(type => Expression.TypeIs(parameter, type))
        .Aggregate<Expression>(Expression.OrElse);
    return Expression.Lambda<Func<T, bool>>(body, parameter);
}

and top level query helper:和顶级查询助手:

public static IQueryable<T> OfTypes<T>(this IQueryable<T> source, IEnumerable<Type> types)
    => source.Where(types.MakeFilter<T>());

Applying it to your sample:将其应用于您的样本:

var types = new[] { typeof(Dog), typeof(Monkey) };
var query = db.Set<Animal>().OfTypes(types);

produces the following debug view expression生成以下调试视图表达式

DbSet<Animal>()
    .Where(e => (e is Dog) || (e is Monkey))

and query for SqlServer provider:并查询 SqlServer 提供程序:

SELECT [a].[Id], [a].[Name], CASE
    WHEN [m].[Id] IS NOT NULL THEN N'Monkey'
    WHEN [d].[Id] IS NOT NULL THEN N'Dog'
    WHEN [b].[Id] IS NOT NULL THEN N'Bear'
END AS [Discriminator]
FROM [Animals] AS [a]
LEFT JOIN [Bears] AS [b] ON [a].[Id] = [b].[Id]
LEFT JOIN [Dogs] AS [d] ON [a].[Id] = [d].[Id]
LEFT JOIN [Monkeys] AS [m] ON [a].[Id] = [m].[Id]
WHERE ([d].[Id] IS NOT NULL) OR ([m].[Id] IS NOT NULL)

It's not perfect (has room for optimizations by EF Core team - the did that for TPH with adding a way to tell the discriminator is "complete", but TPT still lacks such capability), but does the filtering database side as wanted.它并不完美(EF Core 团队有优化的空间——TPH 做到了这一点,添加了一种告诉鉴别器“完整”的方法,但 TPT 仍然缺乏这种能力),但是过滤数据库方面是需要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM