我有一个存储库界面,如下所示:
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>
。
我有一个可以使用Repository<T>
方法包装LinqToSQL数据上下文的类,如下所示:
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 :(得分:2)
您可以编写自定义IQueryProvider
和IQueryable<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();