【问题标题】:SQL to LINQ Query with date in where clause is not workingSQL to LINQ Query with date in where 子句不起作用
【发布时间】:2013-05-13 18:05:45
【问题描述】:

我尝试通过 LINQ 查询数据库项目,但它不起作用。例外是:

对象引用未设置为对象的实例。

堆栈跟踪与异常本身一样无用:

在 SQLite.TableQuery1.CompileExpr(Expression expr, List1 queryArgs) 在 d:\XX\XX\XX\XX\XX\SQLite.cs:line 2383 at SQLite.TableQuery1.CompileExpr(Expression expr, List1 queryArgs) 在 d:\XX\XX\XX\XX\XX\SQLite.cs:第 2388 行 SQLite.TableQuery1.CompileExpr(Expression expr, List1 queryArgs) 在 d:\XX\XX\XX\XX\XX\SQLite.cs:line 2308 at SQLite.TableQuery1.CompileExpr(Expression expr, List1 queryArgs) 在 d:\XX\XX\XX\XX\XX\SQLite.cs:line 2308 at SQLite.TableQuery1.CompileExpr(Expression expr, List1 queryArgs) 在 d:\XX\XX\XX\XX\XX\SQLite.cs:line 2308 at SQLite.TableQuery1.GenerateCommand(String selectionList) in d:\XX\XX\XX\XX\XX\SQLite.cs:line 2274 at SQLite.TableQuery1.GetEnumerator() 在 d:\XX\XX\XX\XX\XX\SQLite.cs:line 2521 at System.Collections.Generic.List1..ctor(IEnumerable1 个集合)
在 System.Linq.Enumerable.ToList[TSource](IEnumerable`1 源) 在 Kapital.DataModel.DataManagerOnetimeExpense.c__DisplayClass5.b__4() 在 d:\XX\XX\XX\XX\XX\DataModel\DataManagerOnetimeExpense.cs:47 行
在 SQLite.SQLiteConnection.RunInTransaction(Action action) 中 d:\XX\XX\XX\XX\XX\SQLite.cs:第 906 行 Kapital.DataModel.DataManagerOnetimeExpense.RetrieveItems(Int32 月, Int32 年,Boolean isPaid) in d:\XX\XX\XX\XX\XX\DataModel\DataManagerOnetimeExpense.cs:第 39 行 UnitTestKapital.Database.TestDataManagerOnetimeExpense.TestRetrieveItemsByMonthYearIsPaid() 在 d:\XX\XX\XX\XX\XX\UnitTestKapital\Database\TestDataManagerOnetimeExpense.cs:line 154

但是这里是 LINQ 查询:

public List<OnetimeExpense> RetrieveItems(int month, int year, bool isPaid)
{
    var onetimeExpenses = new List<OnetimeExpense>();

    connection.RunInTransaction(() =>
    {
        var items = from s in connection.Table<OnetimeExpense>()
                    let convertedDate = (DateTime)s.PaymentDate
                    where (convertedDate.Month == month)
                       && (convertedDate.Year == year)
                       && (s.IsPaid == isPaid)
                    select s;
        onetimeExpenses = items.ToList();
    });

    return onetimeExpenses;
}

它肯定与日期有关,因为以下方法有效(基本上是相同的方法,没有日期的东西):

public List<OnetimeExpense> RetrieveItems(bool isPaid)
{
    var onetimeExpenses = new List<OnetimeExpense>();

    connection.RunInTransaction(() =>
    {
        var items = from s in connection.Table<OnetimeExpense>()
                    where (s.IsPaid == isPaid)
                    select s;
        onetimeExpenses = items.ToList();
    });

    return onetimeExpenses;
}

有趣的部分来了:几个月前我收到了same issue 和一个 WinRT 应用程序。我设法以与上图相同的方式解决了这个问题。

我正在使用SQLite 3.7.16.2。 LINQ 提供程序是 sqlite-net

还有什么? Visual Studio 2012、C#、.Net 4.5。它是一个 WPF 应用程序。

编辑: 这是我的数据对象,它是一个简单的 POCO。 PaymentDate 使用 DateTime.Today 进行初始化,因此永远不会为空。

public class OnetimeExpense : NotifyPropertyChanged
{
    /**
     * int Id
     * string Name
     * DateTime PaymentDate
     * decimal Amount
     * Boolean IsPaid
     * */
    #region getters and setters

    private int id;
    [PrimaryKey, AutoIncrement]
    public int Id
    {
        get { return id; }
        set
        {
            this.id = value;
            this.OnPropertyChanged("Id");
        }
    }

    private DateTime paymentDate = DateTime.Today;
    public DateTime PaymentDate
    {
        get { return this.paymentDate; }
        set
        {
            paymentDate = value;
            this.OnPropertyChanged("PaymentDate");
        }
    }

    private bool isPaid;
    public bool IsPaid
    {
        get { return this.isPaid; }
        set
        {
            this.isPaid = value;
            this.OnPropertyChanged("IsPaid");
        }
    }

    #endregion

    #region Constructor

    public OnetimeExpense(string name, decimal amount, DateTime paymentDate, bool isPaid)
    {
        this.name = name;
        this.paymentDate = paymentDate;
        this.amount = amount;
        this.isPaid = isPaid;
    }

    public OnetimeExpense()
    {
    }

    #endregion
}


编辑2 Gert Arnold 建议使用此解决方法。据我了解,它正在工作并且具有比我的查询更好的性能。不过,我想知道上面的查询有什么问题。
public List<OnetimeExpense> RetrieveItems(int month, int year, bool isPaid)
{
    var onetimeExpenses = new List<OnetimeExpense>();

    var lowerBound = new DateTime(year, month, 1);
    var upperBound = lowerBound.AddMonths(1);

    connection.RunInTransaction(() =>
    {
        var items = from s in connection.Table<OnetimeExpense>()
                    where s.PaymentDate >= lowerBound
                       && s.PaymentDate < upperBound
                       && s.IsPaid == isPaid
                    select s;
        onetimeExpenses = items.ToList();
    });

    return onetimeExpenses;
}

【问题讨论】:

  • 在这两个查询中,每个查询项中应该有多少个结果?
  • PaymentDate 是可以为空的 DateTime 列吗?如果是这样,将空值转换为 DateTime 可能会出现问题?
  • 也许检查 s.PaymentDate 是否为空,类似于where ((convertedDate != null ? convertedDate.Month : null) == month)...
  • @MikePrecup:尝试使用空数据库和一些测试数据(大约 20 个条目)两次都出现相同的异常。
  • @TI 和 Netricity:我明天会重新检查,但我很确定 s.PaymentDate 不为空。看看我的数据模型,你可以看到 PaymentDate 是用 DateTime.Today 初始化的;其他参数(月、年和 isPaid)也不为空。

标签: c# linq sqlite linq-to-sql sqlite-net


【解决方案1】:

不是直接的解决方案,但您可以通过不同的过滤来规避问题。假设您要过滤 2013 年 5 月的记录:

var lowerBound = new DateTime(2013,5,1);
var upperBound = new DateTime(2013,6,1);

var items = from s in connection.Table<OnetimeExpense>()
            where s.PaymentDate >= lowerBound
               && s.PaymentDate < upperBound
               && s.IsPaid == isPaid
            select s;

这不仅仅是回避问题。当PaymentDate 上有索引时,它还具有使查询更高效的潜力。像convertedDate.Year 这样的表达式被翻译成DATEPART(Year, [t0].[PaymentDate])。此类表达式不是sargable,即数据库引擎无法使用索引进行查找。

【讨论】:

  • 听起来不错,我会试一试,然后再回复你。我不知道 sargable 或 non-sargable 查询谢谢。
  • 您的查询有效,谢谢。但尽管如此,我还是想了解为什么我的查询不起作用。
【解决方案2】:
 if (LoggingFilter.StartDate.HasValue)
            {
                logs = auditlogs.Where(x => x.DateTime > auditLoggingFilter.StartDate).ToList();
            }
            if (LoggingFilter.EndDate.HasValue)
            {
                LoggingFilter.EndDate = LoggingFilter.EndDate.Value.AddTicks(DateTime.Now.TimeOfDay.Ticks);
                logs = auditlogs.Where(x => x.DateTime < LoggingFilter.EndDate).ToList();
            }

这对我有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    • 2018-08-21
    相关资源
    最近更新 更多