我一直在试验Peter Montgomery的“缓存LINQ查询结果”源代码here
为IQueryable<T>
创建一个扩展方法,如果可能,从缓存数据返回IEnumerable<T>
。主扩展方法如下所示。
/// <summary>
/// Returns the result of the query; if possible from the cache, otherwise
/// the query is materialized and the result cached before being returned.
/// </summary>
/// <param name="query">The IQueryable for which to return the query.</param>
/// <param name="priority">The relative cache priority of the object.</param>
/// <param name="slidingExpiration">The timespan indicating the duration of the sliding expiration</param>
/// <returns>The result of the query; if possible from the cache</returns>
/// <typeparam name="T">The type of entity for which to provide the method.</typeparam>
public static IEnumerable<T> FromCache<T>(this IQueryable<T> query, CacheItemPriority priority, TimeSpan slidingExpiration)
{
string key = query.GetCacheKey();
// try to get the query result from the cache
var result = HttpRuntime.Cache.Get(key) as List<T>;
if (result == null)
{
// TODO: ... ensure that the query results do not
// hold on to resources for your particular data source
//
//////// for entity framework queries, set to NoTracking
//////var entityQuery = query as ObjectQuery<T>;
//////if (entityQuery != null)
//////{
////// entityQuery.MergeOption = MergeOption.NoTracking;
//////}
// materialize the query
result = query.ToList();
HttpRuntime.Cache.Insert(
key,
result,
null, // no cache dependency
Cache.NoAbsoluteExpiration,
slidingExpiration,
priority,
null); // no removal notification
}
return result;
}
我使用这样的方法:
/// <summary>
/// Retrieves all instances of the specified type, if possible from the cache.
/// Objects are maintained in a <see cref="T:System.Data.EntityState.Detached">Detached</see> state and
/// are not tracked in the <see cref="T:System.Data.Objects.ObjectStateManager">ObjectStateManager</see>.
/// </summary>
/// <returns>A list of all instances of the specified type.</returns>
/// <typeparam name="T">The type of entity for which to provide the method.</typeparam>
public IQueryable<T> All<T>() where T : class, new()
{
//return new ObjectQuery<T>(GetSetName<T>(), this.context, MergeOption.NoTracking);
return new ObjectQuery<T>(GetSetName<T>(), this.context, MergeOption.NoTracking).FromCache<T>().AsQueryable<T>();
}
然后我会过滤我的请求(查询AdventureWorks数据库示例):
List<Product> products = new List<Product>(readonlySession.All<Product>()
.Where(x => x.Color.Equals("Black", StringComparison.InvariantCultureIgnoreCase)));
我的问题是,当我尝试查询这样的数据时,我得到NullReferenceException
,因为某些产品为属性Color
返回null。如果我在没有缓存的情况下查询实体,则没有问题。
任何人都可以解释为什么会发生这种情况以及如何解决问题?
非常感谢。
更新:我发现使用==解决了我的问题虽然我不知道为什么。我希望能够使用Equals()。
答案 0 :(得分:2)
当你执行x.Color.Equals("Black", StringComparison.InvariantCultureIgnoreCase)
并且Color是 null 时,你会得到null异常,更好的方法是使用静态字符串方法,即使参数是也是如此null ,就像那样:
String.Equals(x.Color, "Black", StringComparison.InvariantCultureIgnoreCase)
答案 1 :(得分:2)
由您使用的特定ORM技术将LINQ提供程序转换为适当的SQL,这取决于LINQ提供程序。 EF(或L2S)提供程序知道如何将x.Color.Equals("Black", ...)
转换为适当的SQL。但是,当您缓存resutls(通过调用ToList()
)时,切换到LINQ to Objects,然后开始应用C#的所有规则:您无法在对象的null实例上调用实例方法。
您可以尝试切换它:x=>"Black".Equals(x.Color)
(希望LINQ到EF提供商也能理解这一点,虽然我现在无法测试它,所以你必须亲自尝试)