SQL to LINQ Query,where子句中的日期不起作用

时间:2013-05-18 19:35:24

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

我尝试通过LINQ查询数据库项,但它无法正常工作。唯一的例外是:

  

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

stacktrace与异常本身一样无益:

  

在SQLite.TableQuery 1.CompileExpr(Expression expr, List 1   queryArgs)在d:\ XX \ XX \ XX \ XX \ XX \ SQLite.cs:第2383行   SQLite.TableQuery 1.CompileExpr(Expression expr, List 1 queryArgs)in   d:\ XX \ XX \ XX \ XX \ XX \ SQLite.cs:第2388行   SQLite.TableQuery 1.CompileExpr(Expression expr, List 1 queryArgs)in   d:\ XX \ XX \ XX \ XX \ XX \ SQLite.cs:第2308行   SQLite.TableQuery 1.CompileExpr(Expression expr, List 1 queryArgs)in   d:\ XX \ XX \ XX \ XX \ XX \ SQLite.cs:第2308行   SQLite.TableQuery 1.CompileExpr(Expression expr, List 1 queryArgs)in   d:\ XX \ XX \ XX \ XX \ XX \ SQLite.cs:第2308行   SQLite.TableQuery 1.GenerateCommand(String selectionList) in d:\XX\XX\XX\XX\XX\SQLite.cs:line 2274 at SQLite.TableQuery 1.GetEnumerator()in   d:\ XX \ XX \ XX \ XX \ XX \ SQLite.cs:第2521行   System.Collections.Generic.List 1..ctor(IEnumerable 1集合)
  在System.Linq.Enumerable.ToList [TSource](IEnumerable`1 source)at   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 month,   Int32年,Boolean isPaid)in   d:\ XX \ XX \ XX \ XX \ XX \ DataModel \ DataManagerOnetimeExpense.cs:第39行at   UnitTestKapital.Database.TestDataManagerOnetimeExpense.TestRetrieveItemsByMonthYearIsPaid()   在   d:\ XX \ XX \ XX \ XX \ XX \ UnitTestKapital \数据库\ TestDataManagerOnetimeExpense.cs:线   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;
}

这就是有趣的部分:几个月前我使用WinRT应用程序获得了same issue。我设法以与上面所示相同的方式解决了这个问题。

我正在使用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
}

<小时/> 的 EDIT2 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;
}

2 个答案:

答案 0 :(得分:2)

不是直接的解决方案,但您可以通过不同的过滤来规避问题。假设您要过滤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,即数据库引擎不能使用索引进行查找。

答案 1 :(得分:0)

 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();
            }

这对我有用。