过滤ef核中某些子类型和WebAPI中的TPT传播

发布于 2025-02-10 08:45:09 字数 642 浏览 3 评论 0原文

我正在使用EF核心,并具有类似的数据模型(只是与动物保持简单的例子)。通过TPT在数据库中映射interitante(表每种类型)。

public abstract class Animal { }

public class Monkey : Animal { }

public class Bear : Animal { }

public class Dog: Animal { }

在我的前端,我正在展示所有动物。因此,我创建了基本类型(抽象)的查询,并在前端显示所有动物。但是现在,我想通过查询弦添加一个过滤器,以仅显示某种类型的动物(例如狗和猴子)。

这样做的最佳做法是什么?

不幸的是,对于TPT,我没有歧视器可以过滤吗?这是怎么做的?

当前,我通过字符串数组在我的WebAPI-Conroller中接收所选类型,例如:

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

一个想法将映射此字符串数组以键入键入,然后使用.oftype。但是,我遇到了一个问题,我无法链接tytype,因为链接时结果集总是为零。

直接在数据库上进行过滤是完美的,因此出于性能原因,我想避免在内存中避免使用。

有什么想法吗?

I am using the EF-Core and have a data model like that (just an example with the animal to keep things simple). The inhertitance is mapped in the database via TPT (table per type).

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 (e.g. 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? How is this done?

Currenty I am receiving the selected types in my WebApi-Conroller via a string array, e.g:

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

An idea would by to map this string array to Type and then go with .OfType. But then I have the problem that I am not able to chain OfType, because the result-set is always ZERO when chaining.

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

被你宠の有点坏 2025-02-17 08:45:09

实际上,您不需要访问歧视器。所有继承模型(添加时TPH,TPT和TPC)都使用基于类型的查询条件来过滤基础集。 ofType只是其中之一,通常C#运营商t用于过滤,作为T或cast 或cast (t)用于访问。

没有包装盒的方法用于应用类型基于过滤器,但是可以使用expression类创建它 - C#的相应方法是T expression.typeis ,与 expression.orelse 允许您动态地构建所需的谓词。

例如:

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);
}

以及顶级查询助手:

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

将其应用于样本:

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

为SQLServer提供商生成以下调试视图表达式

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

和查询:

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)

它不是完美的(EF Core Core Team进行了优化的余地 - 对于TPH而言,这是为TPH提供的,并添加了一种方法告诉判别器是“完整的”,但TPT仍然缺乏这样的功能),但是滤波数据库端的侧面是需要的。

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. 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.

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.

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:

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文