一例由于用redis缓存一张表到内存导致列表页面在搜索查询的时候出现未将对象引用设置到对象的实例问题的解决

来源:互联网 发布:linux命令压缩文件 编辑:程序博客网 时间:2024/05/23 21:23

通过定位发现出错行在这里:

model = query.ToPagedList(pageIndex, pageSize);

然后在错误堆栈中再找,发现是ToPagedList调用了 System.Linq.Queryable.Count[TSource](IQueryable`1 source)  这个Count方法导致的问题。

为了确定是否是调用这个方法的问题,在model = query.ToPagedList(pageIndex, pageSize); 增加了一行:int a = query.Count(); 编译运行,这次错误定位到这一行。由于我在内存中存储的表是以Redis的 List<T> 形式存储的。因为要分页,并搜索,用到了按多行排序,并且用到了linq的where方法。在处理的时候把List<T> 变为了 IQueryable形式。代码是这样写的:

 var query = RedisQuestionsLibraryList.AsQueryable().Where(lambda);

刚开始以为是redis存储后的数据,经过AsQueryable()改变后再用where 进行lambda查询,C#支持不够好造成。然后根据这种情况在baidu, google及 stackoverflow网站搜索解决方案,均没有结果。后来由于想到另一个页面也使用过这种redis数据进行查询列表,哪个页面却没报错。于是做了比较。哪个页面的查询列表代码如下:

var query = RedisQuestionsLibraryList.AsQueryable().Where(lambda).OrderBy("iid", true);

然后我为了检测,在这行下面增加:

int a = query.Count();

不报错。至此确认并不是 list 泛型使用 AsQueryable() 转换为 IQueryable 对象早成的问题。仔细的比较了这两个页面的查询语句,并且为了严格一样,把出错页面的orderby方法改为和正确页面的一样。执行仍然是错误的页面还报同样的错误。仔细比对这两行代码,也就lambda表达式不同了。没报错的页面lambda表达式只有一个条件查询。而错误页面实多个条件的复合查询。因为确定是查询条件造成的这个问题。我对各个条件分别进行测试。发觉进行其他条件查询都不报错。就用contains方法的这个条件报错。报错语句是这样写的:

lambda = lambda.And(m => m.Title.Contains(search) || m.Option1.Contains(search) || m.Option2.Contains(search) || m.Option3.Contains(search) || m.Option4.Contains(search));

由于报的是未将对象引用设置到对象的实例的错误。首先是怀疑 m.Title ,m.Option1这里值是null造成。因为 String.Contains方法中。前面的string部分是不能为空的。我在数据库中对此表的数据进行了查询,发现Option1 ,Option2等字段中存的值有的时候是为null。于是,对此语句进行改造,改造后的语句如下:

lambda = lambda.And(m => m.Title.Contains(search) || (m.Option1 != null && m.Option1.Contains(search)) || (m.Option2 != null && m.Option2.Contains(search)) || (m.Option3 != null && m.Option3.Contains(search)) || (m.Option4 != null && m.Option4.Contains(search)));

这次编译运行,没这个问题了。显然是这里的问题。可是问题又来了,为什么同样的查询语句,我原来在没有使用 redis 缓存的情况下。从数据库中查询执行,没发生过这样的错误啊。原来从数据库中查询的语句是这样的;

var query = new QuestionsLibraryService().GetNolock(lambda);
怀疑与生成的sql语句有关。于是决定用 sql server profiler对执行的查询语句进行分析。

首先建立一个跟踪。然后清空跟踪窗口中的所有数据。先执行从 redis 缓存中取数据进行查询。在跟踪窗口中查询这个表,毫无疑问,在sql server分析器中没有有关这个表的查询语句。这很好理解,因为用的是 redis 中的缓存表,是在内存中的。自然在 sql server 分析器中不会查询这张表。 然后注释掉自 redis 中获取数据语句,改为自数据库中直接获取数据语句:

//var query = RedisQuestionsLibraryList.AsQueryable().Where(lambda); var query = new QuestionsLibraryService().GetNolock(lambda);
编译,执行,这次 sql server profiler中有相关语句了。把此语句复制到 sql server的查询窗口,发现使用 Contains方法的地方,在sql查询语句中被转换为了 like 查询。对这样的like语句进行简化,使用了一条 Option1 字段中包括 null 数据的行进行 like 查询。

SELECT * FROM dbo.Questions WHERE GID='5c3d4eaa-efef-4683-91d5-a771823dfe77' AND Option1 LIKE '%某日%'
在sql 查询窗口执行此语句,正常执行,不会报错。至此明白了为什么在内存中存储的null值进行相应查询的时候会报错,而数据库中的null值为什么不报错。是因为在内存中执行的是真正的string.Contains方法,此方法要求 string 必须不为 null 。而对 数据库进行查询的语句,它经过转换,把 contains 方法转换为了 like sql 查询方法。而 like sql 查询方法中 字段是允许为null的。


阅读全文
0 0
原创粉丝点击