2014-11-23 59 views
0

我有兩個由ASP.NET身份使用的類。一個是用戶列表的支持類,另一個是角色。在數據庫中有三個表。 AspNetUsers,AspNetRoles和AspNetUserRoles。因爲這樣,他們在實體框架映射這三個被映射到只有兩個類:如何從一次到多次使用LINQ獲取SingleOrDefault值?

public partial class AspNetUser 
{ 
    public AspNetUser() 
    { 
     this.AspNetUserClaims = new List<AspNetUserClaim>(); 
     this.AspNetUserLogins = new List<AspNetUserLogin>(); 
     this.AspNetRoles = new List<AspNetRole>(); 
    } 
    public int Id { get; set; } 
    public virtual ICollection<AspNetRole> AspNetRoles { get; set; } 
    public virtual ICollection<AspNetUserClaim> AspNetUserClaims { get; set; } 
    public virtual ICollection<AspNetUserLogin> AspNetUserLogins { get; set; } 
} 

public partial class AspNetRole 
{ 
    public AspNetRole() 
    { 
     this.AspNetUsers = new List<AspNetUser>(); 
    } 

    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual ICollection<AspNetUser> AspNetUsers { get; set; } 
} 

我有我用來做數據的一份報告中LINQ聲明。請注意,我只想獲得與用戶關聯的第一個角色。下面是我的嘗試:

 var user = await db.AspNetUsers 
      .Where(u => u.AspNetRoles.Any(r => r.Id == roleId) || roleId == 0) 
      .Include(u => u.AspNetRoles) 
      .Select(u => new UserDTO 
      { 
       Email = u.Email, 
       RoleSingleId = u.AspNetRoles.SingleOrDefault(r => r.Id), 
       UserId = u.Id, 
       UserName = u.UserName 
      }) 
      .ToListAsync(); 

但是這給了我兩個錯誤與藍線r.Id

錯誤4無法轉換lambda表達式的委託類型「System.Func」,因爲一些返回類型 在塊中不能隱式轉換爲委託返回類型

錯誤5不能將類型'int'隱式轉換爲'bool'C:\ App \ ab41 \ Admin1 \ WebRole1 \ Controllers \ Web API - Data \ UserController。 cs 169 71 WebRole1

注意RoleSingleId被聲明爲一個int,而AspNetRoles中的Id也是一個int。

+0

正如所發佈的那樣,您錯過了'.Where'這一行的結束語。 – dursk 2014-11-23 15:29:52

回答

5

有幾個問題。我們從提供編譯時錯誤的那個開始。

RoleSingleId = u.AspNetRoles.SingleOrDefault(r => r.Id), 

SingleOrDefault需要謂詞,而不是一個投影。作爲參數傳遞的是一個條件,該條件指示是否應考慮指定的AspNetRole。這是u.AspNetRoles.Where(r => r.Id).SingleOrDefault()的簡稱。

這不是你想要的。使用Select進行投影,而不是使用Where,以便可以通過將其編寫爲u.AspNetRoles.Select(r => r.Id).SingleOrDefault()來修復該部分。

修復該問題後,下一個問題是SingleOrDefault()被設計爲在發現多個項目時拋出異常。這不是你想要的。你說你想要返回其中一個角色ID。那有FirstOrDefault()

第三個問題整齊地被隱藏起來,SingleOrDefault()通常在查詢中不被支持。它會進行編譯,但它會在運行時拋出一個異常,無論用戶有多少角色,都會告訴你它不知道如何去做,而且你可能想用FirstOrDefault()來代替。由於FirstOrDefault()是你應該使用的,這不是問題。

注意:您可能還想將Select(r => r.Id)更改爲Select(r => (int?)r.Id):前者返回IEnumerable<int>,後者返回IEnumerable<int?>FirstOrDefault()將返回0爲前者,如果用戶沒有角色(如果它工作的話),null爲後者。 null可能在這裏更有意義。您需要以相同的方式調整RoleSingleId的類型。

1

如果您使用SelectMany而不是Select,那麼您的返回將變得非常像傳統的SQL語句。即用戶細節將針對該用戶的每個角色重複。

獲得單或默認用戶/角色會是這個樣子:

var user = await db.AspNetUsers 
     .Where(u => u.AspNetRoles.Any(r => r.Id == roleId) || roleId == 0) 
     .Include(u => u.AspNetRoles) 
     .SelectMany(u => new UserDTO 
     { 
      Email = u.Email, 
      RoleSingleId = u.AspNetRoles.Id, 
      UserId = u.Id, 
      UserName = u.UserName 
     }) 
     .SingleOrDefault(); 

當然,如果你正在試圖獲得針對每個用戶的第一個角色列表,然後你需要的GroupBy用戶ID,用戶名和電子郵件,以某種方式聚合角色ID(例如獲取每個用戶的最大值)。

var user = await db.AspNetUsers 
     .Where(u => u.AspNetRoles.Any(r => r.Id == roleId) || roleId == 0) 
     .Include(u => u.AspNetRoles) 
     .SelectMany(u => new UserDTO 
     { 
      Email = u.Email, 
      RoleSingleId = u.AspNetRoles.Id, 
      UserId = u.Id, 
      UserName = u.UserName 
     }) 
     .GroupBy(key => new { key.Email, key.UserId, key.UserName }, 
     (key, agg) => new UserDTO 
      { 
       Email = key.Email, 
       UserId = key.Id, 
       UserName = key.UserName, 
       RoleSingleId = agg.Max(a => a.RoleSingleId), 
      }) 
     .ToListAsync(); 
+0

謝謝,但這只是顯示一個用戶還是很多?我想向每個用戶展示第一個角色。 – 2014-11-23 15:34:29

+1

編輯回答。你可能可以使LINQ更整潔,但你明白了:) – 2014-11-23 15:36:56