Parameterizing ThenInclude() in Entity Framework

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

Parameterizing ThenInclude() in Entity Framework

问题

我已经创建了一种设置我的 EF 查询的方法,通过传递参数来使用 Include()

public static class AppUserExtensions
{
    public static IQueryable<AppUser> FindById(this IQueryable<AppUser> source, int userId, Expression<Func<AppUser, object?>>[] includes)
    {
        var query = source.Where(u => u.Id == userId);
        foreach (var include in includes)
            query = query.Include(include);
        return query;
    }
}

public static class AppUserIncludes
{
    public static Expression<Func<AppUser, object?>>[] Organizations => new Expression<Func<AppUser, object?>>[]
    {
        u => u.Campaigns,
        u => u.Cantons,
        u => u.States,
        u => u.Countries,
    };
}

这种方法的优点是我避免了在代码中散布的冗长查询,而是拥有了一组适用于任何类型的搜索的查询,以及一组用于选择要包含的属性的数组。我可以使用任何数组与任何查询。

但是... 我现在也面临着对 ThenInclude() 的相同问题。

我想要的是一种类似于构建数组的对象(我知道我在这里写的不是有效的):

Complex<T1, T2> Expression<Expression<Func<T1, object?>>, Expression<Func<T2, object?>>[]>

我在如何做到这一点方面遇到了困难。但我想要能够创建一些东西,其中我传递:

<AppUser, Campaign>(u => u.Campaigns, CampaignExtensions.Parents)

然后在查询中我可以做类似的事情:

foreach (var thenInclude in thenIncludes)
    query = query.Include(include).ThenInclude(thenInclude);

我知道我可能没有完美地解释这一点,因为我不确定如何实现这一点,这就是为什么我在询问。我希望这足够让某人可以帮助我。有办法做到这一点吗?

另外,我假设最终查询中可能会有相同的 Include(u => u.Campaigns) 出现3次或5次没有什么坏处。SQL Server 会将其减少为相同的 SQL,因为它会解析所有表达式吗?


进一步的思考。如果我按照如下方式调用 Include(),它会返回一个知道自己正在返回一个集合的类型:

IIncludableQueryable<Campaign, ICollection<AppUser>> x = query.Include(campaign => campaign.Followers!);

所以在这种情况下,T1 是 AppUser,它返回一个对象,该对象知道 Campaign 需要成为 T2 的类型。因此,如果有一种方法可以在代码中知道这一点,以便在编写代码时进行类型检查,那将是最理想的情况。

所有的信息都在那里,所以我猜这是可能的。我为 C# 和泛型添加了标签,因为这可能更多是一个关于泛型的问题,而不是 EF 的问题。

英文:

I've created a way to set up my EF queries to take what gets Include() via passing a parameter.

public static class AppUserExtensions
{
    public static IQueryable&lt;AppUser&gt; FindById(this IQueryable&lt;AppUser&gt; source, int userId, Expression&lt;Func&lt;AppUser, object?&gt;&gt;[] includes)
	{
		var query = source.Where(u =&gt; u.Id == userId);
		foreach (var include in includes)
			query = query.Include(include);
		return query;
	}
}

public static class AppUserIncludes
{
	public static Expression&lt;Func&lt;AppUser, object?&gt;&gt;[] Organizations =&gt; new Expression&lt;Func&lt;AppUser, object?&gt;&gt;[]
	{
		u =&gt; u.Campaigns,
		u =&gt; u.Cantons,
		u =&gt; u.States,
		u =&gt; u.Countries,
	};
}

The beauty of this approach is I avoid long queries strewn throughout my code and instead have a set of queries for any kind of search and then this set of arrays to select which properties to Include(). And I can use any array with any query.

But... I'm now facing the same issue for ThenInclude().

What I want is some kind of object to build an array of like (what I'm writing here I know is not valid):

Complex&lt;T1, T2&gt; Expression&lt;Expression&lt;Func&lt;T1, object?&gt;&gt;, Expression&lt;Func&lt;T2, object?&gt;&gt;[]&gt;

I'm struggling with exactly what/how to do this. But I want to be able to create something where I pass:

&lt;AppUser, Campaign&gt;(u =&gt; u.Campaigns, CampaignExtensions.Parents)

And then in the query I can do something like:

		foreach (var thenInclude in thenIncludes)
			query = query.Include(include).ThenInclude(thenInclude);

I know I may not be explaining this perfectly as I'm not sure how to accomplish this and that's why I'm asking. I hope this is enough that someone can help. Is there a way to do this?

Also, I assume there's no downside to the eventual query possibly having the same Include(u =&gt; u.Campaigns) in ot 3 or 5 times. Doesn't SQL Server reduce this down to the same SQL as it figures out all the expressions?


Further thought. If I call Include() as follows, it returns a type that knows what type it is returning a collection of:

IIncludableQueryable&lt;Campaign, ICollection&lt;AppUser&gt;&gt; x = query.Include(campaign =&gt; campaign.Followers!);

So where T1 in this case is AppUser, it returns an object that knows Campaign needs to be the type for T2. So if there's a way to know that in the code so it is type checking as I write it, and at compile time, that would be optimal.

The information is all there so I'm guessing this is possible. I added tags for C# & generics because this may be more a generics question and not so much an EF question.

答案1

得分: 0

h/t 致谢 Karen Payne 在 MSDN 上

看起来你应该能够使用路径来实现这个功能,路径是一个用点号分隔的相关对象列表,可以在查询结果中返回。

示例:Include("Addresses.StateProvince.CountryRegion")

英文:

h/t to Karen Payne on MSDN

Seems you should be able to do this with path The dot-separated list of related objects to return in the query results.

Example Include("Addresses.StateProvince.CountryRegion")

huangapple
  • 本文由 发表于 2023年6月22日 11:38:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76528464.html
匿名

发表评论

匿名网友

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

确定