Запросы к базе данных и запросы к IQueryable для создания функций автозаполнения - PullRequest
0 голосов
/ 04 марта 2012

У меня есть элемент управления texbox в моем приложении wpf, где я хочу получать список автозаполнения при вводе пользователем. Другими словами, у меня есть что-то вроде окна поиска Google:

enter image description here


Мне удалось сделать это двумя способами, и я хотел бы знать, какой из них более эффективен.

Первый способ:

КакПользователь вводит текстовое поле каждый раз, когда изменяется текстовое поле. Я обновляю список, запрашивая базу данных.поэтому у меня есть что-то вроде:

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 ]

3 голосов
/ 04 марта 2012

Не будет существенной разницы между двумя версиями.Это потому, что следующий код не делает то, что вы думаете:

// 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 голос
/ 04 марта 2012

Если ваш класс 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 секунд вы отправляете запрос с фильтром.

Взгляните на . Это позволит вам неплохо регулировать событие keyup.

Я написал статью об этом здесь:
http://www.gideondsouza.com/blog/implementing-simple-instant-search-with-rx-reactive-extensions (что примерно сводит на нет поиск)

А затем еще один, в котором говорится о linq-to-entity для ограничения поиска в базе данных:
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();
        });
}
...