我在我的数据库中存储了30,000个SimpleObject:
class SimpleObject
{
public int Id { get; set; }
}
我想在DB4O上运行查询,查找具有任何指定ID的所有SimpleObject:
public IEnumerable<SimpleObject> GetMatches(int[] matchingIds)
{
// OH NOOOOOOES! This activates all 30,000 SimpleObjects. TOO SLOW!
var query = from SimpleObject simple in db
join id in matchingIds on simple.Id equals id
select simple;
return query.ToArray();
}
如何编写此查询以便DB4O不会激活所有30,000个对象?
答案 0 :(得分:3)
我不是这方面的专家,在DB4O论坛上发帖可能会很好,但我认为我有一个解决方案。它涉及不使用LINQ和使用SODA。
这就是我所做的。我创建了一个快速项目,根据您的帖子定义使用30000 SimpleObject填充数据库。然后我编写了一个查询来从数据库中获取所有SimpleObject:
var simpleObjects = db.Query<SimpleObject>(typeof(SimpleObject));
当我在其周围包裹一个StopWatch时,该运行大约需要740毫秒。然后我用你的代码搜索0到2999之间的100个随机数。响应是772毫秒,所以基于这个数字,我假设它将所有对象拉出数据库。我不确定如何验证,但后来我认为我证明了它的性能。
然后我走低了。根据我的理解,DB4O团队的LINQ提供商正在对SODA进行翻译。因此,我认为我会编写一个SODA查询进行测试,我发现使用SODA对一个属性的性能不好,因为它花了19902毫秒来执行。这是代码:private SimpleObject[] GetSimpleObjectUsingSodaAgainstAProperty(int[] matchingIds, IObjectContainer db)
{
SimpleObject[] returnValue = new SimpleObject[matchingIds.Length];
for (int counter = 0; counter < matchingIds.Length; counter++)
{
var query = db.Query();
query.Constrain(typeof(SimpleObject));
query.Descend("Id").Constrain(matchingIds[counter]);
IObjectSet queryResult = query.Execute();
if (queryResult.Count == 1)
returnValue[counter] = (SimpleObject)queryResult[0];
}
return returnValue;
}
所以考虑为什么会这么糟糕,我决定不使用自动实现的属性并将其定义为我自己,因为Properties实际上是方法而不是值:
public class SimpleObject
{
private int _id;
public int Id {
get
{ return _id; }
set
{ _id = value; }
}
}
然后我重写了查询以使用_id私有字段而不是属性。性能在大约91毫秒时好得多。这是代码:
private SimpleObject[] GetSimpleObjectUsingSodaAgainstAField(int[] matchingIds, IObjectContainer db)
{
SimpleObject[] returnValue = new SimpleObject[matchingIds.Length];
for (int counter = 0; counter < matchingIds.Length; counter++)
{
var query = db.Query();
query.Constrain(typeof(SimpleObject));
query.Descend("_id").Constrain(matchingIds[counter]);
IObjectSet queryResult = query.Execute();
if (queryResult.Count == 1)
returnValue[counter] = (SimpleObject)queryResult[0];
}
return returnValue;
}
为了确保它不是侥幸,我跑了几次试运行并收到了类似的结果。然后我添加了另外60,000条记录,共计90,000条记录,这就是性能差异:
GetAll: 2450 ms
GetWithOriginalCode: 2694 ms
GetWithSODAandProperty: 75373 ms
GetWithSODAandField: 77 ms
希望有所帮助。我知道它并没有真正解释为什么,但这可能有助于如何。此外,SODA字段查询的代码也不难包装为更通用。
答案 1 :(得分:2)
我没有对db4o LINQ做过多少工作。但您可以使用DiagnosticToConsole(或ToTrace)并将其添加到IConfiguration.Diagnostic()。AddListener。这将显示查询是否已优化。
您没有提供很多细节,但是SimpleObject上的Id属性是否被索引?
启用诊断后,您可以尝试查询......
from SimpleObject simple in db
where matchingIds.Contains(simple.Id)
select simple
看看是否能为您提供不同的查询计划。
答案 2 :(得分:2)
如果您尝试使用LINQ运行此查询,它将运行未优化(这意味着db4o将检索SimpleObject类型的所有对象并将其余部分委托给LINQ到对象)
最好的方法是运行n个查询(因为id字段被索引,每个查询应该快速运行)并按照“Mark Hall”的建议聚合结果。
你甚至可以使用LINQ(类似的东西)
IList<SimpleObject> objs = new List<SimpleObject>();
foreach(var tbf in ids)
{
var result = from SimpleObject o in db()
where o.Id = tbf
select o;
if (result.Count == 1)
{
objs.Add(result[0]);
}
}
最佳
答案 3 :(得分:2)
您可以“构建”动态linq查询。例如,API可能如下所示:
第一个参数:一个表达式,它告诉您搜索哪个属性 其他参数:id或你要搜索的任何内容。
var result1 = db.ObjectByID((SimpleObject t) => t.Id, 42, 77);
var result2 = db.ObjectByID((SimpleObject t) => t.Id, myIDList);
var result3 = db.ObjectByID((OtherObject t) => t.Name, "gamlerhart","db4o");
该实现构建了一个动态查询,如下所示:
var result = from SimpleObject t
where t.Id = 42 || t.Id==77 ... t.Id == N
select t
由于所有内容都与OR结合,因此可以直接在索引上进行评估。它不需要激活。实施例的实现:
public static class ContainerExtensions{
public static IDb4oLinqQuery<TObjectType> ObjectByID<TObjectType, TIdType>(this IObjectContainer db,
Expression<Func<TObjectType, TIdType>> idPath,
params TIdType[] ids)
{
if(0==ids.Length)
{
return db.Cast<TObjectType>().Where(o=>false);
}
var orCondition = BuildOrChain(ids, idPath);
var whereClause = Expression.Lambda(orCondition, idPath.Parameters.ToArray());
return db.Cast<TObjectType>().Where((Expression<Func<TObjectType, bool>>) whereClause);
}
private static BinaryExpression BuildOrChain<TIdType, TObjectType>(TIdType[] ids, Expression<Func<TObjectType, TIdType>> idPath)
{
var body = idPath.Body;
var currentExpression = Expression.Equal(body, Expression.Constant(ids.First()));
foreach (var id in ids.Skip(1))
{
currentExpression = Expression.OrElse(currentExpression, Expression.Equal(body, Expression.Constant(id)));
}
return currentExpression;
}
}