How can I create an Lambda Expression for Linq in the Where clause for two tables after the Join?












0















I have this,



 Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
Func<MyCourse, bool> funcWhere = filter.Compile();


and then this,



var myClasses = db.MyCourse.Join(db.People, mc => mc.PersonId, 
p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).Where(???)


The reason I need to do it this way is because if I put the filter on the MyCourse table first with,



db.MyCourse.Where(funcWhere).Join....


The SQL created brings back all of the People and all of the MyCourse and then uses the filter. If I do the where at the end,



(mc, p) => new { MyCourse= mc, Person = p }).Where(mc=>mc.MyCourse.Active == 1)


I get a good query with the Joins. Else the engine queries all the rows into memory first. Two separate queries with thousands of rows.



I have seen a tremendous amount of questions on SO and elsewhere about this. I cannot find one that tells me how to do the Expression when there is more than one table, from a Join, using a dynamic Where Expression<Func<T,TResult>>.



The goal is to make a dynamic query statement based upon Expressions (not Dynamic Linq, and no third party.) In fact, this question claims the Where at the end is slower, but in my program it does the correct query with Joins.



MyCourse has a PersonId and People has a PersonId. If I wrote this by hand it'd look like,



select mc.CourseName, p.LastName 
from MyCourse mc inner join Person p on mc.PersonId = p.PersonId
where mc.Active = 1;


