C# 使用波斯日历的Linq到实体过滤查询

C# 使用波斯日历的Linq到实体过滤查询,c#,sql,linq,C#,Sql,Linq,我想使用波斯日历月筛选我的查询 我的原始查询: var getNews = from gn in db.NewsTables where gn.Show select gn; 波斯日历对象: PersianCalendar pc = new PersianCalendar(); 并按如下方式过滤原始查询: getNews = getNews.Where(nm => pc.GetMonth(nm.InsertionDate) == 9 &&

我想使用波斯日历月筛选我的查询

我的原始查询:

var getNews =
    from gn
    in db.NewsTables
    where gn.Show
    select gn;
波斯日历对象:

PersianCalendar pc = new PersianCalendar();
并按如下方式过滤原始查询:

getNews = getNews.Where(nm => pc.GetMonth(nm.InsertionDate) == 9 && pc.GetYear(nm.InsertionDate) == pc.GetYear(DateTime.Now));
例外情况是:

base{System.SystemException}={“LINQ到实体无法识别 方法“Int32 GetMonth(System.DateTime)”方法,以及此方法 无法转换为存储表达式。“}

有人知道吗?

简单(但可能不好*)的答案是:

getNews = getNews.ToList();
getNews = getNews.Where(nm => pc.GetMonth(nm.InsertionDate) == 9 && pc.GetYear(nm.InsertionDate) == pc.GetYear(DateTime.Now));
正如Jcl所说,错误的原因是LINQTo实体只支持有限数量的函数(这些函数可以转换为底层提供程序可以支持的表达式)。您的
pc.GetMonth
函数不是它所理解的函数

通过执行
ToList()
,您将强制在服务器上执行查询并返回结果。现在,
getNews
只是本地内存中的一个集合,任何进一步的LINQ操作都将使用LINQ to对象来完成,它没有上述限制


*如果在过滤之前数据集很小,这是一个好的解决方案,因为
getNews
查询的整个结果集必须在过滤之前返回并存储在内存中。如果是20排,我就不用担心了。如果是2000年,您应该找到重构的方法

Linq to Entities
仅支持其查询。因此,这是不幸的不支持

如果数据集不是太大,您可以完全检索它(例如使用
ToList()
),然后使用
Linq to Objects
对其进行过滤,只需考虑必须检索整个数据集(如果数据库服务器不是本地的,则传输),这可能会产生内存和性能影响

或者,如果数据库记录存储在公历日期(如注释中所述),您可以事先进行转换。。。遵循您的代码:

var gregorianDate = new DateTime(DateTime.Now.Year, 9, 1, new PersianCalendar());
getNews = getNews.Where(nm => nm.InsertionDate.Month == gregorianDate.Month  
                           && nm.InsertionDate.Year == gregorianDate.Year);

实体将代码转换为sql语言,在这种情况下,它无法将函数GetMonth转换为sql。快速解决方法是

var getNews =
    (from gn
    in db.NewsTables
    where gn.Show
    select gn).ToList();
但这意味着您将从内存中的数据库中获取所有记录,并在选择后应用过滤器


如果您向我们展示您的功能代码,我们将能够帮助您直接在数据库中进行过滤。

阅读此问题有两种不同的方式:

  • 你想在一个月内得到所有记录吗
  • 存储的日期与机器日期在不同的日历中
  • 第一部分可以通过在月初和月末之间将查询转换为范围查询来修复:

    var startDate=DateTime.Today.AddDays(1-DateTime.Today.Day);
    var endDate=startDate.AddMonths(1);
    
    var getNews = from gn in db.NewsTables
                  where gn.Show 
                    && gn.InsertionDate >=startDate
                    && gn.InsertionDate <endDate
                  select gn;
    

    我建议你使用Jon Skeet's Noda Time这样的图书馆,它知道日历,并不认为所有的当地时间都是公历

    你说的波斯月是什么意思?存储的日期在一个日历中,但
    DateTime。现在
    返回另一个日历中的日期?或者你想包括一个月内的所有日期?在第一种情况下,将
    DateTime.Now
    转换为存储的日历,然后进行简单的比较。在第二种方法中,在Monti的第一个和最后一个日期之间进行范围搜索。将数据存储在gregorian date中,并希望按波斯日期对其进行筛选。因此,在比较Noops之前,只需将波斯语转换为gregorian,DateTime值始终为gregorian。您只需要使用PersianCalendar进行计算,并直接在查询中使用范围值。这不会过滤任何内容,而是将整个表加载到内存中。@PanagiotisKanavos True。编辑以更清楚地说明,虽然这是一个解决方案,但如果表太大,则不是一个好的解决方案。这假设Linq to Entities将检查转换为
    DATEPART
    语句。即使这样,性能也会非常差,因为这将阻止服务器在日期上使用任何索引。请注意,搜索一个月相当于搜索一个日期范围month@PanagiotisKanavos它会转换它们(规范函数列表包括日期时间部分)。。。如果必须对
    datetime
    字段进行检查并且只对其中的一部分进行检查,那么我就无法解决性能问题。我只是在遵循行动守则。我不会用这个:-)我想你可以用范围检查,但我严格遵守question@PanagiotisKanavos顺便说一句,如果需要的话,在SQL server中,您可以在计算列上创建一个持久化索引,这样,如果需要的话,您可以轻松地从日期部分创建索引(性能甚至比范围检查更好,但要牺牲一些存储):-)如果查询导致性能差,则应避免查询。范围查询是SQL中避免使用函数的一种非常常见的模式。一个持久化的列只会有助于覆盖函数的成本。我不是在暗示其他,但我认为这超出了问题的范围。很明显,问题中提到的问题是将波斯日历日期转换为存储的公历日期。您可以在代码中,在存储过程中,使用或不使用索引,使用范围检查,使用持久化的计算日期部分,或在运行时进行计算,等等。正如我所说的,我坚持问题中所述的问题范围:我说的是OPs代码不工作的原因,并遵循它给出一个工作的解决方案。就这样。
    var calendar =  new PersianCalendar();
    var startDate= calendar.AddDays(searchDate,1- calendar.GetDayOfMonth(searchDate));
    var endDate=calendar.AddMonths(startDate,1);
    
    var getNews = from gn in db.NewsTables
                  where gn.Show 
                    && gn.InsertionDate >=startDate
                    && gn.InsertionDate <endDate
                  select gn;