Db4o查询:查找ID为{数组中的任何内容}的所有对象

时间:2009-11-06 17:29:06

标签: c# linq db4o

我在我的数据库中存储了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个对象?

4 个答案:

答案 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;
    }

}