使用datepickers和Entity Framework 6获取两个日期之间的所有日期

时间:2018-12-10 21:32:19

标签: c# winforms entity-framework linq entity-framework-6

我的表单上有两个日期时间选择器。我想要一个函数,该函数将在这两个日期之间返回来自特定表(即特定列的值)的所有日期时间。

我的方法如下:

public DateTime[] GetAllArchiveDates(string username = null)
{
    var result = new DateTime[0];

    if (username != null)
    {
        result = this._context.archive.OrderBy(s => s.IssuingDate).Where(s => s.insertedBy == username).Select(s => s.issuing_date).Distinct().ToArray();
    }
    else
    {
        result = this._context.archive.OrderBy(s => s.IssuingDate).Select(s => s.issuing_date).Distinct().ToArray();
    }

    return result;
}

但是我收到此错误:

  

System.NotSupportedException:'LINQ to Entities不支持指定的类型成员'IssuingDate'。仅支持初始化程序,实体成员和实体导航属性。'

该怎么做?

1 个答案:

答案 0 :(得分:1)

错误消息的原因

您应该注意IEnumerableIQueryable之间的区别。

实现IEnumerable的类的对象包含要枚举其表示的项目序列的所有内容。您可以要求序列中的第一个项目,一旦获得一个项目,就可以要求下一个项目,直到没有更多项目为止。

另一方面,实现IQueryable的类的对象保留所有内容,以要求另一个进程提供数据以创建IEnumerable序列。为此,它包含一个Expression和一个Provider

Expression是开始枚举IQueryable时必须创建哪种IEnumerable的通用表示形式。

Provider知道谁必须执行查询,并且知道如何将Expression转换为执行者可以理解的格式,例如SQL。

有两种LINQ语句。那些使用延迟执行的,那些不使用延迟执行的。延迟的函数可以被识别,因为它们返回IQueryable<TResult>(或IEnumerable)。例如WhereSelectGroupBy等。

非延迟函数返回TResultToListToDictionaryFirstOrDefaultMax

只要串联延迟的LINQ函数,就不会执行查询,仅更改Expression。一旦开始枚举,无论是显式使用GetEnumeratorMoveNext,还是隐式使用foreach,ToList,Max等,Expression都会发送给Provider,后者将对其进行翻译SQL并执行查询。结果表示为IEnumerable,在其上执行GetEnumerator

这与我的问题有什么关系?

由于必须将Expression转换为SQL,因此它无法保存您发明的任何内容。毕竟,SQL不知道您的功能。实际上,IQueryable中不能使用很多标准功能。参见Supported and unsupported LINQ functions

A,您忘记给我们提供archive类的定义,但是我认为它不是POCO:它包含的功能和属性不仅仅具有get / set的作用。我认为IssuingDate不仅仅是获取/设置。

对于IQueryable,您应该使类保持简单:在查询期间仅使用{get; set;},仅此而已。将IQueryable实例化为IEnumerable并将在本地进程中执行后,可以调用其他功能

回到您的问题

因此,您有一个数据库,该数据库的表Archive至少包含列IssuingDateInsertedBy。看来InsertedBy只是一个字符串。它可能是用户表的外键。这不会对答案产生太大影响。

entity framework code first conventions之后,出现了以下课程

class Archive
{
    public int Id {get; set;}
    public DateTime IssuingDate {get; set;}
    public string InsertedBy {get; set;}
    ...
}

public class MyDbContext : DbContext
{
     public DbSet<Archive> Archives {get; set;}
}

顺便说一句,您是否有适当的理由经常偏离Microsoft的有关命名标识符的标准,特别是复数和驼峰式命名?

无论如何,您的要求

  

我的表单上有两个日期时间选择器。我想要一个函数,该函数将在两个日期之间返回来自特定表(即特定列的值)的所有日期时间。

您的代码似乎还有很多工作,但让我们首先编写一个满足您要求的扩展功能。我将其作为存档类的扩展方法编写。这将使您的存档类保持简单(仅{get; set;}),但为类增加了功能。将其编写为扩展功能也使您可以像使用其他任何LINQ功能一样使用这些功能。参见Extension methods demystified

public static IQueryable<Archive> BetweenDates(this IQueryable<Archive> archives,
    DateTime startDate,
    DateTime endDate)
{
     return archives.Where(archive => startDate <= archive.IssuingDate
                                   && archive.IssuingDate <= endDate);
}

如果我查看您的代码,那么您在日期之间不会选择存档。您使用userName进行某种操作,然后进行排序,选择不同的...有点奇怪的是,您首先订购了所有百万个档案,然后决定仅保留属于userName的十个档案,并且如果您有多个相同的发布日期您决定删除重复项。在开始订购日期之前先限制发行日期的数量会更有效吗?

public static IQueryable<archive> ToIssuingDatesOfUser(this IQueryable<archive> archives,
    string userName)
{
    // first limit the number of archives, depdning on userName,
    // then select the IssuingDate, remove duplicates, and finally Order
    var archivesOfUser = (userName == null) ? archives :
        archives.Where(archive => archive.InsertedBy == userName);

   return archivesOfUser.Select(archive => archive.IssuingDate)
                        .Distinct()
                        .OrderBy(issuingDate => issuingDate);
}

注意:到目前为止,我只创建了IQueryables。因此,仅更改Expression,这非常有效。数据库尚未通信。

用法示例:

要求:给定一个用户名,一个startDate和一个endDate,请给我该用户发布的所有档案的唯一的issuingDate(以升序排列)

public ICollection<string> GetIssuingDatesOfUserBetweenDates(string userName, 
    DateTime startDate,
    DateTime endDate)
{
    using (var dbContext = new MyDbContext(...))
    {
        return dbContext.Archives
               .BetweenDates(startDate, endDate)
               .ToIssuingDatesOfUser(userName)
               .ToList();
    }
}