动态设置要加入LINQ查询的实体 [英] Dynamically set which entity to join in LINQ query

查看:102
本文介绍了动态设置要加入LINQ查询的实体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个查询:

 来自joinedEntities中的joinedEntity 
在dbContext.X中加入relatedEntity .. ..

现在,我想以X动态的方式编写上述查询传递,所以我不需要为每个不同的X写一个单独的linq查询。任何帮助?我应该使用表达式树和反射?如果是,怎么样?谢谢。



我有一些猜测应该是这样的:

  var parameter = Expression.Parameter(typeof(CompanyContext),dbContext); 
var member = Expression.MakeMemberAccess(parameter,typeof(CompanyContext).GetMember(X)[0]);
.....

以下是我的加入示例:

 来自joinedEntities中的joinedEntity 
joinEntity.Id的相关连接在dbContext.Channels中等于relatedEntity.CId
选择新的JoinedEntities( joinEntity)
{
Channel = relatedEntity.Name
};

这是它的方法语法:



新的连接实体(m){(),(m,k)=> Channel = k.Name});

这也是另一个:

 来自joinedEntity的joinEntities 
joinEntity.Id等于relatedEntity.CId
选择新的JoinedEntities(joinedEntity)
{
ActivityOutput = relatedEntity.Name
};


解决方案

最简单的方法是创建一个界面有一个CId字段。

  //不知道你是否需要这个,或者如果你想要joinEntities总是一样的
public interface IJoinedEntities
{
int Id {get;组; }
}

public interface IRelatedEntity
{
int CId {get;组; }
}

所有相关实体都需要实现IRelatedEntity。实体框架的所有类都是部分的,所以你只需要做另外一个局部类来添加这个接口。



由于你希望投影只是创建一个扩展方法,将链接到标准连接。

  public static class CustomIQueryableExtensions 
{
public static IQueryable< TResult> CommonJoinQueryable IQueryable< TInner> inner,
表达式&FunC&TOE,TInner,TResult>>
resultSelector)
其中Touter:IJoinedEntities
其中TInner:IRelatedEntity
{
//必须使用表达式树来构建连接,否则转换为表达式中的接口
var outerParam = Expression 。参数(typeof(TOuter),outer);
var outerBody = Expression.Lambda
var innerParam = Expression.Parameter(typeof(TInner),inner);
var innerBody = Expression.Lambda
return outer.Join(inner,outerBody,innerBody,resultSelector);
}


public static IEnumerable< TResult> CommonJoinEnumerable< TOuter,TInner,TResult>(此IEnumerable< TOuter>外部,
IEnumerable< TInner> inner,
Func&TOE,TInner,TResult>
resultSelector)
:IJoinedEntities
其中TInner:IRelatedEntity
{
//必须使用表达式树来构建连接,否则转换为接口在表达式树
Func< TOuter,int> outerJoin = outerParam => outerParam.Id;
Func< TInner,int> relatedJoin = innerParam => innerParam.CId;

return outer.Join(inner,outerJoin,relatedJoin,resultSelector);
}
}

现在你可以使用它,仍然处理投影

  joinedEntities.CommonJoinQueryable(dbContext.Channels,(m,k)=> new JoinedEntities(){Channel = Name,tracking = m}); 

joinedEntities.CommonJoinEnumerable(dbContext.Channels,(m,k)=> new JoinedEntities(m){Channel = k.Name});

您还应该使用SQL Profiler来观察当您使用Enumerable时生成的内容,因为它将结果拉下来,然后将它们加入到内存中,而不是从SQL中加入。我知道这是你要求的,但这不常见。不要将参数传递给构造函数,您应该考虑使用可以使用投影设置的属性,然后使用IQueryable。



我相信这是什么lnanikian试图得到。


I have a query like :

from joinedEntity in joinedEntities
join relatedEntity in dbContext.X on ....

