简体   繁体   English

动态构建实体框架选择查询

[英]Dynamically build Entity Framework Select queries

I have a query that looks a bit like this: 我有一个查询,看起来像这样:

context.Users
.........
.Select(user => new UserDTO
{
    UserName = user.Name,
    DataList = context.Data.Where(x => x.UserId == user.Id).ToList(),
    Emails = context.Email.Where(x => x.UserId == user.Id).ToList(),
    BankAccounts = context.BankAccount.Where(x => x.UserId == user.Id).ToList(),
    .....
}

The problem with this is that this all creates a massive query, because all those subselects are seperate lists. 问题是这都会创建一个庞大的查询,因为所有这些子选择都是单独的列表。 The only reason these selects are there is because sometimes I need to filter on them. 这些选择存在的唯一原因是因为有时我需要对它们进行过滤。 So I was wondering if it is possible to build them dynamically, like this: 所以我想知道是否有可能像这样动态地构建它们:

var query = context.Users
    .........
    .Select(user => new UserDTO
    {
        UserName = user.Name,
        .....
    }

if (EmailFilter) query.Select.Add(Emails, context.Email.Where(x => x.UserId == user.Id).ToList());

if (AccountsFilter) query.Select.Add(BankAccounts, context.BankAccount.Where(x => x.UserId == user.Id).ToList());

Not exactly that of course but you get the idea hopefully. 当然不完全可以,但是您有希望得到这个主意。

You can dynamically build your projections from outside the DbContext. 您可以从DbContext外部动态构建投影。 If you look into expressions you will notice how you can create your select query before calling the EF methods. 如果查看表达式,您会注意到在调用EF方法之前如何创建选择查询。

For instance what I use is something like this: 例如,我使用的是这样的:

internal class TenantFullProjector : IProjector<Tenant, TenantProjection>
{
    public Expression<Func<Tenant, TenantProjection>> GetProjection()
    {
        return (x) => new TenantProjection()
        {
            Code = x.Code,
            DatabaseId = x.DatabaseId,
            Id = x.Id,               
        };
    }
}

I have a IProjector interface which is used by my repository layer. 我有一个IProjector接口,供存储库层使用。 What matters is that it returns an Expression that ultimately will map the Tenant domain model into a TenantProjection projection model. 重要的是它返回一个表达式,该表达式最终将把Tenant域模型映射到TenantProjection投影模型中。 I can create as many different versions as I want as long as I implement the interface correctly. 只要正确实现接口,我就可以创建任意多个版本。 This example for instance maps all columns whereas other projectors might only select a subset of columns. 例如,此示例映射了所有列,而其他投影仪可能只选择了列的子集。

In the repository layer I will do something like this: 在存储库层中,我将执行以下操作:

return query.Select(myProjectionExpression); 

...where query equals ctx.Set(). ...其中查询等于ctx.Set()。

You can do exactly the same thing for your where statements. 您可以对where语句执行完全相同的操作。

If you don't have navigation properties then I think you can use the flag itself inside the Where like this: 如果您没有导航属性,那么我认为您可以在Where内使用标记本身,如下所示:

Select(user => new UserDTO
{
    UserName = user.Name,
    DataList = context.Data.Where(x => x.UserId == user.Id).ToList(),
    Emails = context.Email.Where(x => EmailFilter && x.UserId == user.Id).ToList(),
    BankAccounts = context.BankAccount.Where(x => AccountsFilter && x.UserId == user.Id).ToList(),
    .....
}

Maybe something like this: 也许是这样的:

var query = context.Users
    .Select(user => new
    {
        UserName = user.Name,
        Emails = EmailFilter ? context.Email.Where(x => x.UserId == user.Id).ToList() : null,
        BankAccounts = AccountsFilter ? context.BankAccount.Where(x => x.UserId == user.Id).ToList() : null,
        // and so on
    });

Depending on the filter values you get value lists or null in the result properties. 根据过滤器值,您将获得值列表或结果属性中为null。

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

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