キャッシングを処理するための魔法のコードを書こうとしていますが、それは可能か不可能かです。基本的には、実行する 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));