Linq to Entities,Inject Join Query Query Expression

时间:2015-08-20 20:44:20

标签: c# linq linq-to-entities entity-framework-6

我试图提高一些LINQ查询的性能,并且有一个小的东西有很大的改进空间:连接。 我的几乎所有查询都有一些用于过滤结果的连接,但未被选择为de result。这些过滤条件是可选的......

我今天所拥有的是:

var q = from t1 in context.Set<T1>() 
    where t1.mandatoryfilter >= 0
    select t1;

if (useFilter) 
{
    var q2 = from t1 in q
        from t2 in context.Set<T2>().Where(t2 => t2.fk == t1.pk).DefaultIfEmpty
        where t2.filterProperty == filterValue
        select t1;

    if (useFilter2) 
    {
        [...]

        return q3.ToList();
    }

    return q2.ToList();
} 
else 
{
    if (useFilter2) 
    {
        [...]

        return q2.ToList();
    }

    return q.ToList();
}

这将生成查询的投影。并且根据过滤器的复杂性/数量,它将生成越来越多的投影,如果我需要组合一些过滤器,则需要嵌套代码。生成的查询可能会增长到太大而无法通过Internet发送的大小(实际上不是太大的XD,但它会成为性能问题)并且代码本身难以维护。

我知道我可以改回字符串SQL ......但这不是一个非常优雅的解决方案吗?

我想知道是否有一种注入连接的方法,并且过滤器直接执行表达式树,因此提供者不会生成投影,代码将是线性和简单的。

ps:非常重要的信息,我使用的是EF6,Linq-to-Entities,默认的SqlClient。

提前谢谢大家。

2 个答案:

答案 0 :(得分:0)

我对LINQ的查询语法版本并不太自信,并且没有VS打开,所以我使用方法语法来确保我得到它正确:但同样的想法适用。< / p>

这是应用过滤器的一种相当标准的方法。您不是每次都创建新查询,只需为每个过滤器附加一个谓词。也适用于连接(除非您使用自定义连接,否则您可能不需要明确使用.Join

var people = context.Set<Person>()
                .Where(p => p.mandatoryFilter > 0);

if (filter.WithAddress != null)
    people = people.Where(p => p.Address == filter.WithAddress);

if (filter.WithBossName != null)
    people = people.Where(p => p.Boss.Name == filter.WithBossName);

return people.ToList();

答案 1 :(得分:0)

如果使用导航属性,几乎不需要连接,并且可以将大多数过滤条件应用为单个表达式。

假设您的public class App42Manager : MonoBehaviour { private const string apiKey = "******"; private const string secretKey = "*****"; //Service for creating, authenticating, deleting, updating User. private UserService userService; //User object that keeps informations about user. public static User user = new User(); string username = ""; string password = ""; string email = ""; void Start () { App42API.Initialize(apiKey,secretKey); userService = App42API.BuildUserService(); } // call this function when user enter all informations and click button void CreateUser(string usrnm,string psswrd, string eml) { username = usrnm; password = psswrd; email = eml; //You can write your own conditions here. if(!username.Equals("") && !password.Equals("") && !email.Equals("") userService.CreateUser(username,password,email,CreateUserCallBack()); } } public class CreateUserCallBack : App42CallBack { public void OnSuccess(object response) { User user = (User)response; /* This will create user in App42 cloud and will return User object */ Debug.Log("CreateUser: userName is" + user.GetUserName()); Debug.Log("CreateUser: email is" + user.GetEmail()); App42Manager.user = this.user; } public void OnException(System.Exception e) { Debug.Log("CreateUser: Exception : " + e); } } 实体具有导航属性T1。过滤器表达式如下所示:

T2

您还可以在此处看到,您只需将表达式附加到现有查询q = q.Where(q.T2.filterProperty1 == filterProperty == filterValue);

现在它变成了一个常用的模式,可以将过滤条件添加到q

IQueryable

(请注意,IQueryable<T1> query = from t1 in context.Set<T1>() where t1.mandatoryfilter >= 0 select t1; if (filterValue2 != null) query = query.Where(t => t.T2.filterProperty == filterValue2); if (filterValue3 != null) query = query.Where(t => t.T3.filterProperty == filterValue3); if (filterValue4 != null) query = query.Where(t => t.T4.filterProperty == filterValue4); 应投放到query,否则作业IQueryable无法编译。)

现在你甚至可以将过滤器应用于1:n关联

query = query....

如果您使用if (filterValue5 != null) query = query.Where(t => t.T5s.Any(t5 => t5.filterProperty == filterValue5)); 进行过滤,则无法执行此操作,因为它会将结果集相乘。