如何在Entity Framework Code First中创建运行时计算(NotMapped)值

时间:2011-05-28 13:47:51

标签: asp.net-mvc linq entity-framework linq-to-entities

我在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集合的匹配,但是我想知道是否有更优雅的方法从一个查询执行此操作,投影运行 - 时间计算列到此属性?

2 个答案:

答案 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查询,您必须解决两个问题:

  • 您无法投影到linq-to-entities
  • 中的映射类型
  • 您无法访问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
                      });