我正在尝试使用IRepository模式(C#,MVC5)创建一个MongoDB Web应用程序,以便更容易进行单元测试.只是想知道是否有人可以告诉我为什么这更快.
这是使用最新的MongoDB c#驱动程序.
在我的IRepository类中,我有以下内容
IQueryableSearchFor(); List SearchFor(FilterDefinition filter);
找到一个SO帖子,建议使用IQueryable来提高使用IEnumerable的速度.
这是MongoRepository类的代码.
public IQueryableSearchFor() { return _collection.AsQueryable (); } public List SearchFor(FilterDefinition filter) { return _collection.Find(filter).ToList(); }
据我所知,过滤器定义是您通常如何将查询编码到数据库.
以下是从数据库获取数据的调用
IQueryableasd4 = collection.SearchFor().Where(x => x.ClientDesc == " "); FilterDefinition filter1 = Builders .Filter.Eq("ClientDesc", " "); List asd10 = collection.SearchFor(filter1).ToList ();
请注意,我知道我应该使用IQueryable和Linq路由只是因为IRepository不应该包含技术相关的classess(如FilterDefinition).
当针对具有30k简单文档的集合进行测试并测试不同方法的速度时,我得到以下结果.
使用IQueryable在3ms内完成,FilterDefinition在43ms内完成.
我想知道为什么对IQueryable的Linq查询比使用API发送请求只是为了返回特定值更快?
更新:根据@lenkan的建议,我为IQueryable的每个循环添加了一个.
public void PerformanceTest(IRepositorycollection) { 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 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 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 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 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 filter1 = Builders .Filter.Eq("ClientDesc", "hahahahahahahahah 10"); List asd10 = collection.SearchFor(filter1).ToList (); 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 filter2 = Builders .Filter.Eq("ClientDesc", "hahahahahahahahah 29999"); List asd11 = collection.SearchFor(filter2).ToList (); 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 filter3 = Builders .Filter.Eq("ClientDesc", "hahahahahahahahah " + i); List asd12 = collection.SearchFor(filter3).ToList (); } 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
Robert McKee.. 8
您最初的问题是为什么LINQ比使用API快得多.这个问题的答案是因为LINQ是延迟(延迟)执行而且查询实际上没有完成.在您实际尝试迭代结果(foreach/.ToList()/ etc)之前,查询将不会完成.
您可能会对此声明进行定时:
IQueryableasd4 = collection.SearchFor().Where(x => x.ClientDesc == " ");
什么时候你应该有这个声明:
Listasd4 = collection.SearchFor().Where(x => x.ClientDesc == " ").ToList();
您在更新期间显示的性能数据似乎是合理的.LINQ实际上比使用直接API稍慢,因为它为查询添加了抽象.这种抽象允许您轻松地将MongoDB更改为另一个数据源(SQL Server/Oracle/MySQL/XML /等)而无需更改代码,但您只需轻微的性能影响即可为该抽象付费.
您最初的问题是为什么LINQ比使用API快得多.这个问题的答案是因为LINQ是延迟(延迟)执行而且查询实际上没有完成.在您实际尝试迭代结果(foreach/.ToList()/ etc)之前,查询将不会完成.
您可能会对此声明进行定时:
IQueryableasd4 = collection.SearchFor().Where(x => x.ClientDesc == " ");
什么时候你应该有这个声明:
Listasd4 = collection.SearchFor().Where(x => x.ClientDesc == " ").ToList();
您在更新期间显示的性能数据似乎是合理的.LINQ实际上比使用直接API稍慢,因为它为查询添加了抽象.这种抽象允许您轻松地将MongoDB更改为另一个数据源(SQL Server/Oracle/MySQL/XML /等)而无需更改代码,但您只需轻微的性能影响即可为该抽象付费.