3

キャッシングを処理するための魔法のコードを書こうとしていますが、それは可能か不可能かです。基本的には、実行する Func をパラメーターとして受け入れる静的メソッドを持つ CacheManager クラスを用意するという考え方です。静的メソッドの本体では、渡された Func の内部を一意に識別するキャッシュ キー (0 個以上のパラメーターを持つ匿名メソッド) を使用して、その Func を実行し、結果をキャッシュすることができます。提供された同じ引数を使用したその静的メソッドへの後続の呼び出しは、同じキャッシュ キーになり、キャッシュされた結果を返します。

渡された無名関数を一意に識別する方法が必要です。

編集:無名関数の構文を調整すると、式が答えを提供しました。

実行時に式をコンパイルしなければならないことによるパフォーマンスへの影響が心配です。これがパフォーマンスのためにキャッシングをサポートする試みであることを考えると、コンパイルにかなりの時間がかかるのはばかげています。何かご意見は?

テスト用の基本リポジトリ:

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class ProductRepository
{
    private List<Product> products { get; set; }

    public ProductRepository()
    {
        products = new List<Product>() { new Product() { ID = 1, Name = "Blue Lightsaber" }, new Product() { ID = 2, Name = "Green Lightsaber" }, new Product() { ID = 3, Name = "Red Lightsaber" } };
    }

    public Product GetByID(int productID)
    {
        return products.SingleOrDefault(p => p.ID == productID);
    }
}

キャッシュマネージャー:

public class CacheManager
{
    public static TResult Get<TResult>(Expression<Func<TResult>> factory)
    {
        if (factory == null) throw new ArgumentNullException("factory");

        var methodCallExpression = factory.Body as MethodCallExpression;
        if (methodCallExpression == null) throw new ArgumentException("factory must contain a single MethodCallExpression.");

        string cacheKey = "|Repository:" + methodCallExpression.Method.DeclaringType.FullName + "|Method:" + methodCallExpression.Method.Name + "|Args";
        foreach (var arg in methodCallExpression.Arguments)
        {
            cacheKey += ":" + (arg is ConstantExpression ? ((ConstantExpression)arg).Value : Expression.Lambda(arg).Compile().DynamicInvoke());
        }

        if (HttpContext.Current.Cache[cacheKey] == null)
        {
            HttpContext.Current.Cache[cacheKey] = factory.Compile().Invoke();
        }
        return (TResult)HttpContext.Current.Cache[cacheKey];
    }
}

使用法:

ProductRepository productRepository = new ProductRepository();
int productID = 1;
Product product;

// From repo
product = CacheManager.Get<Product>(() => productRepository.GetByID(1));

// From cache
product = CacheManager.Get<Product>(() => productRepository.GetByID(productID));
4

1 に答える 1

1

Memoizationのようなことをしたいようです。メモ化は、すでに計算された結果を保存する方法であり、これは要件のようです。

これを行うためのより良い方法は、結果を Dictionary に格納し、指定されたパラメーターで検索を実行するという点で、元の関数オブジェクトとは異なる新しい関数オブジェクトを作成することです。この新しい関数はキャッシュを処理し、ミスの場合はキャッシュに追加します。

このクラスは、1 つの引数の関数のメモ化されたバージョンを作成します。

public static class memofactory
{
    public static Func<In, Out> Memoize<In, Out>(Func<In, Out> BaseFunction)
    {
        Dictionary<In,Out> ResultsDictionary = new Dictionary<In, Out>();

        return Input =>
            {
                Out rval;
                try
                {
                    rval = ResultsDictionary[Input];
                    Console.WriteLine("Cache hit"); // tracing message
                }
                catch (KeyNotFoundException)
                {
                    Console.WriteLine("Cache miss"); // tracing message
                    rval = BaseFunction(Input);
                    ResultsDictionary[Input] = rval;
                }
                return rval;
            };
    }
}

あなたの例に基づく使用法は次のとおりです。

        ProductRepository productRepository = new ProductRepository();
        int productID = 1;
        Product product;

        Func<int, Product> MemoizedGetById = memofactory.Memoize<int, Product>(productRepository.GetByID);

        // From repo
        product = MemoizedGetById(1);

        // From cache
        product = MemoizedGetById(productID);
于 2013-01-16T01:14:15.697 に答える