MVC,实体框架 - 是否有更好的方法来筛选项目



我正在使用一个单独的类来保留过滤器选项:

    public class FilterViewModel
    {
        public string UserName { get; set; }
        public int? TownId { get; set; }
        ...
     }

在操作中,我使用了一个将过滤器作为参数的谓词。这样,"Where"方法返回IQuerable的IEnumerable instaed:

    public ActionResult FilterProfiles(FilterViewModel filter)
    {
       var profiles = this.Data.Profiles.All()
            .Where(Predicate(filter)) 
            .OrderBy(p => p.ProfileUser.UserName).AsQueryable()
            .Project()
            .To<ProfileViewModel>()
            .ToList();
      }
    private static Func<UserProfile, bool> Predicate(FilterViewModel f)
    {
       return p => (CompareFilter(p, f));
    }
    private static bool CompareFilter(UserProfile profile, FilterViewModel filter)
    {
        if (filter.FirstName != null)
        {
            if (profile.FirstName != null)
            {
                if (profile.FirstName.CompareTo(filter.FirstName) != 0)
                {
                    return false;
                }
            }
       ...
      }

这一直有效,直到在 ProfileViewModel 中,我在映射中实现了数据库 DateTime? 操作:

    public class ProfileViewModel : IHaveCustomMappings
    {
        ...
        public bool IsUserOnline { get; set; }
        ...
        public void CreateMappings(IConfiguration configuration)
        {
            configuration.CreateMap<UserProfile, ProfileViewModel>()
                .ForMember(m => m.IsUserOnline, opt => opt.MapFrom(p =>
                   DbFunctions.DiffMinutes(p.ProfileUser.LastActionTime, DateTime.Now) < 5 ? true : false))
        }
    }

然后在操作中的"Where"方法上出现错误:

[NotSupportedException:此函数只能从 LINQ 调用到实体。 System.Data.Entity.DbFunctions.DiffMinutes(Nullable 1 timeValue1, Nullable 1 timeValue2) +56

我也想知道在这种情况下,IEnumerable "Where" 是否复制内存中的所有数据库项目,然后过滤它们?

提前感谢!

EF 期望Expression<Func<T,bool>>,但您返回的是Func<T,bool>

您必须将其更改为以下

private static Expression<Func<UserProfile, bool>> 
    Predicate(FilterViewModel f)
{
   return CompareFilter(f));
}
private static Expression<Func<UserProfile, bool>>
    CompareFilter(FilterViewModel filter)
{
    if (filter.FirstName != null)
    {
       return p => p.FirstName == filter.FirstName;
    }
   ...
   // this means nothing to compare, 
   // return all records...
   return p => true;
}

如果您想应用多个过滤器,那么您将不得不过滤 IQueryable 本身。

private static IQueryable<UserProfile> 
    Predicate(IQueryable<UserProfile> q, FilterViewModel f)
{
    if (filter.FirstName != null)
    {
        q = q.Where( p => p.FirstName == filter.FirstName );
    }
    if (filter.LastName != null)
    {
        q = q.Where( p => p.LastName == filter.LastName );
    }
   ...
   // return all records...
   return q;
}
为了在服务器上

运行SQL和相关操作,您必须在IQueryable上应用过滤器,该过滤器将在服务器上执行,而不是在本地加载所有过滤器,然后尝试过滤它。

最新更新