mysqlasp.net-mvcentity-frameworklinqnested-select

Convert complex nested selects in Entity Framework query


I need to create a table in View by this View Model:

public class ApplicationContentViewModel
{
    public BPMSPARS.Models.MySql.application application {get; set;}
    public BPMSPARS.Models.MySql.content content { get; set; }
    public BPMSPARS.Models.MySql.app_delegation app_delegation { get; set; }
}

But the query for creating new Table is very complex. I use this query in MySQL, and I can get correct results by using it.

SELECT APP_UID, (SELECT CON_VALUE FROM content WHERE CON_CATEGORY = 'PRO_TITLE' AND CON_ID = 
(SELECT PRO_UID from app_delegation WHERE del_thread_status='open' and USR_UID = '00000000000000000000000000000001' AND APP_UID = '9134216305aaaea1b67c4e2096663219')) AS TASK_NAME, 
(SELECT CON_VALUE FROM content WHERE CON_CATEGORY = 'TAS_TITLE' AND CON_ID = 
(SELECT TAS_UID from app_delegation WHERE del_thread_status='open' and USR_UID = '00000000000000000000000000000001' AND APP_UID = '9134216305aaaea1b67c4e2096663219')) AS PROCESS_NAME FROM app_delegation 
WHERE del_thread_status='open' and USR_UID = '00000000000000000000000000000001' AND APP_UID = '9134216305aaaea1b67c4e2096663219'

But, I have to convert this query in linq or EF in MVC.

How Can I write This Query in Entity Framework query? And How Can I display results in View?


Solution

  • Your SQL query seems (very) peculiar to me, as it is quite redundant. I am going to assume the sub-queries return a single value and enforce it with LINQ.

    First I pulled out the common sub-query over app_delegation:

    var USR_APP_Delegation = from a in app_delegation
                             where a.del_thread_status == "open" &&
                                   a.USR_UID == "00000000000000000000000000000001" &&
                                   a.APP_UID == "9134216305aaaea1b67c4e2096663219"
                             select a;
    

    In LINQ it is easy to combine the two UID queries into one query:

    var UIDs = (from a in USR_APP_Delegation
                select new { a.PRO_UID, a.TAS_UID })
               .Single();
    

    Now you can do the name subqueries:

    var TASK_NAME = (from c in content
                     where c.CON_CATEGORY == "PRO_TITLE" &&
                     c.CON_ID == UIDs.PRO_UID
                     select c.CON_VALUE)
                    .Single();
    
    var PROCESS_NAME = (from c in content
                        where c.CON_CATEGORY == "TAS_TITLE" &&
                        c.CON_ID == UIDs.TAS_UID
                        select c.CON_VALUE)
                       .Single();
    

    Then you can put all the queries together for the final result:

    var ans = (from a in USR_APP_Delegation
               select new {
                   a.APP_UID,
                   TASK_NAME,
                   PROCESS_NAME
               })
              .Single();
    

    Again, this makes it obvious that your e.g. returning APP_UID when you know exactly what it is, and you are combining TASK_NAME and PROCESS_NAME into a query for no real advantage.

    I would suggest using join against content makes a much more understandable query (even in SQL) and makes it clearer what is being returned:

    var names = from a in app_delegation
                join cpro in content on new { CON_ID = a.PRO_UID, CON_CATEGORY = "PRO_TITLE" } equals new { cpro.CON_ID, cpro.CON_CATEGORY }
                join ctas in content on new { CON_ID = a.PRO_UID, CON_CATEGORY = "TAS_TITLE" } equals new { ctas.CON_ID, ctas.CON_CATEGORY }
                where a.del_thread_status == "open" &&
                      a.USR_UID == "00000000000000000000000000000001" &&
                      a.APP_UID == "9134216305aaaea1b67c4e2096663219"
                select new {
                    a.APP_UID,
                    Task_Name = ctas.CON_VALUE,
                    Process_Name = cpro.CON_VALUE
                };