(Those are just example columns for the question. They're not really what I want from the above query, except Active == 1.)



Where clause with Join in lambda expression



Update: FWIW, I was able to get it working this this,



    var param = Expression.Parameter(typeof(MyClass), "MyClassDebug");
var exp = Expression.Lambda<Func<MyClass, bool>>(
Expression.Equal(
Expression.Property(param, dbParameter),
Expression.Constant(dbValue)
),
param
);


I did not do navigation properties or anything else. And I was able to use it like this,



var MyQuery = (from recs in dbcontext.MyClass.Where(exp)
...three joins


The produced SQL looked good, and the Explain plan showed minimal row retrieval.










share|improve this question

























  • Are you using Entity Framework? And is PeopleId in your MyCourse table a foreign key?

    – Gabriel Luci
    Nov 15 '18 at 19:53











  • Yes. It is a foreign key in name only, not at the database defined as such.

    – johnny
    Nov 15 '18 at 19:59











  • I see. It would be easier if it was since Entity Framework would create a reference list for you from your Person class. But anyway, it is still possible to do.

    – Gabriel Luci
    Nov 15 '18 at 20:03











  • For one, the start of the query should be db.MyCourse.Where(filter). But I think that depending on your EF version you should look for the various global query filter options.

    – Gert Arnold
    Nov 15 '18 at 20:04











  • @GertArnold The problem is when I use it that way, Linq brings back all the Person records, 36k and then adjusts the rows for the Active courses. If I put the filter at the end of it with the expression typed out x=>x.MyCourse.Active == 1, then I get a single query with a Join (from what I see on the SQL Profiler).

    – johnny
    Nov 15 '18 at 20:07
















0















I have this,



 Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
Func<MyCourse, bool> funcWhere = filter.Compile();


and then this,



var myClasses = db.MyCourse.Join(db.People, mc => mc.PersonId, 
p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).Where(???)


The reason I need to do it this way is because if I put the filter on the MyCourse table first with,



db.MyCourse.Where(funcWhere).Join....


The SQL created brings back all of the People and all of the MyCourse and then uses the filter. If I do the where at the end,



(mc, p) => new { MyCourse= mc, Person = p }).Where(mc=>mc.MyCourse.Active == 1)


I get a good query with the Joins. Else the engine queries all the rows into memory first. Two separate queries with thousands of rows.



I have seen a tremendous amount of questions on SO and elsewhere about this. I cannot find one that tells me how to do the Expression when there is more than one table, from a Join, using a dynamic Where Expression<Func<T,TResult>>.



The goal is to make a dynamic query statement based upon Expressions (not Dynamic Linq, and no third party.) In fact, this question claims the Where at the end is slower, but in my program it does the correct query with Joins.



MyCourse has a PersonId and People has a PersonId. If I wrote this by hand it'd look like,



select mc.CourseName, p.LastName 
from MyCourse mc inner join Person p on mc.PersonId = p.PersonId
where mc.Active = 1;


(Those are just example columns for the question. They're not really what I want from the above query, except Active == 1.)



Where clause with Join in lambda expression



Update: FWIW, I was able to get it working this this,



    var param = Expression.Parameter(typeof(MyClass), "MyClassDebug");
var exp = Expression.Lambda<Func<MyClass, bool>>(
Expression.Equal(
Expression.Property(param, dbParameter),
Expression.Constant(dbValue)
),
param
);


I did not do navigation properties or anything else. And I was able to use it like this,



var MyQuery = (from recs in dbcontext.MyClass.Where(exp)
...three joins


The produced SQL looked good, and the Explain plan showed minimal row retrieval.










share|improve this question

























  • Are you using Entity Framework? And is PeopleId in your MyCourse table a foreign key?

    – Gabriel Luci
    Nov 15 '18 at 19:53











  • Yes. It is a foreign key in name only, not at the database defined as such.

    – johnny
    Nov 15 '18 at 19:59











  • I see. It would be easier if it was since Entity Framework would create a reference list for you from your Person class. But anyway, it is still possible to do.

    – Gabriel Luci
    Nov 15 '18 at 20:03











  • For one, the start of the query should be db.MyCourse.Where(filter). But I think that depending on your EF version you should look for the various global query filter options.

    – Gert Arnold
    Nov 15 '18 at 20:04











  • @GertArnold The problem is when I use it that way, Linq brings back all the Person records, 36k and then adjusts the rows for the Active courses. If I put the filter at the end of it with the expression typed out x=>x.MyCourse.Active == 1, then I get a single query with a Join (from what I see on the SQL Profiler).

    – johnny
    Nov 15 '18 at 20:07














0












0








0








I have this,



 Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
Func<MyCourse, bool> funcWhere = filter.Compile();


and then this,



var myClasses = db.MyCourse.Join(db.People, mc => mc.PersonId, 
p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).Where(???)


The reason I need to do it this way is because if I put the filter on the MyCourse table first with,



db.MyCourse.Where(funcWhere).Join....


The SQL created brings back all of the People and all of the MyCourse and then uses the filter. If I do the where at the end,



(mc, p) => new { MyCourse= mc, Person = p }).Where(mc=>mc.MyCourse.Active == 1)


I get a good query with the Joins. Else the engine queries all the rows into memory first. Two separate queries with thousands of rows.



I have seen a tremendous amount of questions on SO and elsewhere about this. I cannot find one that tells me how to do the Expression when there is more than one table, from a Join, using a dynamic Where Expression<Func<T,TResult>>.



The goal is to make a dynamic query statement based upon Expressions (not Dynamic Linq, and no third party.) In fact, this question claims the Where at the end is slower, but in my program it does the correct query with Joins.



MyCourse has a PersonId and People has a PersonId. If I wrote this by hand it'd look like,



select mc.CourseName, p.LastName 
from MyCourse mc inner join Person p on mc.PersonId = p.PersonId
where mc.Active = 1;


(Those are just example columns for the question. They're not really what I want from the above query, except Active == 1.)



Where clause with Join in lambda expression



Update: FWIW, I was able to get it working this this,



    var param = Expression.Parameter(typeof(MyClass), "MyClassDebug");
var exp = Expression.Lambda<Func<MyClass, bool>>(
Expression.Equal(
Expression.Property(param, dbParameter),
Expression.Constant(dbValue)
),
param
);


I did not do navigation properties or anything else. And I was able to use it like this,



var MyQuery = (from recs in dbcontext.MyClass.Where(exp)
...three joins


The produced SQL looked good, and the Explain plan showed minimal row retrieval.










share|improve this question
















I have this,



 Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
Func<MyCourse, bool> funcWhere = filter.Compile();


and then this,



var myClasses = db.MyCourse.Join(db.People, mc => mc.PersonId, 
p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).Where(???)


The reason I need to do it this way is because if I put the filter on the MyCourse table first with,



db.MyCourse.Where(funcWhere).Join....


The SQL created brings back all of the People and all of the MyCourse and then uses the filter. If I do the where at the end,



(mc, p) => new { MyCourse= mc, Person = p }).Where(mc=>mc.MyCourse.Active == 1)


I get a good query with the Joins. Else the engine queries all the rows into memory first. Two separate queries with thousands of rows.



I have seen a tremendous amount of questions on SO and elsewhere about this. I cannot find one that tells me how to do the Expression when there is more than one table, from a Join, using a dynamic Where Expression<Func<T,TResult>>.



The goal is to make a dynamic query statement based upon Expressions (not Dynamic Linq, and no third party.) In fact, this question claims the Where at the end is slower, but in my program it does the correct query with Joins.



MyCourse has a PersonId and People has a PersonId. If I wrote this by hand it'd look like,



select mc.CourseName, p.LastName 
from MyCourse mc inner join Person p on mc.PersonId = p.PersonId
where mc.Active = 1;


(Those are just example columns for the question. They're not really what I want from the above query, except Active == 1.)



Where clause with Join in lambda expression



Update: FWIW, I was able to get it working this this,



    var param = Expression.Parameter(typeof(MyClass), "MyClassDebug");
var exp = Expression.Lambda<Func<MyClass, bool>>(
Expression.Equal(
Expression.Property(param, dbParameter),
Expression.Constant(dbValue)
),
param
);


I did not do navigation properties or anything else. And I was able to use it like this,



var MyQuery = (from recs in dbcontext.MyClass.Where(exp)
...three joins


The produced SQL looked good, and the Explain plan showed minimal row retrieval.







c# entity-framework linq lambda expression-trees






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 16 '18 at 22:59









Tetsuya Yamamoto

16.8k42242




16.8k42242










asked Nov 15 '18 at 19:44









johnnyjohnny

9,14341129210




9,14341129210













  • Are you using Entity Framework? And is PeopleId in your MyCourse table a foreign key?

    – Gabriel Luci
    Nov 15 '18 at 19:53











  • Yes. It is a foreign key in name only, not at the database defined as such.

    – johnny
    Nov 15 '18 at 19:59











  • I see. It would be easier if it was since Entity Framework would create a reference list for you from your Person class. But anyway, it is still possible to do.

    – Gabriel Luci
    Nov 15 '18 at 20:03











  • For one, the start of the query should be db.MyCourse.Where(filter). But I think that depending on your EF version you should look for the various global query filter options.

    – Gert Arnold
    Nov 15 '18 at 20:04











  • @GertArnold The problem is when I use it that way, Linq brings back all the Person records, 36k and then adjusts the rows for the Active courses. If I put the filter at the end of it with the expression typed out x=>x.MyCourse.Active == 1, then I get a single query with a Join (from what I see on the SQL Profiler).

    – johnny
    Nov 15 '18 at 20:07



















  • Are you using Entity Framework? And is PeopleId in your MyCourse table a foreign key?

    – Gabriel Luci
    Nov 15 '18 at 19:53











  • Yes. It is a foreign key in name only, not at the database defined as such.

    – johnny
    Nov 15 '18 at 19:59











  • I see. It would be easier if it was since Entity Framework would create a reference list for you from your Person class. But anyway, it is still possible to do.

    – Gabriel Luci
    Nov 15 '18 at 20:03











  • For one, the start of the query should be db.MyCourse.Where(filter). But I think that depending on your EF version you should look for the various global query filter options.

    – Gert Arnold
    Nov 15 '18 at 20:04











  • @GertArnold The problem is when I use it that way, Linq brings back all the Person records, 36k and then adjusts the rows for the Active courses. If I put the filter at the end of it with the expression typed out x=>x.MyCourse.Active == 1, then I get a single query with a Join (from what I see on the SQL Profiler).

    – johnny
    Nov 15 '18 at 20:07

















Are you using Entity Framework? And is PeopleId in your MyCourse table a foreign key?

– Gabriel Luci
Nov 15 '18 at 19:53





Are you using Entity Framework? And is PeopleId in your MyCourse table a foreign key?

– Gabriel Luci
Nov 15 '18 at 19:53













Yes. It is a foreign key in name only, not at the database defined as such.

– johnny
Nov 15 '18 at 19:59





Yes. It is a foreign key in name only, not at the database defined as such.

– johnny
Nov 15 '18 at 19:59













I see. It would be easier if it was since Entity Framework would create a reference list for you from your Person class. But anyway, it is still possible to do.

– Gabriel Luci
Nov 15 '18 at 20:03





I see. It would be easier if it was since Entity Framework would create a reference list for you from your Person class. But anyway, it is still possible to do.

– Gabriel Luci
Nov 15 '18 at 20:03













For one, the start of the query should be db.MyCourse.Where(filter). But I think that depending on your EF version you should look for the various global query filter options.

– Gert Arnold
Nov 15 '18 at 20:04





For one, the start of the query should be db.MyCourse.Where(filter). But I think that depending on your EF version you should look for the various global query filter options.

– Gert Arnold
Nov 15 '18 at 20:04













@GertArnold The problem is when I use it that way, Linq brings back all the Person records, 36k and then adjusts the rows for the Active courses. If I put the filter at the end of it with the expression typed out x=>x.MyCourse.Active == 1, then I get a single query with a Join (from what I see on the SQL Profiler).

– johnny
Nov 15 '18 at 20:07





@GertArnold The problem is when I use it that way, Linq brings back all the Person records, 36k and then adjusts the rows for the Active courses. If I put the filter at the end of it with the expression typed out x=>x.MyCourse.Active == 1, then I get a single query with a Join (from what I see on the SQL Profiler).

– johnny
Nov 15 '18 at 20:07












1 Answer
1






active

oldest

votes


















2














I suspect calling Compile() on your Expression is causing your trouble. Your full query includes the Join, but you've already compiled the Where clause, so it can't compile the entire query including the Join together. That might be why it's grabbing the entire table because it's executing the Where by itself first, then doing the Join later.



But you don't need to call Compile(). Just pass the Expression into Where():



Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
var myClasses = db.MyCourse
.Where(filter)
.Join(db.People, mc => mc.PersonId,
p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).ToList();


Somewhat unrelated to your actual problem, but if you created the foreign keys, you can simplify this a bit. Update your model in your Visual Studio project if you haven't already. Your Person class will change to have a list of MyCourse and your MyCourse class will have a list of Person.



So you can do something like:



Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
var courses = db.MyCourse.Include("Person").Where(filter);
foreach (var course in courses) {
var person = course.Person; //This is populated with the Person record
}


Linq handles the join, and each MyCourse returned will have a Person property.






share|improve this answer

























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53326868%2fhow-can-i-create-an-lambda-expression-for-linq-in-the-where-clause-for-two-table%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2














    I suspect calling Compile() on your Expression is causing your trouble. Your full query includes the Join, but you've already compiled the Where clause, so it can't compile the entire query including the Join together. That might be why it's grabbing the entire table because it's executing the Where by itself first, then doing the Join later.



    But you don't need to call Compile(). Just pass the Expression into Where():



    Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
    var myClasses = db.MyCourse
    .Where(filter)
    .Join(db.People, mc => mc.PersonId,
    p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).ToList();


    Somewhat unrelated to your actual problem, but if you created the foreign keys, you can simplify this a bit. Update your model in your Visual Studio project if you haven't already. Your Person class will change to have a list of MyCourse and your MyCourse class will have a list of Person.



    So you can do something like:



    Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
    var courses = db.MyCourse.Include("Person").Where(filter);
    foreach (var course in courses) {
    var person = course.Person; //This is populated with the Person record
    }


    Linq handles the join, and each MyCourse returned will have a Person property.






    share|improve this answer






























      2














      I suspect calling Compile() on your Expression is causing your trouble. Your full query includes the Join, but you've already compiled the Where clause, so it can't compile the entire query including the Join together. That might be why it's grabbing the entire table because it's executing the Where by itself first, then doing the Join later.



      But you don't need to call Compile(). Just pass the Expression into Where():



      Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
      var myClasses = db.MyCourse
      .Where(filter)
      .Join(db.People, mc => mc.PersonId,
      p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).ToList();


      Somewhat unrelated to your actual problem, but if you created the foreign keys, you can simplify this a bit. Update your model in your Visual Studio project if you haven't already. Your Person class will change to have a list of MyCourse and your MyCourse class will have a list of Person.



      So you can do something like:



      Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
      var courses = db.MyCourse.Include("Person").Where(filter);
      foreach (var course in courses) {
      var person = course.Person; //This is populated with the Person record
      }


      Linq handles the join, and each MyCourse returned will have a Person property.






      share|improve this answer




























        2












        2








        2







        I suspect calling Compile() on your Expression is causing your trouble. Your full query includes the Join, but you've already compiled the Where clause, so it can't compile the entire query including the Join together. That might be why it's grabbing the entire table because it's executing the Where by itself first, then doing the Join later.



        But you don't need to call Compile(). Just pass the Expression into Where():



        Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
        var myClasses = db.MyCourse
        .Where(filter)
        .Join(db.People, mc => mc.PersonId,
        p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).ToList();


        Somewhat unrelated to your actual problem, but if you created the foreign keys, you can simplify this a bit. Update your model in your Visual Studio project if you haven't already. Your Person class will change to have a list of MyCourse and your MyCourse class will have a list of Person.



        So you can do something like:



        Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
        var courses = db.MyCourse.Include("Person").Where(filter);
        foreach (var course in courses) {
        var person = course.Person; //This is populated with the Person record
        }


        Linq handles the join, and each MyCourse returned will have a Person property.






        share|improve this answer















        I suspect calling Compile() on your Expression is causing your trouble. Your full query includes the Join, but you've already compiled the Where clause, so it can't compile the entire query including the Join together. That might be why it's grabbing the entire table because it's executing the Where by itself first, then doing the Join later.



        But you don't need to call Compile(). Just pass the Expression into Where():



        Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
        var myClasses = db.MyCourse
        .Where(filter)
        .Join(db.People, mc => mc.PersonId,
        p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).ToList();


        Somewhat unrelated to your actual problem, but if you created the foreign keys, you can simplify this a bit. Update your model in your Visual Studio project if you haven't already. Your Person class will change to have a list of MyCourse and your MyCourse class will have a list of Person.



        So you can do something like:



        Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
        var courses = db.MyCourse.Include("Person").Where(filter);
        foreach (var course in courses) {
        var person = course.Person; //This is populated with the Person record
        }


        Linq handles the join, and each MyCourse returned will have a Person property.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 15 '18 at 22:34

























        answered Nov 15 '18 at 20:13









        Gabriel LuciGabriel Luci

        11.4k11525




        11.4k11525
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53326868%2fhow-can-i-create-an-lambda-expression-for-linq-in-the-where-clause-for-two-table%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Bressuire

            Vorschmack

            Quarantine