查询数据库与查询IQueryable以创建自动完成功能

时间:2012-03-04 15:08:17

标签: c# database performance linq iqueryable

我的wpf应用程序中有一个texbox控件,我希望在用户输入时获得一个自动完成列表框。换句话说,我有类似google的搜索框:

enter image description here


我已经设法以两种方式做到这一点,我想知道哪一种更有效。

第一种方式:

当用户在每次texbox更改时键入文本框时,我通过查询数据库来更新列表框。因此我有类似的东西:

void textBox1_KeyUp(object sender, KeyEventArgs e)
{
        // new text
        var content = ((TextBox)sender).Text;

        // I am selecting the posible items using ado.net
        var posibleItems= PdvEntities.Entities.TableFoos.Where(TableFoo=> TableFoo.Description.Contains(content)).Select(c=>c);


        listbox1.ItemsSource = posibleItems;
}

请注意,通过这种方法,每次keyup事件在该文本框中触发时,我都会查询数据库。

第二种方式:

不是每次启动keyup事件时都查询数据库,而是执行以下操作:

// select all items and store that as a global variable
IQueryable allItems = PdvEntities.Entities.TableFoos.Select(a => a);


void textBox1_KeyUp(object sender, KeyEventArgs e)
{
        // new text
        var content = ((TextBox)sender).Text;

        // I don't have the code but I will then filter variable 
        // allItems based if their description contains 'content'

        // pseudo code
        newFileter <- filter of allItems that contain content


        listbox1.ItemsSource = newFileter;
}

不是说在这种情况下我只查询一次数据库,每当我需要将项目添加到列表框时,我将查询IQueryable变量而不是数据库。我担心如果数据库很大,这种技术会占用大量内存。


另外我忘了提到数据库可能不是本地的。现在我在本地连接到数据库,但此应用程序可能与远程数据库连接一起运行。哪种方法更有效?

2 个答案:

答案 0 :(得分:3)

两个版本之间不会有任何显着差异。那是因为以下代码没有按照您的想法执行:

// select all items and store that as a global variable
IQueryable allItems = PdvEntities.Entities.TableFoos.Select(a => a);

它不会“存储”字段中的所有项目,甚至根本不会进入数据库。它只是一个查询,如果你迭代它可以检索所有项目。

另一方面,如果你做了类似

的事情
Foo[] allItems = PdvEntities.Entities.TableFoos.ToArray();

实际上会将所有项目检索到内存中。但如果我们不了解您的数据库和执行环境的所有内容,就无法知道哪一个会更有效。

答案 1 :(得分:1)

如果您的PdvEntities类是EntityFramework上下文,则以下是Linq-to-Entities查询,该查询将针对您的数据库生成T-SQL并仅获取已过滤的项目。

var posibleItems= PdvEntities.Entities.TableFoos
  .Where(TableFoo=> TableFoo.Description.Contains(content)).Select(c=>c);

不确定我理解您的其他解决方案。正如@svik提到的那样您可以使用ToArray()ToList()来获取内存中的所有项目,但这根本不是完整的。

看起来您需要限制对数据库的调用,因此使用类型时,每隔n秒发送一次带过滤器的查询。

看看。它将允许您以一种很好的方式限制您的键盘事件。

我在这里写了一篇文章:
http://www.gideondsouza.com/blog/implementing-simple-instant-search-with-rx-reactive-extensions(这只是限制搜索)

然后另一个谈论linq-to-entities来限制数据库搜索:
http://www.gideondsouza.com/blog/abstracting-reactive-extensions-for-sql-server-compact-and-implementing-an-instant-search


基于我在文章中写的东西你可以做这样的事情:

你需要一个小帮手

public class ObservableHelper<T>
    where T : class //or EntityObject 
{
    public ObservableHelper()
    {
        _dat = new PdvEntities();
    }
    PdvEntities _dat;
    public IObservable<IList<T>> GetAllAsObservables
                                (Func<PdvEntities, IQueryable<T>> funcquery)
    {
        var getall = Observable.ToAsync<PdvEntities, IQueryable<T>>(funcquery);
        return getall(_dat).Select(x => x.ToList());
    }
}
表单中的

然后

public Form1()
{
    InitializeComponent();
    //your playing with IQueryable<TableFoos>
    _repo = new ObservableHelper<TableFoos>()

    Observable.FromEventPattern(h => textBox1.KeyUp += h,
                           h => textBox1.KeyUp -= h)//tell Rx about our event
        .Throttle(TimeSpan.FromMilliseconds(500), cs)///throttle
        .ObserveOn(Scheduler.Dispatcher);//so we have no cross threading issues
        .Do(a => SearchList(textBox1.Text))//do this method 
        .Subscribe();
}

IObservableHelper<TableFoos, PdvEntities> _repo;

void SearchList(string query)
{//AS MANY keystrokes are there, this function will be called only
 // once every 500 milliseconds..

    listBox1.Items.Clear();
    listBox1.BeginUpdate();
    var getfn = _repo.GetAllAsObservables
        (d => d.TableFoos.Where(c => c.TableFoos.Contains(query)));
    getfn.ObserveOn(this).Subscribe(resultList => //is an IList<TableFoos>
        {
            foreach (var item in resultList)
            {
                listBox1.Items.Add(...
            }
            listBox1.EndUpdate();
        });
}