【问题标题】:selecting properties from other table with Lambda expression使用 Lambda 表达式从其他表中选择属性
【发布时间】:2016-10-15 02:53:27
【问题描述】:

我对 .NET 的 Lambda 表达式和尝试使用 lambda 表达式从 SQL 获取数据的经验较少。通过以下查询,我可以取回数据,但不想使用include 从其他表中获取所有属性。

public IEnumerable<ResourceGroup> GetAllServersByApplication(string application_name, string environment_name, string status)
{
    var query = _context.ResourceGroup
        .Include(a => a.Application)
        .Include(t => t.Type)
        .Include(e => e.ServersGroup).ThenInclude(e => e.Environment)
        .Include(s => s.ServersGroup).ThenInclude(s => s.Server)
        .Include(s => s.ServersGroup).ThenInclude(s => s.Server).ThenInclude(s => s.Status)
        .Where(a => a.Application.Name == application_name && a.ServersGroup.Any(s => s.Environment.Name == environment_name && s.Server.Status.Name == status))
        .ToList();

    return query;
}

让我们以下面的包含语句为例。

.Include(s => s.ServersGroup).ThenInclude(s => s.Server)

s.Server,我只想选择Id,ServerName,Status, and IPAddress。这些是我作为模型创建的 Servers 类的属性。

排除所有包含并仅显示我感兴趣的属性的简单方法是什么?

这是我的表格及其属性:

状态表:

Id, Name

应用程序表:

Id, Name

服务器表:

Id, ServerName, Status

环境表:

Id, Name

资源组表:

Id, Name, Application_Id, Environment_Id

ServersResourceGroup 表:

Id, Server_Id, Resource_Id

更新 1

var query = _context.ResourceGroup
                    .SelectMany(rg => rg.ServersGroup
                    .Select(sg => new
                    {
                        ResourceName = rg.Name,
                        ApplicationName = rg.Application.Name,
                        ServerName = sg.Server.ServerName,
                        EnvironmentName = sg.Environment.Name,
                        Status = sg.Server.Status.Name
                    })).Where(a => a.ApplicationName == application_name && a.EnvironmentName == environment_name && a.Status == status).ToList();
        return query;

query 变量上的红线错误:

更新 2

这里是查询语法:

var query = from rg in _context.ResourceGroup
                    let app = rg.Application
                    from sg in rg.ServersGroup
                    let env = sg.Environment
                    let srv = sg.Server
                    let stat = srv.Status
                    where app.Name == application_name
                            && rg.ServersGroup.Any(s => s.Environment.Name == environment_name
                                      && s.Server.Status.Name == status)
                    select new
                    {
                        ResourceGroupName = rg.Name,
                        ApplicationName = app.Name,
                        ServerName = srv.ServerName,
                        Alias = srv.Alias,
                        IPAddress = srv.IPAddress,
                        Type = rg.Type.Name,
                        Status = stat.Name
                    };
        return query;

这是我在查询变量中得到的红线错误:

非常感谢您的帮助。 :)

谢谢,

【问题讨论】:

  • EF 无法使用 include 部分加载关联属性,因此您需要使用 .Select() 将其投影到视图模型(或匿名对象)中
  • @StephenMuecke,感谢您的回复。我已经用更多细节更新了我的问题。如果我删除所有Includes,如何使用 lambda 语法选择语句?我可以用原生的sql查询来写,很简单,但是lambda让我很困惑。
  • 保留包含(取决于是否启用延迟加载),然后使用.Select(x =&gt; new MyModel(){ // set values here }); 其中MyModel 是一个仅包含您想要包含在视图中的属性/集合的类.

标签: c# asp.net-mvc linq lambda


【解决方案1】:

使用 lambda 表达式,您可以使用 SelectMany 将 1-n 关联扁平化为一维列表(即并排的父属性和子属性)。在你的情况下,从Where 子句来看,我认为只有ResourceGroup - ServerGroup 是1 - n,所以它应该是这样的:

var query = _context.ResourceGroup
                    .SelectMany
                     (
                          rg => rg.ServersGroup
                                  .Select(sg => new 
                                  {
                                      ResourceGroup = rg.Name,
                                      Application = rg.Application.Name,
                                      Server = sg.Server.ServerName,
                                      // etc.
                                  })
                     )

知道如何使用 lambda 表达式当然很好,但是当查询语法使代码更易于理解时,使用它们真的没有意义。

查询语法中的等价物是:

var query = from rg in _context.ResourceGroup
            let app = rg.Application
            from sg in rg.ServersGroup
            let env = sg.Environment
            let srv = sg.Server
            let stat = srv.Status
            where app.Name == application_name
               && sg.ServersGroup.Any(s => s.Environment.Name == environment_name
                                        && s.Server.Status.Name == status)
            select new
            {
                ResourceGroup = rg.Name,
                Application = app.Name,
                Server = srv.ServerName,
                // etc. use any property from rg, app, sg, srv, stat
            };

如你所见-

  • n - 1 个关联由 let 语句表示(这实际上只有助于缩短 select 中的引用)
  • 1-n 关联由from ... from 语法表示,这是SelectMany 的查询语法。

我没有更改查询语法中的Where 子句。 也许你可以使用...

            where app.Name == application_name
               && env.Name == environment_name
               && stat.Name == status)

...但请注意,这是不同的。原始where 返回所有ResourceGroup至少一个ServerGroup 满足条件(可能还有其他具有不同环境和状态的组)。另一个where 只返回环境和状态等于搜索参数的数据。

【讨论】:

  • 首先非常感谢您花时间向我解释 lambda 和查询语法。我认为查询语法与 lambda 语法相比更具可读性。我尝试了这两种方法,但我得到了一条红线,它不能用于 IEnumarable。我已使用更新 1 状态更新了我的查询,其中包含您的答案中建议的第一个 lambda 表达式查询。我将很快使用查询语法添加更新两个。
  • 错误告诉你你想从你的方法返回IEnumerable&lt;ResourceGroup&gt;,而查询返回匿名类型。您希望只返回选定的属性和返回ResourceGroups(带有粘附对象)的要求是不兼容的。如果要返回选定的属性,则必须返回一些包含所需属性的自定义类的集合。 (不建议从方法中返回匿名类型)。
  • 明白,所以在这种情况下,我可以保留我的原始查询或修改我的方法以使用List&lt;T&gt;?
  • 没错。你不能两者兼得。就个人而言,我倾向于使用自定义投影(例如,DTO 类)。包含会严重影响性能。
  • 有道理。谢谢格特的澄清。
【解决方案2】:

不要Include所有相关的表,而是Select你需要的所有字段。您可能会发现创建一个新类来保存数据更容易。

对不起,如果我不能在这里做一个真正的查询语句,但你的问题没有指定你需要的字段。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-22
    • 1970-01-01
    相关资源
    最近更新 更多