2014-02-08 2 views
2

데이터베이스에서 자주 읽는 항목을 캐시하는 데 사용하려는 다음과 같은 간단한 캐시 클래스가 있습니다.동적으로 생성 된 IQueryable의 조회 키

public class Cache<TKey, TElement> 
    where TElement : class 
{ 
    private Dictionary<TKey, TElement> cache = new Dictionary<TKey, TElement>(); 
    private Func<TElement, TKey> dbKey; 

    public Cache(Func<TElement, TKey> dbKey) 
    { 
     this.dbKey = dbKey; 
    } 

    public TElement Get(TKey key, DataContext db) 
    { 
     if (cache.ContainsKey(key)) return cache[key]; 

     // This line throws exception. See below 
     var value = db.GetTable<TElement>().SingleOrDefault(x => dbKey(x).Equals(key)); 

     if (value != null) cache[key] = value; 
     return value; 
    } 
} 

는 사용법은 다음과 같습니다 그러나, 표시된 라인은 메시지 "방법과 NotSupportedException이 발생

var db = new DataContext(); 
var userCache = new Cache<string, User>(u => u.Username); 
userCache.Get("dave", db); 

'으로 System.Object DynamicInvoke는 (은 System.Object는 [])'지원되는 변환이 없습니다 SQL에. " 예외가 throw되는 이유를 이해하지만 해결 방법을 모르겠습니다.

캐시는 조회 키에 다른 열이 사용되는 여러 다른 db 테이블의 앞에서 작동하도록되어 있습니다. 내 생각은 코드에서 볼 수 있듯이 람다를 전달하여 조회 열을 지정하는 것이 었습니다. IEnumerable에서는 작동하지만 IQueryable에서는 작동하지 않습니다.

LINQ to SQL에서이를 수행하는 또 다른 방법이 있습니까?

답변

2

위임자와 람다가 아닌 표현식 트리를 사용해야합니다.

  1. 변경 생성자의 매개 변수 유형과 필드 유형 Expression<Func<TElement, TKey>>에 : 자동 컴파일시에 람다 식에서 식 트리를 생성하는 컴파일러

    private Expression<Func<TElement, TKey>> dbKey; 
    
    public Cache(Expression<Func<TElement, TKey>> dbKey) 
    { 
        this.dbKey = dbKey; 
    } 
    

    감사합니다, 당신은 여전히 ​​이전처럼 호출 할 수

    var userCache = new Cache<string, User>(u => u.Username); 
    
  2. 키 선택기 표현식과 등호 확인을 결합하는 개인 도우미 메서드를 추가하십시오.

    private Expression<Func<TElement, bool>> GetConditionExpression(TKey key) 
    { 
        var param = Expression.Parameter(typeof(TElement)); 
    
        return 
         Expression.Lambda<Func<TElement, bool>>(
          Expression.Equal(
           Expression.Invoke(
            dbKey, 
            param 
           ), 
           Expression.Constant(key) 
          ), 
         param 
         ); 
    } 
    
  3. 전화하는 방법은 SingleOrDefault에 대한 적절한 표현을 얻을 수 있습니다 :

    var value = db.GetTable<TElement>().SingleOrDefault(GetConditionExpression(key)); 
    

나는 실제 DB와 지금을 테스트 할 수 없습니다,하지만 작동합니다. 그렇지 않다면, 적어도 당신을 올바른 방향으로 가리켜 야합니다.