1

これらのタスクはすべてLINQクエリに基づいています。それらをリファクタリングして読みやすくし、言語/地域などに応じてクエリを変更できるようにするための良い方法を探しています。

var mailTaskOne = CreateTask(() => myService.Mail.Where(p => p.ProjectName == "Delta"
    && (p.MailLang== (int)MailLanguage.EU || p.MailLang == (int)MailLanguage.RU)
    && (p.DateEntered >= startDate && p.DateEntered <= endDate)
    && p.MailPriority == (int)MailPriority.High).Count());

私が便利だと思った方法の1つは、クエリを次のようなものに分割することです。

var results = myService.Mail.Where(x => x.ProjectName == "Delta");
results = results.Where(p => p.MailLang== (int)MailLanguage.EU);
results = results.Where(p => p.DateModified >= startDate && p.DateModified <= endDate);

これにより、リージョンごとにクエリ全体を繰り返すことなく、これを実行できます。

if (MailLanguage == "English")
    results = results.Where(p => p.MailLang== (int)MailLanguage.EU);
else
    results = results.Where(p => p.MailLang== (int)MailLanguage.RU);

これに対するより良い解決策を知っている人はいますか?要件に応じて、これらのクエリを20回実行する必要があるため、最終的には巨大な関数を使用することになります。地域、プロジェクト名など。


編集:

バックエンド(Webサービス/ API)では知らなかったいくつかの制限のため、残念ながら、この質問で言及されているすばらしい回答のいくつかを使用できませんでした。

たとえば、これは適切に翻訳されませんが、答えが正しくないため、私が取り組んでいるAPIでは機能しません。おそらく実装が不十分なためです。

public bool IsValid(Type x)
{
    return (x.a == b) && (x.c ==d) && (x.d == e);
}

とにかく、同様の解決策を探している人は誰でもこれらすべてが有効な答えですが、最終的に私は提供された解決策に似たものを使うことになりました。

4

6 に答える 6

2

プロジェクト名、変更されたデータ、メール言語、およびその他の基準を変数に変換し、任意の条件に基づいて必要な値をそれらに与えることができます。次に、クエリはリテラル値ではなく変数を使用します。

var projectName="Delta";
var mailLanguage=(int)MailLanguage.RU;

var results=myService.Mail.Where(x => x.ProjectName == projectName)
            && (p.MailLang== mailLanguage);

そうすれば、複雑さのほとんどを変数に値を与えることに置くことができ、linqクエリは読みやすく、維持しやすくなります。

于 2012-09-03T08:37:36.290 に答える
2

目的のための方法を単純に持たないのはなぜですか?

public static IQueryable<Mail> Count(this IQueryable<Mail> mails, 
                  string projectName, 
                  MailLanguage mailLanguage,
                  DateTime startDate,
                  DateTime endDate) {
    return mails.Count(p=>
           p.ProjectName == projectName
           && p.MailLang == mailLanguage
           && p.DateEntered >= startDate 
           && p.DateEntered <= endDate
           && p.MailPriority == (int)MailPriority.High);
}

次に、このように簡単に使用できます

CreateTask(() => myService.Mail.Count("Delta",MailLanguage.EU,startDate,endDate));
于 2012-09-03T08:28:00.787 に答える
2

あなたが提案したように、クエリをさまざまな行に分割するだけで済みます。つまり、行ごとにコメントを付けて、何をしているのかを説明できます。データベースへのアクセスはまだ 1 回しかないため、パフォーマンスの面で何も失われることはありませんが、読みやすさは向上します。

于 2012-09-03T08:19:43.810 に答える
1

複雑な比較を関数に移動することを検討してください。たとえば、代わりに

Results.Where(x => (x.a == b) && (x.c == d) && (x.d == e))

検討

Results.Where(x => IsValid(x))

...

public bool IsValid(Type x)
{
    return (x.a == b) && (x.c ==d) && (x.d == e);
}

コードが読みやすくなり、自動テスト フレームワークを使用して IsValid を簡単にテストできます。

于 2012-09-03T08:27:31.067 に答える
1

