我在EF Code First中有一个包含很多属性的用户类,每个用户都有一个“联系人”集合,其他用户作为总用户群的子集。另一个集合“ContactOfOthers”反过来显示谁将此用户作为联系人,因为这是一个多对多的关系。
public class User {
[Key]
public string UserName { get; set; }
// Lots of other properties not relevant to this question...
[NotMapped]
public bool IsMyContact { get; set; }
public virtual ICollection<User> Contacts { get; set; }
public virtual ICollection<User> ContactOfOthers { get; set; }
}
我引入了一个名为 IsMyContact 的非映射(未映射到数据库)属性。这适用于用户查询一堆用户的情况,我需要在View中显示哪些用户已经在他们的联系人列表中。因此,如果用户是其“联系人”集合的一部分,则此属性应为true。它不应该保存到数据库,因为对于同一个用户可能会有所不同,具体取决于执行查询的用户。
在上下文的查询中有一个很好的方法吗?它当然可以通过做两个查询然后遍历主要查询来强制强制,寻找与用户的Contacts集合的匹配,但是我想知道是否有更优雅的方法从一个查询执行此操作,投影运行 - 时间计算列到此属性?
答案 0 :(得分:3)
我不知道如何在查询中直接填充IsMyContact
中的User
属性。但另一种方法可能是引入一个ViewModel,它包装User
并且还有IsMyContact
标志:
public class UserViewModel
{
public bool IsMyContact { get; set; }
public User User { get; set; }
}
(班级User
将不再有IsMyContact
旗帜。)
然后,您可以在运行查询时投影到此类型:
string me = "That's me"; // name of the user who is selecting
List<UserViewModel> list = context.Users
.Where(u => ...some filter...)
.Select(u => new UserViewModel
{
IsMyContact = u.ContactOfOthers.Any(c => c.UserName == me),
User = u
})
.ToList();
好处是:您只需要往返一次,而不是被迫加载Contacts
的整个集合来确定IsMyContactFlag
(但如果您愿意,也可以。)
缺点:您需要这个额外的ViewModel类型。
答案 1 :(得分:2)
可以这样做,但它远非“好的方式”,因为你无法返回User
类型的实例。您必须编写自定义linq-to-entities查询,您必须解决两个问题:
所以关于这样做的高级未经考验的想法是:
var query = from u in ctx.Users
where u.Id != id // don't include current user - you can add other condition
join c in ctx.Users
.Where(x => x.Id == id) // current user
.SelectMany(x => x.Contacts)
on u.Id equals c.Id into leftJoin
from y in leftJoin.DefaultIfEmpty()
select new
{
UserName = u.UserName,
IsMyContact = y != null
};
这应该是一个查询,如果用户是否联系,将加载UserName
对和信息。如果您想要User
实例,则必须执行以下操作:
var users = query.AsEnumerable()
.Select(new User
{
// Project to list in linq-to-objects
});