2

我正在开发一个 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 等)上使用相同类型的代码

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

4

1 回答 1

0

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

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

  1. 用方法定义一个IFilterableFilterClause(string filter)
  2. IFilterable在您的实体上实现类( PersonModel,OrganizationModel等)
  3. IFilterable为将应用您的过滤的对象编写扩展
  4. 查询数据时使用此扩展

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

这是一些说明上述概念的代码。首先,IFilterable在您的实体类( 、 等)上定义和PersonModel实现OrganizationModel

/// <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;
    }
于 2013-06-05T05:46:35.457 回答