在LINQ中重用连接

时间:2011-10-10 12:45:30

标签: c# linq entity-framework-4

我有许多查询(在C#项目中)都使用相同的连接语法,是否有一种简单的方法可以将查询中的联接移动到“函数”或“模板”中(因为需要一个更好的词)。

当前查询看起来像这样(rc是我的ObjectContext)

var x =
from b in rc.Pressures
join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
where g.UserId == userId
orderby b.Recorded descending
select b;

我希望能够把联接出来并把它放在一个名为'PressuresJoin'的'功能'中

from b in rc.Pressures
join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
where g.UserId == userId

然后使用类似

的内容
var x = PressuresJoin()
orderby b.Recorded descending
select b;

更新 按照@Xanatos的指示,我创建了一个像这样的IQueryable函数。

public static IQueryable<Pressure> PressuresJoin(ObjectContext rc, Guid userId)
{
    return from b in rc.Pressures
           join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
           where g.UserId == userId
           select b;
}

然后我像这样使用

 IQueryable<Pressure> q = PressuresJoin(rc, userId);
            var y =
                from b in q
                orderby b.Recorded descending
                select b;

所以现在使用IQueryable意味着我不需要反复编写相同的连接代码,也不需要确保来自数据库的压力仅限于正确的用户(因为PressuresJoin确保这一点)总是发生。)

请注意,我不需要创建额外的类(如建议的那样),因为在加入之后我不需要任何GUIDIdentityMaps信息。

非常干净整洁 - 感谢所有贡献者

更新II: 根据Kirk提供的答案,我放弃了“手动连接”语法并查看了使用导航属性。

这让我有了几个选择 第一个是使用像这样的.Include

public static IQueryable<Pressure> PressuresJoinInclude(ObjectContext rc, Guid userId)
        {
            return from b in rc.Pressures.Include("GuidIdentityMap")
                   where b.GUIDIdentityMap.UserId == userId
                   select b;
        }

并像这样称呼它

IQueryable<Pressure> q = PressuresJoinInclude(rc, userId);
var y =
from b in q
orderby b.Recorded descending
select b;

这只是花花公子(所以Kirks的回答也是答案​​)但是我并不喜欢.Include(字符串)语法,因为我错误地拼写了GUIDIdentityMap,所以它让我感到高兴。

然后我研究了避免使用.Include(string)的方法并想出了这个

public static IQueryable<Pressure> PressuresJoinDirect(ObjectContext rc, Guid userId)
{
     return from b in rc.GUIDIdentityMaps.Single(i => i.UserId == userId).Pressures.AsQueryable()
     select b;
}

注意最后的'AsQueryable()(从IEnumerable转发回IQueryable) 这被称为

IQueryable<Pressure> q = PressuresJoinDirect(rc, userId);
            var y =
                from b in q
                orderby b.Recorded descending
                select b;

到目前为止,我认为这是我最喜欢的,虽然如果压力是声明中的“第一个”对象而不是GUIDIdentityMaps会更好,但是我没有检查过生成什么SQL或者有什么性能差异(如果有的话)

现在我要看看Jims。(点)解决方案。

更新III: 希望我能正确理解点符号,这里是我通过使用内置于对象的导航来“加入”的。 我的IQueryable函数看起来像这样

   public static IQueryable<Pressure> PressuresJoinDot(ObjectContext rc, Guid userId)
   {
       return from b in rc.Pressures
              where b.GUIDIdentityMap.UserId == userId
              select b;
   }

使用它就像其他人一样简单

 IQueryable<Pressure> q = PressuresJoinDot(rc, userId);
            var y =
                from b in q
                orderby b.Recorded descending
                select b;

在所有这一切的结尾,我做了一个Skip / Take这样的

    IQueryable<Pressure> z = y.Skip(startRow).Take(rows);
    List<Pressure> l = z.ToList();

以上所有这四个选项都可以使用,而且这些查询的最终结果都是同一组记录。令我惊讶的是,他们并不都生成相同的SQL,并且它们并不都以相同的速度运行(因此我分割了Skip / Take和ToList())。

普通的Join和Dot版本以相似的速度执行,普通的Join可能更快一点。 Include版本运行良好,但在跳过/接收到记录列表时,DB读取的数量会增加。 Direct方法非常慢,在其他方法上超过10倍,我确信Skip / Take是在生成的List()上而不是在数据库上执行的。

当然让我看到选择错误的LINQ是多么容易,当然选择它看起来是“最好的”Direct方法,也不是最好的主意。

3 个答案:

答案 0 :(得分:3)

您看到的join确实是Queryable.Join<TOuter, TInner, TKey, TResult>。因此,任何返回IQueryable<TResult>的方法都可以用作PressuresJoin

如果您正在使用linq-to-objects,则只需要返回IEnumerable<TResult>

class PressuresGUIDIdentityMap
{
    public Pressure Pressure;
    public GUIDIdentityMap GUIDIdentityMap;
}

public static IQueryable<PressuresGUIDIdentityMap> PressuresJoin(ObjectContext rc)
{
    return from b in rc.Pressures
        join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
        select new PressuresGUIDIdentityMap { Pressure = b, GUIDIdentityMap = g };
}

请注意,您必须创建一个类来“支持”您的加入,就像我一样。这是因为通常您在查询结尾处有投影部分(选择)。在这里你必须创建一个临时对象。

答案 1 :(得分:1)

您可以查看使用compiled query来提高性能。引用:

  

“当您在实体框架中有多次执行结构相似的查询的应用程序时,您可以通过一次编译查询并使用不同的参数多次执行它来经常提高性能。例如,应用程序可能必须检索特定城市中的所有客户;用户在表单中在运行时指定城市.LINQ to Entities支持使用编译查询。“

答案 2 :(得分:1)

您应该只使用导航属性。如果您已正确映射实体,那么实体框架应该为您处理“连接”。

请参阅我的问题Why use LINQ Join on a simple one-many relationship?

而不是

from b in rc.Pressures
join g in rc.GUIDIdentityMaps on b.UserIdentity equals g.UserIdentity
where g.UserId == userId

只需致电

rc.Pressures.Include("GUIDIdentityMaps")

或者如果您愿意

from b rc.Pressures.Include("GUIDIdentityMaps")
select b