Я пытаюсь создать веб-приложение MongoDB с шаблоном IRepository (C#, MVC5), чтобы упростить модульный тест. Просто интересно, может ли кто-нибудь дать мне информацию о том, почему это происходит быстрее.MongoDB C# Driver: API vs Linq Performance
Использование новейших драйверов для MongoDB C#.
В моем IRepository классе я следующий
IQueryable<T> SearchFor();
List<T> SearchFor(FilterDefinition<T> filter);
Нашли SO пост, который предложил использовать IQueryable для скорости по сравнению с использованием IEnumerable.
Вот код от класса MongoRepository.
public IQueryable<T> SearchFor() {
return _collection.AsQueryable<T>();
}
public List<T> SearchFor(FilterDefinition<T> filter) {
return _collection.Find(filter).ToList();
}
Насколько я могу судить, определение фильтра - это то, как вы обычно кодируете запрос в БД.
Вот вызовы, чтобы получить данные из БД
IQueryable<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "<search text>");
FilterDefinition<Client> filter1 = Builders<Client>.Filter.Eq("ClientDesc", "<search text>");
List<Client> asd10 = collection.SearchFor(filter1).ToList<Client>();
Пожалуйста, обратите внимание, что я знаю, что, вероятно, следует использовать маршрут IQueryable и Linq только для чистого факта, что IRepository не должна содержать технологии зависимую classess (например, FilterDefinition).
При тестировании на коллекцию с 30k простыми документами и тестирование скорости различных методов я получаю следующие результаты.
Использование IQueryable завершается в 3 мс, а FilterDefinition завершается в 43 мс.
Мне интересно, почему запрос Linq на IQueryable быстрее, чем использование API для отправки запроса только для возврата определенного значения?
UPDATE: Из предложения по @lenkan я добавил для каждого цикла для IQueryable.
public void PerformanceTest(IRepository<Client> collection) {
Stopwatch sw = new Stopwatch();
// Delete all records
// ******************
System.Diagnostics.Debug.WriteLine("*****************");
sw.Start();
collection.DeleteAll();
sw.Stop();
System.Diagnostics.Debug.WriteLine("Deleting all records: " + sw.Elapsed);
// Create 30k Records
// ******************
System.Diagnostics.Debug.WriteLine("*****************");
sw.Reset();
sw.Start();
// Create 30k records
for (int i = 0; i < 30000; i++) {
Client testclient = new Client() {
ClientDesc = "hahahahahahahahah " + i
};
collection.Add(testclient);
}
sw.Stop();
System.Diagnostics.Debug.WriteLine("Created: 30k rows: " + sw.Elapsed);
// Test IQueryable & LINQ
// **********************
System.Diagnostics.Debug.WriteLine("*********************");
System.Diagnostics.Debug.WriteLine("* IQueryable & LINQ *");
System.Diagnostics.Debug.WriteLine("*********************");
sw.Reset();
sw.Start();
IQueryable<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah 10");
foreach (Client item in asd4) {
string aaaaaa = item.ClientDesc;
}
sw.Stop();
System.Diagnostics.Debug.WriteLine("Find one from start: " + sw.Elapsed);
sw.Reset();
sw.Start();
IQueryable<Client> asd7 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah 10");
foreach (Client item in asd7) {
string aaaaaa = item.ClientDesc;
}
sw.Stop();
System.Diagnostics.Debug.WriteLine("Find one from start: " + sw.Elapsed);
sw.Reset();
sw.Start();
IQueryable<Client> asd5 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah 29999");
foreach (Client item in asd5) {
string bbbbbb = item.ClientDesc;
}
sw.Stop();
System.Diagnostics.Debug.WriteLine("Find one from end: " + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 10000; i < 10050; i++) {
IQueryable<Client> asd6 = collection.SearchFor().Where(x => x.ClientDesc == "hahahahahahahahah " + i);
foreach (Client item in asd6) {
string aaaaaa = item.ClientDesc;
}
}
sw.Stop();
System.Diagnostics.Debug.WriteLine("Find in loop of 50: " + sw.Elapsed);
// Test Filter & LINQ
// ***********************
System.Diagnostics.Debug.WriteLine("*****************");
System.Diagnostics.Debug.WriteLine("* List & Filter *");
System.Diagnostics.Debug.WriteLine("*****************");
sw.Reset();
sw.Start();
FilterDefinition<Client> filter1 = Builders<Client>.Filter.Eq("ClientDesc", "hahahahahahahahah 10");
List<Client> asd10 = collection.SearchFor(filter1).ToList<Client>();
foreach (Client item in asd10) {
string aaaaaa = item.ClientDesc;
}
sw.Stop();
System.Diagnostics.Debug.WriteLine("Find one from start: " + sw.Elapsed);
sw.Reset();
sw.Start();
FilterDefinition<Client> filter2 = Builders<Client>.Filter.Eq("ClientDesc", "hahahahahahahahah 29999");
List<Client> asd11 = collection.SearchFor(filter2).ToList<Client>();
foreach (Client item in asd11) {
string cccccc = item.ClientDesc;
}
sw.Stop();
System.Diagnostics.Debug.WriteLine("Find one from end: " + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 10000; i < 10050; i++) {
FilterDefinition<Client> filter3 = Builders<Client>.Filter.Eq("ClientDesc", "hahahahahahahahah " + i);
List<Client> asd12 = collection.SearchFor(filter3).ToList<Client>();
}
sw.Stop();
System.Diagnostics.Debug.WriteLine("Find in loop of 50: " + sw.Elapsed);
// Delete all records
// ******************
System.Diagnostics.Debug.WriteLine("*****************");
sw.Start();
collection.DeleteAll();
sw.Stop();
System.Diagnostics.Debug.WriteLine("Deleting all records: " + sw.Elapsed);
}
Вот результаты сейчас. Так что, похоже, перечисляя с IQueryable имеет начальный удар в исполнении, но тогда все, кажется, ускорить, когда вы перезвоните позже ищет то
*****************
Deleting all records: 00:00:00.0670336
*****************
Created: 30k rows: 00:00:04.6829844
*********************
* IQueryable & LINQ *
*********************
Find one from start: 00:00:00.0878309
Find one from start: 00:00:00.0120098
Find one from end: 00:00:00.0116334
Find in loop of 50: 00:00:00.5890532
*****************
* List & Filter *
*****************
Find one from start: 00:00:00.0248407
Find one from end: 00:00:00.0118345
Find in loop of 50: 00:00:00.5377828
*****************
Deleting all records: 00:00:00.7029368
Интересно. Можете ли вы предоставить минимальный образец кода, который вы использовали для бенчмаркинга? Из предоставленных вами вызовов вы никогда не перечисляете asd4 IQueryable. – lenkan
Добавлен ответ на исходное сообщение. – Dwiea
Я по-прежнему вижу большой бонус от использования Linq вместо FilterDefinitions в качестве метода для удаления определенных классов технологий из интерфейса. Несмотря на крошечный успех в производительности (в основном это будет админ-сервер). – Dwiea