题
我已经存储30000个SimpleObjects在我的数据库:
class SimpleObject
{
public int Id { get; set; }
}
我希望运行查找所有SimpleObjects与任何指定的ID的上DB4O的查询:
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对象?
解决方案
如果您尝试运行使用LINQ它会运行未优化(这意味着db4o的打算检索类型SimpleObject的所有对象,并委托其余的LINQ to对象)这个查询
在最好的办法是跑N次查询(因为id字段建立索引,每个查询应该跑得快)和“马克·霍尔”的建议汇总结果。
您甚至可以使用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]);
}
}
最佳
其他提示
我不是这方面的专家,它可能是件好事张贴关于它的DB4O论坛,但我想我有一个解决办法。它包括不使用LINQ和使用SODA。
这就是我所做的。我创建了一个快速的项目填充根据您的文章的定义与30000 SimpleObject数据库。然后我写了一个查询来获取所有SimpleObjects从数据库中:
var simpleObjects = db.Query<SimpleObject>(typeof(SimpleObject));
当我缠它秒表,该运行时间约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;
}
于是想为什么会这样不好,我决定不使用自动实现的属性,并将其定义我自己,因为属性实际上是方法,而不是值:
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;
}
只是为了确保它是不是侥幸,我跑了试运行多次,收到了类似的结果。然后我加入另一个60000条记录为总共90000,这是的性能差异:
GetAll: 2450 ms
GetWithOriginalCode: 2694 ms
GetWithSODAandProperty: 75373 ms
GetWithSODAandField: 77 ms
希望有所帮助。我知道,它并没有真正解释为什么,但是这可能与如何帮助。也为SODA字段查询的代码不会很难换行到更通用的。
我没有做太多使用db4o LINQ。但是你可以使用DiagnosticToConsole(或ToTrace),并把它添加到IConfiguration.Diagnostic()的addListener。这会告诉你,如果在查询优化。
您不给很多细节,但是是SimpleObject Id属性索引?
一旦诊断被打开,你可以尝试像这样的查询...
from SimpleObject simple in db
where matchingIds.Contains(simple.Id)
select simple
看看,给你一个不同的查询计划。
您可以“建”动态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
由于一切都与结合或可以直接在索引进行评估。它不需要激活。实施例的实现:
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;
}
}