次のようなパラメーター クラスを作成できます。

public class MailParameters
{
    public DateTime EndTime { get; private set; }
    public IEnumerable<int> Languages { get; private set; }
    public int Priority { get; private set; }
    public string ProjectName { get; private set; }
    public DateTime StartTime { get; private set; }

    public MailParameters(string projectName, DateTime startTime, DateTime endTime, MailLang language, Priority priority)
        : this(projectName, startTime, endTime, new[] { language }, priority)

    public MailParameters(string projectName, DateTime startTime, DateTime endTime, IEnumerable<MailLang> languages, Priority priority)
    {
        ProjectName = projectName;
        StartTime = startTime;
        EndTime = endTime;
        Languages = languages.Cast<int>();
        Priority = (int)priority;
    }
}

次に、これらの拡張メソッドを追加します。

public static int Count(this IQueryable<Mail> mails, MailCountParameter p)
{
    return mails.Count(m =>
        m.ProjectName == p.ProjectName &&
        p.Languages.Contains(m.MailLang) &&
        m.EnteredBetween(p.StartTime, p.EndTime) &&
        m.Priority == p.Priority);
}

public static bool EnteredBetween(this Mail mail, DateTime startTime, DateTime endTime)
{
    return mail.DateEntered >= startTime && mail.DateEntered <= endTime;
}

使用法は次のようになります。

var mailParametersOne = new MailParameters("Delta", startDate, endDate, new[] { MailLang.EU, MailLang.RU }, MailPriority.High);
var mailTaskOne = CreateTask(() => myService.Mail.Count(mailParametersOne));
于 2012-09-03T09:08:37.333 に答える
0

私の最終的な解決策は、ScottGu の記事に基づいています。 http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

このような LINQ クエリを作成します。

    var linqStatements = new List<String>();

    linqStatements.Add(parser.StringToLinqQuery<Project>("ProjectId", report.Project));
    linqStatements.Add(parser.StringToLinqQuery<Region>("RegionId", report.Region));
    linqStatements.Add(parser.StringToLinqQuery<Status>("Status", report.Status));
    linqStatements.Add(parser.StringToLinqQuery<Priority>("Priority", report.Priority));
    linqStatements.Add(parser.StringToLinqQuery<Category>("CategoryId", report.Category));
    linqStatements.Add(AccountIdsToLinqQuery(report.PrimaryAssignment));

    string baseQuery = String.Join(" AND ", linqStatements.Where(s => !String.IsNullOrWhiteSpace(s)));
    var linqQuery = service.Mail.Where(baseQuery).Cast<Mail>();

StringToLinqQueryこんな感じ(簡易版)。

public string StringToLinqQuery<TEnum>(string field, string value) where TEnum : struct
{
    if (String.IsNullOrWhiteSpace(value))
        return String.Empty;

    var valueArray = value.Split('|');
    var query = new StringBuilder();

    for (int i = 0; i < valueArray.Count(); i++)
    {
        TEnum result;
        if (Enum.TryParse<TEnum>(valueArray[i].ToLower(), true, out result))
        {
            if (i > 0)
                query.Append(" OR ");
            query.AppendFormat("{0} == {1}", field, Convert.ToInt32(result));
        }
        else
        {
            throw new DynoException("Item '" + valueArray[i] + "' not found. (" + type of (TEnum) + ")",
                                    query.ToString());
        }
    }

    // Wrap field == value with parentheses ()
    query.Insert(0, "(");
    query.Insert(query.Length, ")");

    return query.ToString();
}

そして、最終結果は次のようになります。

service.Mail.Where("(ProjectId == 5) AND (RegionId == 6 OR RegionId == 7) AND (Status == 5) and (Priority == 5)")

私のプロジェクトでは、値を XML ファイルに保存し、それらを上記の LINQ クエリにフィードします。フィールドが空の場合、無視されます。また、|記号を使用して複数の値をサポートします。たとえば、EU|USに変換され(Region == 5 OR Region == 6)ます。

于 2012-09-06T10:39:15.430 に答える