2013-01-15 25 views
1

我之前在向查詢添加聯接時獲得了一些幫助。我現在意識到我實際上需要在同一個查詢中添加多個連接,但我嘗試了很多方法(基於原始連接),但不斷收到錯誤。查詢中的多個聯接

我有一個名爲SchemeName模型 -

namespace TRS.Models 
{ 
    public class SchemeName 
    { 
     [Key] 
     public int SchemeNameID { get; set; } 
     [Display(Name = "Scheme Name")] 
     public string Name { get; set; } 

     public virtual ICollection<Benefit> Benefits { get; set; } 
    } 
} 

和多種型號的員工福利,如以下兩個,每個有SchemeIDs -

namespace TRS.Models 
{ 
    public class Pension 
    { 
     [Key] 
     public string UserName { get; set; } 
     public bool PensionRequired { get; set; } 
     public int PensionSchemeNameID { get; set; } 
     public int PensionProviderID { get; set; } 
     public int PensionBenefitLevelID { get; set; } 
     public decimal PensionEmployerCost { get; set; } 
     public decimal PensionEmployeeCost { get; set; } 

     public virtual PensionBenefitLevel PensionBenefitLevel { get; set; } 

     [Required] 
     public virtual User User { get; set; } 
    } 
} 

namespace TRS.Models 
{ 
    public class LifeAssurance 
    { 
     [Key] 
     public string UserName { get; set; } 
     public bool LifeAssuranceRequired { get; set; } 
     public int LifeAssuranceSchemeNameID { get; set; } 
     public int LifeAssuranceProviderID { get; set; } 
     public string LifeAssuranceBenefitLevel { get; set; } 
     public decimal LifeAssuranceEmployerCost { get; set; } 
     public decimal LifeAssuranceEmployeeCost { get; set; } 


     [Required] 
     public virtual User User { get; set; } 
    } 
} 

在我的控制,我有以下 -

