C# 从缓存列表查询IQuery将返回null异常

C# 从缓存列表查询IQuery将返回null异常,c#,linq,entity-framework,caching,C#,Linq,Entity Framework,Caching,我一直在试验Peter Montgomery的“缓存LINQ查询的结果”源代码 它为IQueryable创建一个扩展方法,如果可能,从缓存数据返回IEnumerable。主扩展方法如下所示 /// <summary> /// Returns the result of the query; if possible from the cache, otherwise /// the query is materialized and the result cac

我一直在试验Peter Montgomery的“缓存LINQ查询的结果”源代码

它为
IQueryable
创建一个扩展方法,如果可能,从缓存数据返回
IEnumerable
。主扩展方法如下所示

    /// <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;
    }
//
///返回查询结果;如果可能,请从缓存中删除,否则
///查询被物化,结果在返回之前被缓存。
/// 
///要为其返回查询的IQueryable。
///对象的相对缓存优先级。
///指示滑动过期持续时间的时间跨度
///查询结果;如果可能,从缓存中删除
///要为其提供方法的实体的类型。
公共静态IEnumerable FromCache(此IQueryable查询、CacheItemPriority、TimeSpan slidingExpiration)
{
string key=query.GetCacheKey();
//尝试从缓存中获取查询结果
var result=HttpRuntime.Cache.Get(key)as List;
如果(结果==null)
{
//TODO:…确保查询结果不会
//保留特定数据源的资源
//
////////对于实体框架查询,设置为NoTracking
//////var entityQuery=查询为ObjectQuery;
//////if(entityQuery!=null)
//////{
//////entityQuery.MergeOption=MergeOption.NoTracking;
//////}
//具体化查询
结果=query.ToList();
HttpRuntime.Cache.Insert(
钥匙
结果,,
null,//无缓存依赖项
Cache.NoAbsoluteExpiration,
滑动到期,
优先,
null);//没有删除通知
}
返回结果;
}
我使用的方法如下:

    /// <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>();
    }
//
///如果可能,从缓存检索指定类型的所有实例。
///对象保持在分离状态,并且
///未在ObjectStateManager中跟踪。
/// 
///指定类型的所有实例的列表。
///要为其提供方法的实体的类型。
public IQueryable All(),其中T:class,new()
{
//返回新的ObjectQuery(GetSetName(),this.context,MergeOption.NoTracking);
返回新的ObjectQuery(GetSetName(),this.context,MergeOption.NoTracking).FromCache().AsQueryable();
}
然后我会像这样过滤我的请求(查询AdventureWorks数据库示例):

List products=新列表(readonlySession.All()
其中(x=>x.Color.Equals(“黑色”,StringComparison.InvariantCultureIgnoreCase));
我的问题是,当我尝试像这样查询数据时,我会得到一个
NullReferenceException
,因为有些产品会为属性
Color
返回null。如果我在没有缓存的情况下查询实体,则不会出现问题

有人能解释为什么会发生这种情况,以及我如何解决这个问题吗

非常感谢


更新:我发现使用==解决了我的问题,尽管我不知道为什么。但是我希望能够使用Equals()。

当您执行
x.Color.Equals(“黑色”,StringComparison.InvariantCultureInogoreCase)
并且颜色为空时,您将得到空异常,更好的方法是使用静态字符串方法,即使参数为空,也可以工作,如下所示:

String.Equals(x.Color, "Black", StringComparison.InvariantCultureIgnoreCase)

要将查询转换为适当的SQL,您使用的特定ORM技术取决于LINQ提供程序。EF(或L2S)提供程序知道如何将
x.Color.Equals(“Black”,…)
转换为适当的SQL。但是,当您缓存Resultl时(通过调用
ToList()
),您将切换到LINQ to Objects,然后开始应用C的所有规则:您不能对对象的空实例调用实例方法


您可以尝试切换:
x=>“Black”.Equals(x.Color)
(希望LINQ to EF提供商也能理解这一点,尽管我现在无法测试,所以您必须自己尝试)

@K Ivanov:谢谢您的解释。我不能接受你的回答的唯一原因是你被时间打败了。不过我已经投票支持了你的答案。thx,虽然我的答案比你选择的答案早4分钟,但这不是一个大交易谢谢你的洞察力。我担心答案会是这样的。切换语法感觉有点奇怪,但我相信我会习惯的,而且会起作用。干杯
String.Equals(x.Color, "Black", StringComparison.InvariantCultureIgnoreCase)