asp.net mvc-如何在实体框架代码优先中创建运行时计算(NotMapped)值



我在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; }
}

我引入了一个未映射(未映射到DB)的属性,名为IsMyContact。这适用于用户查询一群用户时,我需要在视图中显示哪些用户已经在他们的联系人列表中。因此,如果用户是其"联系人"集合的一部分,则此属性应为true。它不应该保存到DB中,因为对于同一个用户,它可能不同,这取决于执行查询的用户。

有没有一种很好的方法可以在上下文中的查询中做到这一点?当然,这可能是通过执行两个查询,然后迭代主查询,寻找与用户的Contacts集合匹配的内容来强制执行的,但我想知道是否有更优雅的方法可以从一个查询中做到这一点,将运行时计算的列投影到该属性上?

我不知道如何在查询中直接填充User中的IsMyContact属性。但另一种方法可能是引入一个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类型。

这样做是可能的,但这不是一种"好方法",因为您无法返回User类型的实例。您必须编写自定义linq到实体查询,并且必须解决两个问题:

  • 不能将linq中的映射类型投影到实体
  • 无法访问linq到实体中的未映射特性

因此,我的高级未经测试的想法是:

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

相关内容

  • 没有找到相关文章

最新更新