我只是测试一个简单的查询,我以不同的方式访问,但每个查询的速度最多可以变化2秒。我希望有人能澄清为什么会这样。我的项目处于早期阶段,所以我想我确保在它变得太大之前就做好了。
不可否认,我的测试风格并不完美,但我认为它已经足够好了。
我使用通用存储库和UnitofWork,并在此while
语句中点击了DB(本地计算机上的sqlexpress)10,000次。该表只有64条记录。测试在发布模式下运行。
[TestMethod]
public void MyTestMethod()
{
using (var u = new UnitOfWork())
{
TestA(u);
TestB(u);
}
}
TestA(Func):
public void TestA(UnitOfWork u)
{
Stopwatch s = Stopwatch.StartNew();
s.Start();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
Console.WriteLine("TESTA: " + s.Elapsed);
}
TestB(表达式):
public void TestB(UnitOfWork u)
{
Stopwatch s = Stopwatch.StartNew();
s.Start();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.First(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
Console.WriteLine("TESTB: " + s.Elapsed);
}
即使我正在使用First()
和Single()
来电,但它们并不是内置的LINQ调用。他们是我的存储库的一部分。
First()
表达式(IQueryable)
public TEntity Single(Func<TEntity, bool> predicate)
{
return dbSet.FirstOrDefault(predicate);
}
Single()
func(IEnumerable)
public TEntity First(Expression<Func<TEntity, bool>> predicate)
{
return dbSet.FirstOrDefault(predicate);
}
输出:
Test Name: MyTestMethod
Test Outcome: Passed
Result StandardOutput:
TESTA: 00:00:02.4798818
TESTB: 00:00:03.4212112
答案 0 :(得分:2)
Expression<Func<...>>
参数的 First()
是IQueryable<T>
上的扩展方法,供查询提供程序使用,如LINQ to Entities。您提供的表达式树将转换为正确的SQL查询,该查询将发送到DB,并且只将必要的行返回给您的应用程序。
Func<...>
参数的 First()
是IEnumerable<T>
上的扩展方法,由LINQ to Objects使用,这意味着数据库中的所有记录都将被提取到应用程序内存中,然后是元素将搜索为内存中查询,实现为线性搜索。
您绝对应该使用IQueryable<T>
中的那个,因为它会更有效(因为数据库已经过优化以执行查询)。
答案 1 :(得分:1)
我将列出一些您可能想要尝试的测试,以帮助您缩小操作之间的差异。
检查实际的SQL代码
打开查询的调试日志或在SSE日志上进行检查。这很重要,因为EF引擎应该优化语句,你可以看到发送给数据库的真正内容。
正如你所说,First
操作应该更快,因为有优化的SQL运算符。 Single
应该更慢,因为它必须验证所有值,并且会根据行数进行缩放。
使用数据库上的真实SQL进行参考测试
拥有真正的SQL后,您还可以直接检查数据库所用时间的差异。在DB上执行相同的C#测试,也可以使用Sotred Procedure,看看会发生什么。
尝试使用内置LINQ进行比较
我不知道你是否已经为测试做过,但尝试使用原生LINQ进行比较。
我在这里使用LINQ做了很多测试,你提出的两个语句之间没有区别,所以它实际上可能是表达式。 (我使用SS CE btw)。
另外,只是为了说出来,重新为重型操作中涉及的列创建索引;) EF 6.1现在内置了此功能。
[Index]
public String MyProperty{ get; set; }
让我知道它是否有用。
答案 2 :(得分:1)
这不是答案,只是试图确保测试结果更可靠。
尝试编写这样的测试:
public long TestA()
{
using (var u = new UnitOfWork())
{
var s = Stopwatch.StartNew();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
return s.ElapsedMilliseconds;
}
}
(显然TestB
只是一个小变种。)
然后您的测试方法变为:
[TestMethod]
public void MyTestMethod()
{
var dummyA = TestA();
var dummyB = TestB();
var realA = 0L;
var realB = 0L;
for (var i = 0; i < 10; i++)
{
realA += TestA();
realB += TestB();
}
Console.WriteLine("TESTA: " + realA.ToString());
Console.WriteLine("TESTB: " + realA.ToString());
}
现在您的结果可能会更准确。现在让我们知道时间安排。
现在尝试更改您的测试:
public int TestA()
{
var gc0 = GC.CollectionCount(0);
using (var u = new UnitOfWork())
{
var s = Stopwatch.StartNew();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
}
return GC.CollectionCount(0) - gc0;
}
这应该确定正在执行多少代0垃圾收集。这可能表明性能问题与您的测试有关,而与SQL无关。