EF 5过滤任何列 - 扩展方法?

时间:2013-06-05 05:21:12

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

我正在开发一个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等......)

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

1 个答案:

答案 0 :(得分:0)

使用更正确的问题解决方案更新

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

  1. 使用IFilterable方法
  2. 定义FilterClause(string filter)
  3. 在您的实体(IFilterablePersonModel等)上实施OrganizationModel课程。)
  4. 为将应用过滤的IFilterable个对象编写扩展名
  5. 查询数据时使用此扩展程序
  6. 因此,基本上对于要应用相同过滤的每个实体,您可以实现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;
        }