2013-05-01 90 views
4

庫模式我有如下一個倉庫接口:與緩存和SqlMethods

public interface IDataContext<TId> : IDisposable 
{ 
    IQueryable<T> Repository<T>() where T : class, IEntity<TId>; 
    T FindById<T>(TId id) where T : class, IEntity<TId>; 
    void Insert<T>(T item) where T : class, IEntity<TId>; 
    void Delete<T>(T item) where T : class, IEntity<TId>; 
    void Commit(); 
} 

注意Repository<T>返回IQueryable<T>

我有一個類,可以換一個LinqToSQL數據上下文,與Repository<T>方法如下:

public IQueryable<T> Repository<T>() where T : class, IEntity<int> 
{ 
    ITable table = _context.GetTable(GetEntityType<T>()); 
    return table.Cast<T>(); 
} 

這工作得很好,我可以這樣做

new Repository(new SQLDataContext()) 
    .Repository<MyEntity>().Where(e => SqlMethods.Like(e.Id, "123%"); 

現在我已經開始考慮緩存但我有一個問題。

我創建了一個包裝和實現IDataContext<TId>的類,它將緩存來自內存中調用Repository<T>的結果。喜歡的東西下面:

public IQueryable<T> Repository<T>() where T : class, IEntity<TId> 
{ 
    // Actual caching logic here.....   

    return _CachedEntities[typeof(T)].OfType<T>().AsQueryable<T>(); 
} 

我的問題是,現在的IQueryable<T>我回是在內存中,未轉換爲SQL,所以我得到關於使用SqlMethods.Like異常。

TL; DR:所以,我怎麼能以這樣的方式創建我的緩存庫包裝,調用類不需要擔心是否IDataContext<T>它處理的是內存中的存儲庫(即緩存一個)還是正常的LinqToSQL存儲庫?

+0

你最有可能調用'AsQueryable'上緩存的IEnumerable' 返回時? – 2013-05-01 08:06:50

+0

@ErenErsönmez:對。 – 2013-05-01 08:14:42

回答

2

這是可能的,你需要編寫自定義IQueryProviderIQueryable<T>

public static class MySqlMethods 
{ 
    public static bool Like(string matchExpression, string pattern) 
    { 
     //Your implementation 
     return true; 
    } 
} 

public class ChangeMethodsVisitor : ExpressionVisitor 
{ 
    //This method will change SqlMethods to MySqlMethods. 
    protected override Expression VisitMethodCall(MethodCallExpression node) 
    { 
     if (node.Method.DeclaringType == typeof(SqlMethods)) 
     { 
      //Getting method from MySqlMethods class. 
      var method = typeof(MySqlMethods).GetMethod(node.Method.Name, 
                 node.Method.GetParameters() 
                  .Select(info => info.ParameterType) 
                  .ToArray()); 
      return Expression.Call(method, node.Arguments); 
     } 
     return base.VisitMethodCall(node); 
    } 
} 

public class MyQueryProvider : IQueryProvider 
{ 
    private static readonly ExpressionVisitor ExpressionVisitor = new ChangeMethodsVisitor(); 
    private readonly IQueryProvider _queryProvider; 

    public MyQueryProvider(IQueryProvider queryProvider) 
    { 
     _queryProvider = queryProvider; 
    } 

    public IQueryable CreateQuery(Expression expression) 
    { 
     expression = ExpressionVisitor.Visit(expression); 
     var queryable = _queryProvider.CreateQuery(expression); 
     //Wrap queryable to MyQuery class. 
     var makeGenericType = typeof(MyQuery<>).MakeGenericType(queryable.ElementType); 
     return (IQueryable)makeGenericType.GetConstructor(new[] { typeof(IQueryable<>).MakeGenericType(queryable.ElementType) }) 
              .Invoke(new object[] { queryable }); 
    } 

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) 
    { 
     expression = ExpressionVisitor.Visit(expression); 
     //Wrap queryable to MyQuery class. 
     var queryable = _queryProvider.CreateQuery<TElement>(expression); 
     return new MyQuery<TElement>(queryable); 
    } 

    public object Execute(Expression expression) 
    { 
     expression = ExpressionVisitor.Visit(expression); 
     return _queryProvider.Execute(expression); 
    } 

    public TResult Execute<TResult>(Expression expression) 
    { 
     expression = ExpressionVisitor.Visit(expression); 
     return _queryProvider.Execute<TResult>(expression); 
    } 
} 

public class MyQuery<T> : IOrderedQueryable<T> 
{ 
    private readonly IQueryable<T> _queryable; 

    public MyQuery(IQueryable<T> queryable) 
    { 
     _queryable = queryable; 
     Provider = new MyQueryProvider(_queryable.Provider); 
    } 

    public MyQuery(IEnumerable<T> enumerable) 
     : this(enumerable.AsQueryable()) 
    { 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return _queryable.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public Expression Expression 
    { 
     get { return _queryable.Expression; } 
    } 

    public Type ElementType 
    { 
     get { return _queryable.ElementType; } 
    } 

    public IQueryProvider Provider { get; private set; } 
} 

然後你就可以使用它:

var list = new List<string>(){"test", "test1"}; 

var myQuery = new MyQuery<string>(list); 
var queryable = myQuery.Where(s => SqlMethods.Like(s, "123%")).ToArray(); 
+0

有些東西不太對,我不認爲,例如'MyQuery <>。Provider'只能在你的代碼中設置。 – 2013-05-01 09:41:22

+0

「MyQuery」類只能用於集合。當你從緩存'_CachedEntities [typeof(T)]。OfType ()'而不是調用'AsQueryable ()'方法時,你將它包裝到'MyQuery'類中。什麼是不正確的? – 2013-05-01 09:45:56

+0

對不起,反正我錯了。我有我的測試代碼的另一個問題,看到'提供程序'被分配,但沒有訪問您的代碼中的任何地方,並認爲這是問題。將此標記爲已接受,謝謝。 – 2013-05-01 09:48:00