var trs = db.Users 
       .Join(db.SchemeNames, 
       user => user.Pension.PensionSchemeNameID, 
       schemeName => schemeName.SchemeNameID, 
       (user, schemeName) => new { User = user, SchemeName = schemeName }) 
       .Where(a => UserIDs.Contains(a.User.UserName)) 
       .Select(a => new TRSViewModel 
      { 
       UserName = a.User.UserName, 
       FirstName = a.User.UserDetails.FirstName, 
       LastName = a.User.UserDetails.LastName, 
       Salary = a.User.UserDetails.Salary, 
       PensionSchemeName = a.SchemeName.Name, 

這對於顯示PensionSchemeName非常適用,但我還需要顯示LifeAssuranceSchemeName(和其他人),但正如我所提到的一直在收到錯誤。我想我只需要添加與LifeAssuranceSchemeName所以我嘗試添加的細節額外加入 -

   var trs = db.Users 
       .Join(db.SchemeNames, 
       user => user.Pension.PensionSchemeNameID, 
       schemeName => schemeName.SchemeNameID, 
       (user, schemeName) => new { User = user, SchemeName = schemeName }) 
       .Join(db.SchemeNames, 
       la => la.Pension.PensionSchemeNameID, 
       schemeName => schemeName.SchemeNameID, 
       (user, schemeName) => new { User = user, SchemeName = schemeName }) 
       .Where(a => UserIDs.Contains(a.User.UserName)) 
       .Select(a => new TRSViewModel 

,但是這給了我一個錯誤 -

錯誤10「AnonymousType#1」不包含「退休金」的定義和沒有擴展方法'退休金'接受類型'AnonymousType#1'的第一個參數可以找到(你是否缺少一個使用指令或程序集引用?)

任何想法如何添加這些額外的聯接?

+0

都在同一個表中的'PensionSchemeName'和'LifeAssuranceSchemeName'值? –

回答

3

我認爲那些連接沒有被正確鏈接。

在第二次連接中,第二個參數引用了外部集合。這將是第一次加入new { User = user, SchemeName = schemeName })的預計匿名類型。

假設有對用戶類的屬性LifeAssurance,然後我猜你想要做的第二加入基於從用戶對象屬性,像這樣:

var trs = db.Users 
      .Join(db.SchemeNames, 
       user => user.Pension.PensionSchemeNameID, 
       schemeName => schemeName.SchemeNameID, 
       (user, schemeName) => new { User = user, 
              SchemeName = schemeName }) 
      .Join(db.SchemeNames, 
       x => x.User.LifeAssurance.LifeAssuranceSchemeNameID, 
       schemeName => schemeName.SchemeNameID, 
       (x, schemeName) => new { User = x.User, 
              PensionSchemeName = x.SchemeName, 
              LifeAssuranceSchemeName = schemeName }) 
      .Where(a => UserIDs.Contains(a.User.UserName)) 
      .Select(a => new TRSViewModel{ 
          UserName = a.User.UserName, 
          FirstName = a.User.UserDetails.FirstName, 
          LastName = a.User.UserDetails.LastName, 
          Salary = a.User.UserDetails.Salary, 
          PensionSchemeName = a.PensionSchemeName.Name, 
          LifeAssuranceSchemeName = a.LifeAssuranceSchemeName.Name 
      }); 

這可以寫上查詢語法(可能更易於閱讀):

var trs = from user in db.Users 
      join pensionSchema in db.SchemeNames 
       on user.Pension.PensionSchemeNameID equals pensionSchema.SchemeNameID 
      join lifeAssuranceSchema in db.SchemeNames 
       on user.LifeAssurance.LifeAssuranceSchemeNameID equals lifeAssuranceSchema.SchemeNameID 
      where UserIds.Contains(user.UserName) 
      select new TRSViewModel{ 
         UserName = user.UserName, 
         FirstName = user.UserDetails.FirstName, 
         LastName = user.UserDetails.LastName, 
         Salary = user.UserDetails.Salary, 
         PensionSchemeName = pensionSchema.Name, 
         LifeAssuranceSchemeName = lifeAssuranceSchema.Name } 

最後,考慮到這些都是內部連接,因此任何用戶沒有退休金或人壽保險架構將不予退還。如果你想左連接,你可以不喜歡的方式描述here(雖然我猜你不需要這個否則像user.Pension.PensionSchemeNameID表達式將拋出一個NullReferenceException如果有用戶沒有退休金)

var query = 
     from user in db.Users 
     from pensionScheme in db.SchemeNames 
       .Where(s => s.SchemeNameID == user.Pension.PensionSchemeNameID) 
       .DefaultIfEmpty() 
     from lifeAssuranceScheme in db.SchemeNames 
       .Where(s => s.SchemeNameID == user.LifeAssurance.LifeAssuranceSchemeNameID) 
       .DefaultIfEmpty() 
     select new { User = user, 
        Pension = pensionScheme, 
        LifeAssurance = lifeAssuranceScheme} 

我也很快測試在內存中的集合使用下面的代碼與查詢,但他們應該通過實體框架被正確地轉換爲SQL:

public class SchemeName 
{ 
    public int SchemeNameID { get; set; } 
    public string Name { get; set; } 
} 

public class Pension 
{ 
    public int PensionSchemeNameID { get; set; } 
} 

public class LifeAssurance 
{ 
    public int LifeAssuranceSchemeNameID { get; set; } 
} 

public class User 
{ 
    public string Name { get; set; } 
    public Pension Pension { get; set; } 
    public LifeAssurance LifeAssurance { get; set; } 
} 

public static class db 
{ 
    public static IEnumerable<User> Users = new List<User>() { 
      new User { Name = "User 1", 
         Pension = new Pension { PensionSchemeNameID = 1 }, 
         LifeAssurance = new LifeAssurance { LifeAssuranceSchemeNameID = 2 } }, 
      new User { Name = "User 2", 
         Pension = new Pension { PensionSchemeNameID = 1 }, 
         LifeAssurance = new LifeAssurance { LifeAssuranceSchemeNameID = 2 } }, 
      new User { Name = "User 3", 
         Pension = new Pension { PensionSchemeNameID = 1 }, 
         LifeAssurance = new LifeAssurance { LifeAssuranceSchemeNameID = -999 }}, 
      new User { Name = "User 4", 
         Pension = new Pension { PensionSchemeNameID = -999 }, 
         LifeAssurance = new LifeAssurance { LifeAssuranceSchemeNameID = 2 } } 
    }; 
    public static IEnumerable<SchemeName> SchemeNames = new List<SchemeName>() { 
       new SchemeName{ SchemeNameID = 1, Name = "Scheme 1" }, 
       new SchemeName{ SchemeNameID = 2, Name = "Scheme 2" } 
    }; 
} 

    private void Run() 
    { 
     var innerJoinQuery1 = db.Users 
        .Join(db.SchemeNames, 
          user => user.Pension.PensionSchemeNameID, 
          schemeName => schemeName.SchemeNameID, 
          (user, schemeName) => new 
          { 
           User = user, 
           SchemeName = schemeName 
          }) 
        .Join(db.SchemeNames, 
          x => x.User.LifeAssurance.LifeAssuranceSchemeNameID, 
          schemeName => schemeName.SchemeNameID, 
          (x, schemeName) => new 
          { 
           User = x.User, 
           PensionSchemeName = x.SchemeName, 
           LifeAssuranceSchemeName = schemeName 
          }) 
        .Where(a => a.User.Name.StartsWith("User ")) 
        .Select(a => new 
        { 
         UserName = a.User.Name, 
         PensionSchemeName = a.PensionSchemeName.Name, 
         LifeAssuranceSchemeName = a.LifeAssuranceSchemeName.Name 
        }); 

     var innerJoinQuery2 = from user in db.Users 
        join pensionSchema in db.SchemeNames 
         on user.Pension.PensionSchemeNameID equals pensionSchema.SchemeNameID 
        join lifeAssuranceSchema in db.SchemeNames 
         on user.LifeAssurance.LifeAssuranceSchemeNameID equals lifeAssuranceSchema.SchemeNameID 
        where user.Name.StartsWith("User ") 
        select new 
           { 
            UserName = user.Name, 
            PensionSchemeName = pensionSchema.Name, 
            LifeAssuranceSchemeName = lifeAssuranceSchema.Name 
           }; 

     var lefJoinQuery = 
         from user in db.Users 
         from pensionScheme in db.SchemeNames 
           .Where(s => s.SchemeNameID == user.Pension.PensionSchemeNameID) 
           .DefaultIfEmpty() 
         from lifeAssuranceScheme in db.SchemeNames 
           .Where(s => s.SchemeNameID == user.LifeAssurance.LifeAssuranceSchemeNameID) 
           .DefaultIfEmpty() 
         select new 
         { 
          UserName = user.Name, 
          PensionSchemeName = pensionScheme != null ? pensionScheme.Name : "No Pension", 
          LifeAssuranceSchemeName = lifeAssuranceScheme != null ? lifeAssuranceScheme.Name : "No Life Assurance" 
         }; 

     foreach (var result in innerJoinQuery1) 
      Print(result.UserName, result.PensionSchemeName, result.LifeAssuranceSchemeName); 

     Console.WriteLine(); 

     foreach (var result in innerJoinQuery2) 
      Print(result.UserName, result.PensionSchemeName, result.LifeAssuranceSchemeName); 

     Console.WriteLine(); 

     foreach (var result in lefJoinQuery) 
      Print(result.UserName, result.PensionSchemeName, result.LifeAssuranceSchemeName); 

     Console.WriteLine(); 

     Console.ReadKey(); 
    } 

    private void Print(string user, string pension, string lifeAssurance) 
    { 
     Console.WriteLine(String.Format("User: '{0}', Pension: '{1}', Life Assurance: '{2}'", user, pension, lifeAssurance)); 
    } 
} 

將會產生預期的輸出:

對於第一內連接查詢

用戶: '用戶1',退休金: '方案1',人壽保險: '方案2'

用戶: '用戶2',年金:'方案1' ,人壽保險: '方案2'

對於第二內連接查詢

用戶: '用戶1',退休金: '方案1',人壽保險: '方案2'

用戶: '用戶2',養老金: '方案1',人壽保險: '計劃2'

左連接查詢

用戶: '用戶1',養老金: '方案1',人壽保險: '方案2'

用戶: '用戶2',養老金: '方案1',人壽保險: '方案2'

用戶: '用戶3',養老金:'計劃1',人生保證:'沒有生命保證」

用戶: '用戶4',養老: '沒有退休金',人壽保險: '計劃2'

+0

非常感謝。現在正在工作,而且查詢語法更容易閱讀。 –

+0

@MartinMcMahon,我很高興我可以幫助:) –