【问题标题】:EF 5 Filtering on any column - Extension method?EF 5 过滤任何列 - 扩展方法?
【发布时间】:2013-05-31 16:35:13
【问题描述】:

我正在开发一个 CRM 类型的应用程序,我正在实现的功能之一是能够查看数据网格,然后使用单个文本框来过滤来自任何列的值。我想出了一个非常丑陋的解决方案(请注意,此方法还使用 Dynamic Linq 对数据进行排序),但是我希望它更“通用”,可能使用反射,所以我可以调用 WithFiltering 扩展方法和提供过滤条件的扩展方法。这是我目前所拥有的:

public List<PersonModel> GetPeople(int owningOrganisationID, int skip, int records, out int totalCount, Ordering orderByDirection, string filter, string orderBy = "")
    {
        if (string.IsNullOrEmpty(orderBy))
            orderBy = "PersonID";

        if (!string.IsNullOrEmpty(filter))
        {
            filter = filter.ToLower();

            totalCount = Context.PeopleView.Where(p => p.OwningOrganisationID == owningOrganisationID &&
                p.City.ToLower().Contains(filter)
                || p.CountryName.ToLower().Contains(filter)
                || p.Forename.ToLower().Contains(filter)
                || p.PersonTypeName.ToLower().Contains(filter)
                || p.Postcode.ToLower().Contains(filter)
                || p.Surname.ToLower().Contains(filter)).Count();

            return Context.PeopleView.Where(p => p.OwningOrganisationID == owningOrganisationID &&
                p.City.ToLower().Contains(filter)
                || p.CountryName.ToLower().Contains(filter)
                || p.Forename.ToLower().Contains(filter)
                || p.PersonTypeName.ToLower().Contains(filter)
                || p.Postcode.ToLower().Contains(filter)
                || p.Surname.ToLower().Contains(filter))
                    .OrderBy(orderBy + " " + orderByDirection.ToString())
                    .Skip(skip)
                    .Take(records)
                    .ToList();
        }
        else
        {
            totalCount = Context.PeopleView.Where(p => p.OwningOrganisationID == owningOrganisationID).Count();

            return Context.PeopleView
                .Where(o => o.OwningOrganisationID == owningOrganisationID)
                .OrderBy(orderBy + " " + orderByDirection.ToString())
                .Skip(skip)
                .Take(records)
                .ToList();
        }
    }

这不仅看起来很糟糕,而且容易出错,我将在几个不同的实体(PersonModel、OrganizationModel、DocumentModel 等)上使用相同类型的代码

只是想知道是否有人对更简洁的代码有更好的想法?

【问题讨论】:

    标签: c# entity-framework c#-4.0 entity-framework-5 extension-methods


    【解决方案1】:

    更新了更正确的问题解决方案

    不使用反射,您可以使用以下模式:

    1. 使用FilterClause(string filter) 方法定义IFilterable
    2. 在您的实体上实现IFilterable 类(PersonModelOrganizationModel 等)
    3. IFilterable 对象编写扩展,以应用您的过滤器
    4. 查询数据时使用此扩展

    因此,基本上对于您想要应用相同过滤的每个实体,您可以实现IFilterable 并定义哪些 属性以及如何 这些属性将用于过滤。

    这里有一些代码说明了上述概念。首先,定义IFilterable 并在您的实体类上实现它(PersonModelOrganizationModel 等)

    /// <summary>
    /// Specifies that the class can be filterable
    /// </summary>
    interface IFilterable {
        /// <summary>
        /// Specifies the way the filtering will occur
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        bool FilterClause(string filter);
    }
    
    //partial class definition for PersonModel
    public partial class PersonModel: IFilterable{
        //entity properties (normaly these are generated by EF)
        public int Id { get; set; }
        public int OwningOrganisationID { get; set; }
        public string City { get; set; }
        public string CountryName { get; set; }
        public string Forename { get; set; }
        public string PersonTypeName { get; set; }
        public string Postcode { get; set; }
        public string Surname { get; set; }
    
        //Implementation of IFiltrable 
        public bool FilterClause(string filter) {
            return City.ToLower().Contains(filter)
                    || CountryName.ToLower().Contains(filter)
                    || Forename.ToLower().Contains(filter)
                    || PersonTypeName.ToLower().Contains(filter)
                    || Postcode.ToLower().Contains(filter)
                    || Surname.ToLower().Contains(filter);
        }
    }
    
    //partial class definition for OrganizationModel
    public partial class OrganizationModel: IFilterable{        
    
        //Implementation of IFiltrable 
        public bool FilterClause(string filter) {
            //replace 'true' with code that will apply filtering for the OrganizationModel class
            return  true;
        }
    }
    

    现在在 IQueryable 对象上定义一个扩展,它将对实现“IFilterable”的对象应用过滤

    static class FilterableEntensions{
        /// <summary>
        /// Filters a IFilterable enumeration
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public static IQueryable<T> WithFiltering<T>(this IQueryable<T> query, string filter)  where T: IFilterable  {
            if (string.IsNullOrWhiteSpace(filter)) {
                //filter is empty, return original query
                return query;
            }
            else {
                //apply the filter
                return query.Where(x => x.FilterClause(filter));
            }
        }
    }
    

    使用上面的这些定义,您的GetPeople 方法可以被重写(见下文)。它还首先查询 id 以设置 totalCount 值,并且仅使用分页 id 进行最终查询

    public List<PersonModel> GetPeople(int owningOrganisationID, int skip, int records, out int totalCount, Ordering orderByDirection, string filter, string orderBy = "") {
    
            List<PersonModel> result = new List<PersonModel>();
    
            if (string.IsNullOrEmpty(orderBy)) {
                orderBy = "PersonID";
            }
    
            if (!string.IsNullOrWhiteSpace(filter)) {
                filter = filter.ToLower();
            }
    
            //use a first query to take only the ids of the data that match the filter.
            List<int> peopleViewIds = Context.PeopleView
                .Where(p => p.OwningOrganisationID == owningOrganisationID)
                .WithFiltering(filter) //use our extension here!!!
                .OrderBy(orderBy + " " + orderByDirection.ToString())
                .Select(p => p.Id).ToList();
    
            //set the 'out' parameter to the total count of the retrieved ids
            totalCount = peopleViewIds.Count;
    
            if (totalCount > 0) {
                //page the ids accordingly (the sorting of the ids was applied in the previous query)
                List<int> pagedPeopleViewIds = peopleViewIds.Skip(skip).Take(records).ToList();
    
                //use the paged ids to make a second query, this time only with the required ids. 
                //This should be really fast if you have PersonModel.Id is a PRIMARY KEY or you have an index attached on this column
                //Please note the reuse of the OrderBy, this will ensure the correct order on the paged result
                result = Context.PeopleView.Where(p => pagedPeopleViewIds.Contains(p.Id))
                    .OrderBy(orderBy + " " + orderByDirection.ToString())                    
                    .ToList();
            }
            return result;
        }
    

    【讨论】:

      猜你喜欢
      • 2020-08-29
      • 2013-03-26
      • 1970-01-01
      • 2011-03-11
      • 2021-12-31
      • 1970-01-01
      • 2015-03-12
      • 1970-01-01
      • 2012-10-14
      相关资源
      最近更新 更多