Entity Framework 返回两个表的联接的IQueryable。

4
我想使用Entity Framework从2个表中返回数据,并选择这2个表的一些列。起初我并没有成功地尝试一些简单的操作,例如返回int和string(即select new { id = t1.ID, other = t2.OtherData }),因为在目标应用程序(我的Winform)中转换匿名类型十分繁琐。所以我考虑了一下,决定直接返回这2个表的行...
  public static IQueryable<{Table1,Table2}> byRecID(Guid recID, MyContext DBContext)
  {
      return (from i1 in DBContext.Table1
             join j1 in DBContext.Table2 on i1.GroupID equals j1.GroupID
             where i1.RecID.Equals(RecID)
             select new { i1, j1 }).SingleOrDefault();
  }

除了方法的返回类型不正确外,一切都很好。我尝试过几种组合。不幸的是,当我从winform调用“byRecID”时,“SingleOrDefault”不可用,但在“byRecID”方法内部可用,因此不能只返回 IQueryable ,需要是已输入IQueryable<SOMETHING IN HERE> (因为SingleOrDefault不是IQueryable的扩展,只有IQueryable<T>)。

我的问题是...是否有一种语法让我指定它是两个表行的连接?

我想知道...为什么SingleOrDefault选项在方法内部是可用的,但在从winform调用该方法的结果中却无法使用?

基本上我希望找到一些能够让我从winforms进行干净数据调用的东西,而无需将其转换为丑陋的匿名类型,然后使用反射(就像当我返回原始的匿名类型时),但也不想生成一个类型,仅供我的byRecID方法使用。


1
你不能从方法中返回匿名类型,你需要一个具体的类型。 - George Vovos
你可以这样做,只是它们很难使用,这就是我的观点,我想要一种类型,它将自己描述为两种其他类型的结合。 - Ninjanoel
1
此外,当你执行查询时,结果并不是某种IQueryable对象,而是某个实际的对象。 - George Vovos
抱歉@George,您是指匿名类型不能在方法签名中吗?因为我认为无类型的IQueryable基本上是匿名类型列表,稍有不同。 - Ninjanoel
1
因为你能做某件事并不意味着你应该这样做。请记住,你只需要写一次,但你(更重要的是其他人)将会阅读此代码多年。我认为你应该选择最干净的设计,为结果创建一个新的模型类。 - George Vovos
是的,我在尝试设计简洁的界面。但是有很多种方法可以达到目的,我会找到合适的方案的。 - Ninjanoel
3个回答

8
在C#中,匿名类型最好只在投影它们的同一方法范围内使用。正如您自己所见,它们无法在定义方法之外使用。因此,您不能从方法中返回匿名类型(并保留其结构,您始终可以将它们作为对象返回,但这不是一个好主意),这会使您的方法语法无效
最好的选择是将结果投影到为您的需求指定的类中。
public class TableJoinResult
{
   public Table1 Table1 { get; set; }
   public Table2 Table2 { get; set; }
}

你的查询:

 public static IQueryable<TableJoinResult> byRecID(Guid recID, MyContext DBContext)
  {
      return (from i1 in DBContext.Table1
             join j1 in DBContext.Table2 on i1.GroupID equals j1.GroupID
             where i1.RecID.Equals(RecID)
             select new TableJoinResult { Table1= i1, Table2 = j1 }).SingleOrDefault();
  }

关于匿名类型的更多信息: https://msdn.microsoft.com/zh-cn/library/bb397696.aspx


1
啊啊啊,我正在将旧式的 SQL 用法转换为代码中编写 SQL 并选择任何字段的方式。必须重新考虑我的方法。我试图通过创建可编译为 SQL 的 iqueryables 来保持性能,但是创建数百个这些“TableJoinResult”类型听起来不干净也不好玩,而几个 linq 表达式更干净但不如高效(单个 linq 表达式可以更好地进行编译)。 - Ninjanoel
是的,这并不好玩,但我认为这是最好的选择。使用带有Visual Studio 2017(于3月7日发布)的C# 7,语言中有一个新概念(实际上是更好的支持,因为元组已经存在于语言中,但它们并不是很可用)-> 元组,可以解决这些情况。 - vasil oreshenski

2

1)创建一个具体的类来描述你的结果。

2)参见1。


0

我想避免创建特定的类或结构体。 以下是一种避免创建结构体/类的冗长方式。 因此,我在我的服务类中这样做:

 List<KeyValuePair<string,int>> IWorkflowService.GetServiceRetention()
        {
           var serviceRetention =  from y in _context.SERVICES
                     join z in _context.SERVICE_SETTINGS on y.ID equals z.SERVICEID
                     select new { Service= y.SERVICE, Retention=z.RETENTION };//new KeyValuePair<string,int?>( y.SERVICE, z.RETENTION ));
            var list = new List<KeyValuePair<string, int>>();
            foreach (var obj in serviceRetention)
            {
                list.Add(new KeyValuePair<string, int>(obj.Service,Convert.ToInt32(obj.Retention)));
            }
            return list;
        }

我必须在我的控制器类中这样做:

List<KeyValuePair<string, int>> c = _service.GetServiceRetention();

            foreach (var obj in c)
            {
                 //The key contains service and value contains Rtention.
                  RecurringJob.AddOrUpdate(DELETE_SERVICE + obj.Key, () =>
                  Delete(obj.Key), //this is my function that does the actual process
               Cron.DayInterval(Convert.ToInt32(obj.Value)));
            }

我不确定这是否是最好的方法,但对我有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接