【问题标题】:Select * from two tables into two separate lists从两个表中选择 * 到两个单独的列表中
【发布时间】:2017-02-12 02:03:16
【问题描述】:

伪代码中的基本思想

Select(a, b) => new Tuple<List<Item1>, List<Item2>>(a, b)

我正在尝试在以下位置完成此操作:

  • 对 db 的单个查询
  • 显然使用 linq(查询或方法语法)

这里是涉及到的两个类

public class Bundle
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public HashSet<Inventory> Inventories { get; set; }
}

public class Inventory
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public int Stock { get; set; }
}

现在我能想到的只有

 using (var context = new MyEntities())
 {
     return new Tuple<IEnumerable<Inventory>, IEnumerable<Bundle>>(context.Inventories.OrderBy(a => a.Stock).ToList()
         , context.Bundles.Include(b => b.Inventories).OrderBy(c => c.Name).ToList());
 }  

但是,这会访问数据库两次。

我知道UNION 用于组合来自 2 个不同查询的结果集,但是这两个查询必须具有相同的列数,所以我假设在选择相同数据时最好使用它。

如何将两个不同表中的数据选择到两个单独的列表中,而对 db 只进行一次命中?

【问题讨论】:

  • 想要只访问一次数据库有什么特别的原因吗?
  • 只是为了尽量减少执行的查询量

标签: c# entity-framework linq query-optimization


【解决方案1】:

如果你想要两个结果集,你可以通过抛出两个查询来实现。这可以在单个数据库调用中毫无问题地完成,但它不会像您感兴趣的那样神奇地分成两组对象。

事实上,当建立连接的成本(实例化成本或延迟成本等)大到足以保证连接时,提出多个问题并获得多个结果集是很常见的。我自己在一个存储过程中完成了它,在一个查询中询问页面所需的所有内容。

但是,如果性能是关键问题,缓存也很常见。而且,如果这些是下拉列表,或者请求的列表很小,并且列表不经常更改,您甚至可以在应用程序启动时将其拉入内存并让它在 Web 服务器上设置,这样您就不会进行数据库之旅。

我不喜欢 LINQ to SQL,因为它会给 DBA 造成混乱,但您可以这样做(仅举一个例子,任何可以链接命令的方法都可以):

var connString = "{conn string here}";
var commandString = "select * from tableA; select * from tableB";
var conn = new SqlConnection(connString);
var command = new SqlCommand(commandString, conn);
try {
    conn.Open();
    var result = command.Execute();
    // work with results here
}
finally {
    conn.Dispose();
}

我还没有填写所有详细信息,但是您可以使用任意数量的命令来完成此操作。再一次,如果信息没有改变,请考虑单次命中并保留在内存中(通过编程进行缓存)或使用其他类型的缓存机制。

【讨论】:

  • Gregory,感谢您深思熟虑的回复,但我不得不将 Sam 的回答标记为正确,因为他展示了使用 linq 的可能解决方案。不过,我赞成你的回答。
  • @Adrian - 没问题。我对 LINQ to SQL 的问题是 DBA 的噩梦。对于一个小项目来说还不错,但在企业情况下就不那么好了。我并不反对 LINQ 本身,因为我发现它是梳理对象的好方法。有趣的是,LINQ to SQL 的编写很大程度上是由于 SQL Server 2005(实体框架 1.0)的延迟以及 Visual Studio 2005 推出时突出显示如何在 15 分钟内构建网站的演示。
【解决方案2】:

如果您担心性能,我不认为减少查询量是要走的路。事实上,如果您想要细粒度的优化,LINQ 也不是最合适的工具。

话虽如此,您可以使两个不同的对象匹配相同的接口/列,为其他类型中缺少的对象填充虚拟属性。这在理论上应该被翻译成包含所有列的 SQL union

var union = context.Inventories
    .Select(i => new { i.Id, i.Name, i.Stock, Inventories=null })
    .Concat(context.Bundles.Select(b => new { b.Id, b.Name, Stock=0, b.Inventories));

请注意,在这种情况下,Concat 优先于 Union,因为它不会改变行的顺序并允许重复行。

【讨论】:

    【解决方案3】:

    你所要求的是不可能的。 如果您试图获得两个不同的结果,则需要两次访问数据库,每个结果集一次。您可以进行外部连接,但这会使您的结果集比它应该的更大,并且从您想要访问数据库的事实来看,您关心性能。 另外,如果性能是一个问题,我一开始就不会使用 Linq。

    【讨论】:

    • 我同意你关于 linq 的观点。这个项目只是一次采访的收获,所以我试图预见我可能遇到的任何“陷阱”。不过这很奇怪,我原以为有办法一次性做到这一点。
    • 啊啊啊面试问题通常没有意义:)
    【解决方案4】:

    我认为您可以通过要求多个结果集来做到这一点(正如 Gregory 已经指出的那样),我不确定什么是“最好的”方法。例如,您为什么不看看这篇 msdn 文章? How to: Use Stored Procedures Mapped for Multiple Result Shape

    或者这个,对于 linq-to-sql 方法: LINQ to SQL: returning multiple result sets

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多