12

これに似た他の質問を見てきましたが、実行可能な答えは見つかりませんでした。

次のコードを使用して、linqクエリの結果をキャッシュに保存するための一意のキーを生成しています。

    string key = ((LambdaExpression)expression).Body.ToString();

    foreach (ParameterExpression param in expression.Parameters)
    {
        string name = param.Name;
        string typeName = param.Type.Name;

        key = key.Replace(name + ".", typeName + ".");
    }

    return key;

整数またはブール値を含む単純なクエリでは正常に機能するようですが、クエリにネストされた定数式が含まれている場合。

// Get all the crops on a farm where the slug matches the given slug.
(x => x.Crops.Any(y => slug == y.Slug) && x.Deleted == false)

したがって、返されるキーは次のとおりです。

(True AndAlso(Farm.Crops.Any(y =>(value(OzFarmGuide.Controllers.FarmController + <> c__DisplayClassd).slug == y.Slug))AndAlso(Farm.Deleted == False)))

ご覧のとおり、私が渡す作物名は同じ重要な結果をもたらします。クエリを区別できるように、指定されたパラメータの値を抽出する方法はありますか?

yまた、正しい型名を言うように変換するといいでしょう....。

4

4 に答える 4

7

PolityとMarcがコメントで述べたように、必要なのはLINQ式の部分評価者です。Matt WarrenのLINQ:IQueryable Providerの構築-パートIIIExpressionVisitorを使用して、これを行う方法を読むことができます。Pete MontgomeryによるLINQクエリの結果のキャッシュ(Polityによってリンクされている)の記事では、クエリでコレクションを表す方法など、この種のキャッシュに関する詳細について説明しています。

また、私はToString()このように頼ることができるかどうかわかりません。これは主にデバッグを目的としたものであり、将来変更される可能性があると思います。IEqualityComparer<Expression>別の方法は、任意の式のハッシュコードを作成し、2つの式を比較して等しいかどうかを確認できる独自の式を作成することです。私もそうするでしょうExpressionVisitorが、そうするのはかなり面倒です。

于 2012-03-17T11:53:22.217 に答える
4

私は、この種のアプローチが、維持するのがめちゃくちゃ難しいキャッシュの肥大化につながることなく役立つ可能性があるシナリオを理解しようとしてきました。

これがあなたの質問に直接答えていないことは知っていますが、最初は魅力的に聞こえるかもしれないこのアプローチについていくつか質問をしたいと思います。

  • パラメータの順序をどのように管理する予定でしたか?つまり。(x => x.blah == "slug" &&!x.Deleted)キャッシュキーは(x =>!x.Deleted && x.blah == "slug")キャッシュキーと等しくなければなりません。
  • キャッシュ内のオブジェクトの重複をどのように回避する予定でしたか?つまり。複数のクエリからの同じファームは、設計上、クエリごとに個別にキャッシュされます。たとえば、ファームに表示されるナメクジごとに、ファームの個別のコピーがあります。
  • パーセル、ファーマーなど、より多くのパラメーターを使用して上記を拡張すると、それぞれがファームの個別のコピーをキャッシュする、より一致するクエリになります。クエリを実行する可能性のある各タイプに同じことが当てはまり、パラメータが同じ順序になっていない可能性があります
  • では、ファームを更新するとどうなりますか?キャッシュされたクエリにファームが含まれるかどうかがわからないと、キャッシュ全体を強制終了する必要があります。どの種類があなたが達成しようとしていることに逆効果です。

このアプローチの背後にある理由がわかります。メンテナンス0のパフォーマンスレイヤー。ただし、上記の点を考慮しないと、このアプローチは最初にパフォーマンスを低下させ、次にそれを維持するための多くの試みにつながり、その後完全に維持不可能であることが判明します。

私はその道を進んできました。最終的に多くの時間を無駄にし、あきらめました。

結果が各タイプの拡張メソッドを使用してバックエンドから個別に、または共通のインターフェイスを介して取得される場合、結果の各エンティティを個別にキャッシュすることで、はるかに優れたアプローチを見つけました。

次に、ラムダ式の拡張メソッドを構築して、dbにアクセスする前に最初にキャッシュを試すことができます。

var query = (x => x.Crops.Any(y => slug == y.Slug) && x.Deleted == false);
var results = query.FromCache();
if (!results.Any()) {
    results = query.FromDatabase();
    results.ForEach(x = x.ToCache());
}

もちろん、クエリAがDBから3つのファームを返し、キャッシュから1つの一致するファームを使用して、データベースが実際に20の一致するファームを使用できるようにするために、実際にデータベースにヒットしたクエリを追跡する必要があります。したがって、各クエリは少なくとも1回はDBにヒットする必要があります。

また、結果としてDBに何もヒットしないように、0件の結果を返すクエリを追跡する必要があります。

しかし、全体として、コードを大幅に削減でき、ボーナスとして、ファームを更新すると、次のことが可能になります。

var farm = (f => f.farmId == farmId).FromCache().First();
farm.Name = "My Test Farm";
var updatedFarm = farm.ToDatabase();
updatedFarm.ToCache();
于 2012-11-13T23:19:53.703 に答える
1

これはどうですか?

public class KeyGeneratorVisitor : ExpressionVisitor
{
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return Expression.Parameter(node.Type, node.Type.Name);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (CanBeEvaluated(node))
        {
            return Expression.Constant(Evaluate(node));
        }
        else
        {
            return base.VisitMember(node);
        }
    }

    private static bool CanBeEvaluated(MemberExpression exp)
    {
        while (exp.Expression.NodeType == ExpressionType.MemberAccess)
        {
            exp = (MemberExpression) exp.Expression;
        }

        return (exp.Expression.NodeType == ExpressionType.Constant);
    }

    private static object Evaluate(Expression exp)
    {
        if (exp.NodeType == ExpressionType.Constant)
        {
            return ((ConstantExpression) exp).Value;
        }
        else
        {
            MemberExpression mexp = (MemberExpression) exp;
            object value = Evaluate(mexp.Expression);

            FieldInfo field = mexp.Member as FieldInfo;
            if (field != null)
            {
                return field.GetValue(value);
            }
            else
            {
                PropertyInfo property = (PropertyInfo) mexp.Member;
                return property.GetValue(value, null);
            }
        }
    }
}

これにより、複雑な定数式が元の値に置き換えられ、パラメーター名がタイプ名に置き換えられます。したがって、新しいKeyGeneratorVisitorインスタンスを作成し、そのVisitorVisitAndConvertメソッドを式で呼び出す必要があります。

Expression.ToStringメソッドは複合型でも呼び出されるため、メソッドをオーバーライドするか、ToStringメソッドにカスタムロジックを記述してくださいEvaluate

于 2012-03-17T16:13:43.307 に答える
0

どうですか:

var call = expression.Body as MethodCallExpression;

if (call != null)
{

    List<object> list = new List<object>();

    foreach (Expression argument in call.Arguments)
    {

        object o = Expression.Lambda(argument, expression.Parameters).Compile().DynamicInvoke();

        list.Add(o);

    }

    StringBuilder keyValue = new StringBuilder();

    keyValue.Append(expression.Body.ToString());

    list.ForEach(e => keyValue.Append(String.Format("_{0}", e.ToString())));

    string key = keyValue.ToString();

}
于 2013-08-06T08:10:05.597 に答える