Now, I want to write the above query in a way that "X" could be dynamically passed so that I don't need to write a separate linq query for every different "X". Any help? Should I use expression trees and reflections? If yes, how? Thank you.

I have some guess that it should be something like this:

var parameter =  Expression.Parameter(typeof (CompanyContext), "dbContext");
var member = Expression.MakeMemberAccess(parameter, typeof(CompanyContext).GetMember("X")[0]);
.....

Here is a sample of my Join:

       from joinedEntity in joinedEntities
       join relatedEntity in dbContext.Channels on joinedEntity.Id equals relatedEntity.CId
        select new JoinedEntities(joinedEntity)
        {
        Channel  = relatedEntity.Name
        };

And this is the method syntax of it:

joinedEntities.Join(dbContext.Channels, m => m.Id, k => k.CId, (m, k) => new JoinedEntities(m)    { Channel = k.Name });

This is also another one:

from joinedEntity in joinedEntities
join relatedEntity in dbContext.ActivityOutput on joinedEntity.Id equals relatedEntity.CId
        select new JoinedEntities(joinedEntity)
         {
              ActivityOutput = relatedEntity.Name
           };

解决方案

The easiest way would be to create an interface that says there is an CId field.

// Don't know if you need this one or if you want to have the class of joinedEntities always being the same
public interface IJoinedEntities
{
    int Id { get; set; }
}

public interface IRelatedEntity
{
    int CId { get; set; }
}

All the related entities would need to implement IRelatedEntity. Entity Framework has all their class are partial so you would just need to make another partial class to add this interface.

Since you want the projections just create an extension method that we will chain into the standard join.

public static class CustomIQueryableExtensions
{
    public static IQueryable<TResult> CommonJoinQueryable<TOuter, TInner, TResult>(this IQueryable<TOuter> outer,
                                                                          IQueryable<TInner> inner,
                                                                          Expression<Func<TOuter, TInner, TResult>>
                                                                              resultSelector)
        where TOuter : IJoinedEntities
        where TInner : IRelatedEntity
    {
        // have to use expression trees to build the join otherwise cast to interface is in expression tree
        var outerParam = Expression.Parameter(typeof (TOuter), "outer");
        var outerBody = Expression.Lambda<Func<TOuter, int>>(Expression.Property(outerParam, "CId"), outerParam);

        var innerParam = Expression.Parameter(typeof (TInner), "inner");
        var innerBody = Expression.Lambda<Func<TInner, int>>(Expression.Property(innerParam, "Id"), innerParam);

        return outer.Join(inner, outerBody, innerBody, resultSelector);
    }


    public static IEnumerable<TResult> CommonJoinEnumerable<TOuter, TInner, TResult>(this IEnumerable<TOuter> outer,
                                                                           IEnumerable<TInner> inner,
                                                                           Func<TOuter, TInner, TResult>
                                                                               resultSelector)
        where TOuter : IJoinedEntities
        where TInner : IRelatedEntity
    {
        // have to use expression trees to build the join otherwise cast to interface is in expression tree
        Func<TOuter, int> outerJoin = outerParam => outerParam.Id;
        Func<TInner, int> relatedJoin = innerParam => innerParam.CId;

        return outer.Join(inner, outerJoin, relatedJoin, resultSelector);
    }
}

Now you can just use it and still handle the projection.

joinedEntities.CommonJoinQueryable(dbContext.Channels, (m, k) => new JoinedEntities()    { Channel = k.Name, tracking = m }); 

joinedEntities.CommonJoinEnumerable(dbContext.Channels, (m, k) => new JoinedEntities(m)    { Channel = k.Name }); 

You should still use SQL Profiler to watch what gets generated when you use the Enumerable because it will pull the results down and then join them in memory, not from SQL. I know that's what you requested but it's not common. Instead of passing a parameter into the constructor you should consider making a property that you can just set with the projection then stick with IQueryable.

I believe this is what lnanikian was trying to get at.

这篇关于动态设置要加入LINQ查询的实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