C# Linq到实体:一次选择多对多

C# Linq到实体:一次选择多对多,c#,entity-framework,linq-to-entities,C#,Entity Framework,Linq To Entities,我有这个域名: 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

我有这个域名:

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语句来实现,但我不想这样

我该怎么做呢?

试试这个:

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()
        });
试试这个:

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()
        });

实际上,您只需对用户设置的
UserApplications
实体进行分组:

context
    .UserApplications
    .GroupBy(_ => _.User, _ => _.Application)
    .ToList();
因为,事实上,
i分组
是您所需要的(
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();

实际上,您只需对用户设置的
UserApplications
实体进行分组:

context
    .UserApplications
    .GroupBy(_ => _.User, _ => _.Application)
    .ToList();
因为,事实上,
i分组
是您所需要的(
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();

我不记得实体框架是否可以基于实体对象本身执行
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) };
如果您希望已提取
用户

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到对象查询。

我不记得实体框架是否可以基于实体对象本身执行
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) };
如果您希望已提取
用户

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查询。

我担心,这将失败,因为
tmp
仍然是
IQueryable
,因此
ToArray
无法转换为SQL查询。我担心,这将失败,由于
tmp
仍然是
IQueryable
,因此
ToArray
无法转换为SQL查询。谢谢@Dennis!仅仅通过linq到entitites就可以做到这一点吗?我的意思是,“from”、“group”和“select”子句是什么?这是LINQtoEntities,只是另一种表单方法语法(而您的示例使用查询语法)。谢谢@Dennis!仅仅通过linq到entitites就可以做到这一点吗?我的意思是,有“from”、“group”和“select”子句吗?这是LINQtoEntities,只是另一种表单方法语法(而您的示例使用查询语法)。谢谢。这是可行的,但应用程序必须是列表,而不是列表。它只需要从
g
!:)的项中提取
Application
属性最好不要将Linq-to-Object(扩展)方法调用(如
ToList
)与Linq-to-Entity查询混淆。而是迭代Linq到实体查询,并将模型转换为(比如)视图模型对象(如果您进行更多的编码并以惰性的方式从数据库中提取它们,而不是将它们全部拉入内存,这将是完美的;您将看到分页等任务的不同,在这种情况下,不需要完整的答案)。谢谢。这是可行的,但应用程序必须是列表,而不是列表。它只需要从
g
!:)的项中提取
Application
属性,最好不要将诸如
ToList
之类的Linq to Object(扩展)方法调用与Linq to Entity查询混淆。而是迭代Linq到实体查询,并将模型转换为(比如)视图模型对象(如果您进行更多的编码并以一种懒惰的方式从数据库中提取它们,而不是将它们全部拉入内存,那将是完美的;您将看到分页之类的任务的区别,在这种情况下,不需要完整的答案集)。