2014-12-02 84 views
0

我有這個域名:LINQ到實體:多對多在一個單一的選擇

public class User 
{ 
    public long Id { get; set; } 
    public string Login { get; set; } 
    public string Password { get; set; } 
} 

public class Application 
{ 
    public long Id { get; set; } 
    public string Name { get; set; } 
} 

public class UserApplications 
{ 
    [ForeignKey("User")] 
    public long UserId { get; set; } 
    public User User { get; set; } 

    [ForeignKey("Application")] 
    public long ApplicationId { get; set; } 
    public Application Application { get; set; } 

    public DateTime LastConnection { get; set; } 
} 

我希望做一個選擇返回類似的東西:

List of select new 
{ 
    User = user, 
    Applications = applications // List of all user's applications 
} 

我嘗試:

from u in Users 
join ua in UserApplications on u.Id equals ua.UserId into userApplications 
from ua in userApplications.DefaultIfEmpty() 
join a in Applications on ua.ApplicationId equals a.Id into applications 
select new 
{ 
    User = u, 
    Applications = applications 
} 

但是,這會重複用戶到每個應用程序。

我知道我可以在兩個select語句中做到這一點,但我不希望這樣做。

我該怎麼做?

回答

2

我不記得,如果實體框架可以基於實體對象本身做groupby(並且在場景後面提取它Id並且替換適合的東西等等);但是這個代碼適用於這種情況:

var q = from uapp in cntxt.UserApplications 
     group uapp by uapp.UserId 
      into g 
      select new { UserId = g.Key, Applications = g.Select(x => x.Application) }; 

如果你願意有User已經提取:

var q2 = from uapp in cntxt.UserApplications 
      group uapp by uapp.UserId 
       into g 
       let u = Users.First(x => x.Id == g.Key) 
       select new { User = u, Applications = g.Select(x => x.Application) }; 

假設你對一個實體框架上下文編寫查詢 - 而不是隻是想做一個Linq to Objects查詢。

+0

謝謝。這可行,但應用程序必須是列表,而不是列表。 – 2014-12-02 08:33:57

+0

它只需要從'g'項目中提取'Application'屬性! :) – 2014-12-02 08:35:33

+0

最好不要將Linq與Linq to Entity查詢混合使用Object(擴展)方法調用,例如'ToList'。相反,將Linq轉換爲Entity查詢並將模型轉換爲(比如說)View Model對象(而且,如果您通過一些更多的代碼並以懶惰的方式從數據庫中提取它們,而不是將它們全部拉入記憶;你會看到像分頁這樣的任務的區別,在這種情況下,沒有實際需要整套答案)。 – 2014-12-02 08:40:42

1

試試這個:

var tmp = 
    from u in Users 
     join ua in UserApplications on u.Id equals ua.UserId 
     join a in Applications on ua.ApplicationId equals a.Id 
    select new 
     { 
      User = u, 
      App = a 
     }; 
var res = tmp 
    .ToArray() // edited 
    .GroupBy(_ => _.User) 
    .Select(_ => new 
     { 
      User = _.Key, 
      Applications = _.Select(_ => _.App).ToArray() 
     }); 
+0

恐怕這樣做會失敗,因爲'tmp'仍然是'IQueryable ',因此'ToArray'不能轉換爲SQL查詢。 – Dennis 2014-12-02 08:10:15

1

其實,你只需要通過用戶設置組UserApplications實體:

context 
    .UserApplications 
    .GroupBy(_ => _.User, _ => _.Application) 
    .ToList(); 

,因爲,事實上,IGrouping<User, Application>是你所需要的(Key是用戶,組項目是他的應用程序)。

任何其他的改進是品味的問題,像投影到匿名類型:

context 
    .UserApplications 
    .GroupBy(_ => _.User, _ => _.Application) 
    .Select(_ => new 
    { 
     User = _.Key, 
     // since IGrouping<User, Application> is IEnumerable<Application>, 
     // we colud return a grouping directly 
     Applications = _ 
    }) 
    .ToList(); 

(另一種投影選項扔掉在Applications組密鑰):

context 
    .UserApplications 
    .GroupBy(_ => _.User, _ => _.Application) 
    .Select(_ => new 
    { 
     User = _.Key, 
     Applications = _.Select(app => app) 
    }) 
    .ToList(); 
+0

謝謝@丹尼斯!有可能只用linq來實現嗎?我的意思是,「來自」,「組」和「選擇」條款的意義? – 2014-12-02 08:18:05

+0

*這是實體的LINQ,只是另一種形式的方法語法(而您的示例使用查詢語法)。 – Dennis 2014-12-02 08:23:19