11

これが誰かにとって役立つことが判明した場合、私は喜んでそれをコミュニティwikiのものに変えます。

MVC3アプリに遅いページがいくつかありますが、コードで実行時間がほとんど発生していないように見えたので、これほど時間がかかったものについてもっと知ることができるかどうかを確認したいと思いました。成功したわけではありませんが、その過程でもう少し知恵を得ることができました。

ここには、MVCの経験がある人には明らかでないことは何もありません。基本的に、私は次のような独自のActionFilterAttributeを作成しました。

public class ProfilerAttribute : ActionFilterAttribute
{
    IDisposable actionStep = null;
    IDisposable resultStep = null;

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        actionStep = MiniProfiler.Current.Step("OnActionExecuting " + ResultDescriptor(filterContext));
        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (actionStep != null)
        {
            actionStep.Dispose();
            actionStep = null;
        }
        base.OnActionExecuted(filterContext);
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        resultStep = MiniProfiler.Current.Step("OnResultExecuting " + ResultDescriptor(filterContext));
        base.OnResultExecuting(filterContext);
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        if (resultStep != null)
        {
            resultStep.Dispose();
            resultStep = null;
        }
        base.OnResultExecuted(filterContext);
    }

    private string ResultDescriptor(ActionExecutingContext filterContext)
    {
        return filterContext.ActionDescriptor.ControllerDescriptor.ControllerName + "." + filterContext.ActionDescriptor.ActionName;
    }

    private string ResultDescriptor(ResultExecutingContext filterContext)
    {
        var values = filterContext.RouteData.Values;

        return String.Format("{0}.{1}", values["controller"], values["action"]);
    }

これはうまく機能しているようです。私の場合、ほとんどの時間は、私の行動の中でではなく、人生のResultExecutingの部分に実際に費やされていることを学びました。

ただし、このアプローチについていくつか質問があります。

1)これはリクエストに対して安全な方法ですか?ActionfilterはGlobal.asax.csのRegisterGlobalFilters()メソッドで一度だけ作成されるので、私はノーと推測しています。2つのリクエストが同時に表示される場合、actionStepとresultStepは無意味になります。これは本当ですか?もしそうなら、私よりも知っている誰かがこれを処理するための賢い方法を提供できますか?ローカルマシンのプロファイリング中には機能しますが、複数の人が同時にリクエストを行うサーバーにはあまりデプロイされていない可能性があります。

2)結果実行プロセスについてより多くの洞察を得る方法はありますか?それとも、ビューのレンダリングなどに時間がかかることを受け入れる必要がありますか?私自身のアプリでは、アクションメソッドが終了する前にすべてのデータベースアクセスが終了することを確認し(私の場合はNHibernate Profilerを使用)、ビューをスリムでシンプルに保つのが好きです。ただし、レンダリングの速度を低下させる原因についてのあらゆる種類の洞察は、依然として役立つ可能性があります。私の側でスローコードがここで実行された場合、モデルオブジェクトでミニプロファイラーを使用するとここに表示されると思います。

3)ResultDescriptorメソッドは、おそらく邪悪で有毒です。彼らは私のテストで私のために働いたが、おそらくもっと頑丈なものに置き換える必要があるだろう。私はちょうど私に中途半端に役立つ何かを与えた最初のバージョンで行きました。

「これは悪い考えです、一人で死ぬ」であっても、これに対する他のコメントも大歓迎です。

4

3 に答える 3

5

これはクールなアイデアのように見えます。私はそれが物事を行うための要求の安全な方法ではないと信じています。

HttpContext.Itemsあなたはそれをこのようにリンクすることができます

HttpContext.Items.Add("actionstep", actionStep);
HttpContext.Items.Add("resultstep", resultStep);

そして、同様の方法でそれを取得します

actionStep = HttpContext.Items["actionstep"];
resultStep = HttpContext.Items["resultstep"];

明らかに、nullなどの独自のチェックを入れます。

HttpContextユーザー/リクエストごとに異なります。

覚えておくべきHttpContext.Current.Session.SessionIDことは、それが現在のHTTPリクエストのSessionIdであることを忘れることがあります(つまり、F5キーを押すか、新しいリクエストを行うたびに変更されます)。覚えておくべきもう1つの重要な点は、すべてのHttpContext.Current.Session.SessionID値は常に一意である必要がありますが(つまり、ユーザーごと、またはリクエストごとに1つ)、再利用できるため、一度だけ使用されるGUIDとは考えないでください。各。

于 2011-06-23T09:01:43.933 に答える
4

MiniProfilerアセンブリには、アクションのプロファイリングを行うアクションフィルター属性がすでに存在します。これはStackExchange.Profiling.MVCHelpers名前空間にあり、ProfilingActionFilterと呼ばれます。これを拡張して、ビューのプロファイルを作成することもできます。

@Dommerで説明されているのと同じアプローチを使用しますが、IDisposableを直接格納する代わりに、スタックをHttpContext.Current.Itemsに格納します。ビューについても同じことができます。

アクションプロファイリングのコードは次のとおりです。

/// <summary>
/// This filter can be applied globally to hook up automatic action profiling
/// 
/// </summary>
public class ProfilingActionFilter : ActionFilterAttribute
{
    private const string stackKey = "ProfilingActionFilterStack";

    /// <summary>
    /// Happens before the action starts running
    /// 
    /// </summary>
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (MiniProfiler.Current != null)
        {
            Stack<IDisposable> stack = HttpContext.Current.Items[(object) "ProfilingActionFilterStack"] as Stack<IDisposable>;
            if (stack == null)
            {
                stack = new Stack<IDisposable>();
                HttpContext.Current.Items[(object) "ProfilingActionFilterStack"] = (object) stack;
            }
            MiniProfiler current = MiniProfiler.Current;
            if (current != null)
            {
                RouteValueDictionary dataTokens = filterContext.RouteData.DataTokens;
                string str1 = !dataTokens.ContainsKey("area") || string.IsNullOrEmpty(dataTokens["area"].ToString()) ? "" : (string) dataTokens["area"] + (object) ".";
                string str2 = Enumerable.Last<string>((IEnumerable<string>) filterContext.Controller.ToString().Split(new char[1] { '.' })) + ".";
                string actionName = filterContext.ActionDescriptor.ActionName;
                stack.Push(MiniProfilerExtensions.Step(current, "Controller: " + str1 + str2 + actionName, ProfileLevel.Info));
            }
        }
        base.OnActionExecuting(filterContext);
    }

    /// <summary>
    /// Happens after the action executes
    /// 
    /// </summary>
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        Stack<IDisposable> stack = HttpContext.Current.Items[(object) "ProfilingActionFilterStack"] as Stack<IDisposable>;
        if (stack == null || stack.Count <= 0) return;
        stack.Pop().Dispose();
    }
}

この助けを願っています。

于 2012-05-07T09:06:41.547 に答える
0

ControllerでExecuteCoreメソッドをラップするだけです。:)

于 2011-07-02T21:48:03.413 に答える