我想对从IQueryable
返回的实体执行一些后处理,但我想在评估查询之前指定后处理。例如:
IQueryable<Client> GetClients()
{
return _context.Set<Client>()
.PostProcess(c => c.MobileNumber = c.MobileNumber.Replace(" ", ""));
}
Client GetClient(int id)
{
return GetClients()
.FirstOrDefault(c => c.Id == id);
}
在上面的示例中,我希望从上述两种方法返回的每个Client
都可以从其移动号码中删除所有空格。这可能吗?
答案 0 :(得分:4)
它适用于OP中所述的简单情况,但任何进一步的IQueryable
扩展(例如.Where()
子句)都将丢弃后处理行为。我不确定在技术上可以做我想做的事情,现在我开始想到它我甚至不确定语义将超出简单的情况。尽管如此,这个简单的案例仍然有效,并且能够让事情变得流畅......
我想我会分享我的方法以防万一有人发现它有用(或评论为什么这是一个坏主意)。该解决方案使用两个装饰器来推迟后处理操作,直到执行查询,并使用扩展方法来设置它们:
public static class QueryableExtensions
{
public static IQueryable<T> PostProcess<T>(this IQueryable<T> source, Action<T> postProcessor) where T : class
{
return new QueryableWrapper<T>(source, postProcessor);
}
// wraps IQueryProvider.Execute methods with post-processing action
class QueryProviderWrapper<T> : IQueryProvider where T : class
{
private readonly IQueryProvider _wrapped;
private readonly Action<T> _postProcessor;
public QueryProviderWrapper(IQueryProvider wrapped, Action<T> postProcessor)
{
_wrapped = wrapped;
_postProcessor = postProcessor;
}
public IQueryable CreateQuery(Expression expression)
{
return _wrapped.CreateQuery(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return _wrapped.CreateQuery<TElement>(expression);
}
public object Execute(Expression expression)
{
var result = _wrapped.Execute(expression);
var asT = result as T;
if (asT != null)
_postProcessor(asT);
return result;
}
public TResult Execute<TResult>(Expression expression)
{
var result = _wrapped.Execute<TResult>(expression);
var asT = result as T;
if (asT != null)
_postProcessor(asT);
return result;
}
}
// wraps IQueryable.GetEnumerator() with post-processing action
class QueryableWrapper<T> : IQueryable<T> where T : class
{
private readonly IQueryable<T> _wrapped;
private readonly Action<T> _postProcessor;
private readonly IQueryProvider _provider;
public QueryableWrapper(IQueryable<T> wrapped, Action<T> postProcessor)
{
_wrapped = wrapped;
_postProcessor = postProcessor;
_provider = new QueryProviderWrapper<T>(_wrapped.Provider, postProcessor);
}
public Expression Expression
{
get { return _wrapped.Expression; }
}
public Type ElementType
{
get { return _wrapped.ElementType; }
}
public IQueryProvider Provider
{
get { return _provider; }
}
public IEnumerator<T> GetEnumerator()
{
return _wrapped
.AsEnumerable()
.Do(_postProcessor)
.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
GetEnumerator()
装饰器使用Interactive Extensions中的.Do()
扩展方法,就像Select
和ForEach
之间的交叉:它被懒惰地调用,但需要Action<T>
并返回T