【问题标题】:Search query IQueryable<> MVC搜索查询 IQueryable<> MVC
【发布时间】:2015-10-26 00:08:12
【问题描述】:

我的搜索查询有问题,当我在 MVC 中使用 IQueryable 时,它没有从数据库中选择任何值。

我的代码如下所示:

IQueryable<Invoice> res =
            (from c in table
                join i in tableX on c.attr equals i.Id
                where c.attr== merchant
                select i);


        if (buyer > 0)
        {
            res = res.Where(i => i.Buyer.Msisdn == buyer);
        }

        if (send)
        {
            res =res.Where(i => i.Status == InvoiceStatus.Sent);                 
        }
        if (paid)
        {
            res=  res.Where(i => i.Status == InvoiceStatus.Paid);
        }
        if (notBilled)
        {
            res = res.Where(i => i.Status == InvoiceStatus.Open);
        }

        if (startDate <= endDate)
        {
            res =  res.Where(i => i.DateCreated >= startDate && i.DateCreated <= endDate);
        }

        return res.ToList();

如果我没有设置 res = res.Where() 而是只有 res.where() 查询将从数据库中选择值。有人可以让我理解为什么会这样。我认为您需要将查询结果存储在变量中。

【问题讨论】:

    标签: c# asp.net linq iqueryable


    【解决方案1】:

    在您执行 ToList() 之前,IQueryable 对象实际上并不包含数据。在那之前,它们只是查询。因此,您在作业中所做的事情被替换为......我不知道是什么。你应该做的是这样的:

    IQueryable Results = res.Where(i => i.Status == InvoiceStatus.Paid); //(or whatever)
    return (Results.ToList());
    

    【讨论】:

    • 是的,这是我第一次想到有另一个 Iqueryable 结果并将每个 where 子句附加到该结果。但这不起作用,所以我不知道。
    【解决方案2】:

    您发布的代码看起来是实现 IQueryable 的合适方式。

    res = res.Where(...)
    

    res.ToList(); 处执行查询之前,基本上会添加额外的 where 子句信息。

    调用 res.Where 实际上并不会更改 res 查询。

    您可能只是过多地限制了 where 子句并从查询中删除了所有记录。

    您是否尝试过分析查询以确定正在查询的内容?

    我可以告诉你,如果sendpaidnotbilled 中的一个以上为真,那将立即不允许从查询返回任何结果,因为它们都在检查@ 987654329@ 列 - 不可能有多个值。

    编辑

    我不知道这是否会有所帮助,但这里有一个关于 IQueryable 复杂性的小提琴:https://dotnetfiddle.net/d70XKA

    这是小提琴的代码:

    public class Program
    {
    
        public static void Main()
        {
            Thingy t = new Thingy();
    
            // Note execution is deferred until enumeration (in this case Count())
            var allData = t.GetData();
            Console.WriteLine("All Data count: {0}", allData.Count());
    
            // Select only valid records from data set (should be 2)
            var isValid = t.GetData();
            isValid = isValid.Where(w => w.IsValid);
            Console.WriteLine("IsValid count: {0}", isValid.Count());
    
            // select only records with an ID greater than 1 (should be 2)
            var gt1 = t.GetData();
            gt1 = gt1.Where(w => w.Id > 1);
            Console.WriteLine("gt 1 count: {0}", gt1.Count());
    
            // Here we're combining in a single statement, IsValid and gt 1 (should be 1)
            var isValidAndIdGt1 = t.GetData();
            isValidAndIdGt1 = isValidAndIdGt1.Where(w => w.IsValid && w.Id > 1);
            Console.WriteLine("IsValid and gt 1 count: {0}", isValidAndIdGt1.Count());
    
            // This is the same query as the one directly above, just broken up (could perhaps be some if logic in there to determine if to add the second Where
            // Note this is how you're doing it in your question (and it's perfectly valid (should be 1)
            var isValidAndIdGt1Appended = t.GetData();
            isValidAndIdGt1Appended = isValidAndIdGt1Appended.Where(w => w.IsValid);
            isValidAndIdGt1Appended = isValidAndIdGt1Appended.Where(w => w.Id > 1);
            Console.WriteLine("IsValid and gt 1 count w/ appended where: {0}", isValidAndIdGt1Appended.Count());
    
            // This is the same query as the one directly above, but note we are executing the query twice
            var isValidAndIdGt1AppendedTwice = t.GetData();
            isValidAndIdGt1AppendedTwice = isValidAndIdGt1AppendedTwice.Where(w => w.IsValid);
            Console.WriteLine("IsValid and gt 1 count w/ appended where executing twice: {0}", isValidAndIdGt1AppendedTwice.Count()); // 2 results are valid
            isValidAndIdGt1AppendedTwice = isValidAndIdGt1AppendedTwice.Where(w => w.Id > 1);
            Console.WriteLine("IsValid and gt 1 count w/ appended where executing twice: {0}", isValidAndIdGt1AppendedTwice.Count()); // 1 result is both valid and id gt 1
    
            // This is one of the things you were asking about - note that without assigning the additional Where criteria to the Iqueryable, you do not get the results of the where clause, but the original query - in this case there are no appended where conditions on the t.GetData() call, so you get the full result set.
            var notReallyValid = t.GetData();
            notReallyValid.Where(w => w.Name == "this name definitly does not exist");
            Console.WriteLine("where clause not correctly appended count: {0}", notReallyValid.Count());
    
            // vs
            var validUse = t.GetData();
            validUse = validUse.Where(w => w.Name == "this name definitly does not exist");
            Console.WriteLine("valid use count: {0}", validUse.Count());
    
        }
    
    }
    
    public class Thingy
    {
        private List<Foo> _testData = new List<Foo>()
        {
            new Foo()
            {
                Id = 1,
                Name = "Alpha",
                Created = new DateTime(2015, 1, 1),
                IsValid = true
            },
            new Foo()
            {
                Id = 2,
                Name = "Beta",
                Created = new DateTime(2015, 2, 1),
                IsValid = false
            },
            new Foo()
            {
                Id = 3,
                Name = "Gamma",
                Created = new DateTime(2015, 3, 1),
                IsValid = true
            },          
        };
    
        public IQueryable<Foo> GetData()
        {
            return _testData.AsQueryable();
        }
    
        public void PrintData(IEnumerable<Foo> data)
        {
            // Note calling this will enumerate the data for IQueryable
            foreach (Foo f in data)
            {
                Console.WriteLine(string.Format("id: {0}, name: {1}, created: {2}, isValid: {3}", f.Id, f.Name, f.Created, f.IsValid));
            }
        }
    }
    
    public class Foo
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime Created { get; set; }
        public bool IsValid { get; set; }
    }
    

    说了这么多,你的 where 子句中有一些东西会过滤掉你的预期数据。正如您从上面的示例中看到的那样,res = res.Where(...)res.Where(...) 非常不同——前者是正确的方法。后者只是完全省略语句中的所有 where 子句,然后当调用 ToList() 时,您将获得完整的结果集,因为没有添加 Where 条件(从原始 var 创建中保存 where c.attr== merchant

    【讨论】:

    • Send、paid 和 notBilled 是 GUI 中的复选框,所以如果那是真的,那就是在查询中。但我明白你的意思。如果用户选中所有框,它们都指向相同的状态
    • 好的,感谢您的帮助和非常好的信息。我想我现在解决了。这是查询的问题, res = res.Where() 确实有效。
    猜你喜欢
    • 1970-01-01
    • 2015-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    • 2021-07-16
    • 2012-05-21
    相关资源
    最近更新 更多