我正在尝试使用IRepository模式(C#,MVC5)创建一个MongoDB Web应用程序,以便更容易进行单元测试。只是想知道是否有人可以告诉我为什么这更快的信息。
这是使用最新的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在3ms内完成,FilterDefinition在43ms内完成。
我想知道为什么对IQueryable的Linq查询比使用API发送请求只是为了返回特定值更快?
更新:根据@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
答案 0 :(得分:6)
您最初的问题是为什么LINQ比使用API快得多。这个问题的答案是因为LINQ是延迟(懒惰)执行而且查询实际上并未完成。在您实际尝试迭代结果(foreach / .ToList()/ etc)之前,查询将无法完成。
您可能会对此声明进行时间安排:
IQueryable<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "<search text>");
你什么时候应该说这个陈述:
List<Client> asd4 = collection.SearchFor().Where(x => x.ClientDesc == "<search text>").ToList();
您在更新期间显示的效果数字似乎合理。 LINQ实际上比使用直接API稍慢,因为它为查询添加了抽象。这种抽象将允许您轻松地将MongoDB更改为另一个数据源(SQL Server / Oracle / MySQL / XML /等),而无需更改代码,但您只需轻微的性能影响即可为该抽象付费。