2

CRM タイプのアプリケーションを開発しています。実装している機能の 1 つは、データのグリッドを表示し、単一のテキスト ボックスを使用して任意の列から値をフィルター処理できるようにすることです。私はかなり醜い解決策を思いつきました (このメソッドも 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. メソッドでIFilterableクラスを定義するFilterClause(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メソッドを書き換えることができます (以下を参照)。また、totalCount値を設定するためだけに ID を最初に照会し、最終的な照会にはページ化された 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 に答える