Domanda

Ho memorizzato 30.000 SimpleObjects nel mio database:

class SimpleObject
{
    public int Id { get; set; }
} 

Voglio eseguire una query su db4o che trova tutti i SimpleObjects con qualsiasi degli ID specificati:

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();
}  

Come faccio a scrivere questa query in modo che db4o non si attiva tutte le 30.000 oggetti?

È stato utile?

Soluzione

Se si tenta di eseguire questa query utilizzando LINQ sarà eseguito non ottimizzata (il che significa che db4o stanno andando a recuperare tutti gli oggetti di tipo SimpleObject e delegare il resto a LINQ to Objects)

L'approccio migliore sarebbe quello di eseguire query n (dal momento che il campo id è indicizzato, ogni query deve correre veloce) e aggregare i risultati come suggerito da "Mark Hall".

È anche possibile utilizzare LINQ per questo (qualcosa di simile)

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]);
     }
}

Best

Altri suggerimenti

Io non sono un esperto su questo, e potrebbe essere buono per postare sul forum db4o su di esso, ma credo di avere una soluzione. Non comporta l'uso di LINQ e l'utilizzo di soda.

Questo è quello che ho fatto. Ho creato un progetto semplice che popola il database con 30000 SimpleObject sulla base di definizione del tuo post. Ho quindi scritto una query per afferrare tutti i SimpleObjects dal database:

var simpleObjects = db.Query<SimpleObject>(typeof(SimpleObject));

Quando ho avvolto un cronometro intorno ad esso, che corsa dura circa 740 millisecondi. Ho quindi utilizzato il codice per la ricerca di un 100 numeri casuali compresi tra 0 e 2999. La risposta è stata 772 ms, così sulla base di quel numero Io parto dal presupposto che si tratta di tirare fuori tutti gli oggetti del database. Non sono sicuro di come verificare che, ma poi penso che ho dimostrato con le prestazioni.

Mi sono recato poi in basso. Dalla mia comprensione il provider LINQ dal team db4o è solo facendo una traduzione in SODA. Perciò ho pensato che avrei scritto una query SODA per testare, e quello che ho trovato è stato che l'uso di SODA contro una proprietà è male per le prestazioni perché ci sono voluti 19902 ms da eseguire. Ecco il codice:

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

Quindi, pensare a perché questo sarebbe così male, ho deciso di non utilizzare una proprietà auto-implementato e definirla la mia auto perché le proprietà sono in realtà i metodi e non i valori:

public class SimpleObject
{
    private int _id;

    public int Id { 
        get
        { return _id; }
        set
        { _id = value; }
    }
}

Poi ho riscritto la query per utilizzare il campo privato _id invece della proprietà. La performance è stata molto meglio a circa 91 ms. Ecco che il codice:

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

Solo per fare in modo che non è stato un colpo di fortuna, ho eseguito il test diverse volte e ricevuto risultati simili. Ho poi aggiunto un altro 60.000 record per un totale di 90.000, e questo era le differenze di prestazioni:

GetAll: 2450 ms
GetWithOriginalCode: 2694 ms
GetWithSODAandProperty: 75373 ms
GetWithSODAandField: 77 ms

La speranza che aiuta. So che non spiega veramente perché, ma questo potrebbe aiutare con il come. Anche il codice per la query campo SODA non sarebbe difficile per avvolgere ad essere più generico.

Non ho fatto molto con db4o LINQ. Ma è possibile utilizzare la DiagnosticToConsole (o ToTrace) e aggiungerlo al IConfiguration.Diagnostic (). AddListener. Questo ti mostrerà se la query è ottimizzata.

Non te ne frega un sacco di dettagli, ma è la proprietà Id su SimpleObject indicizzato?

Una volta che la diagnostica sono attivati, si potrebbe provare la query in questo modo ...

from SimpleObject simple in db
where matchingIds.Contains(simple.Id)
select simple

Vedere se che ti dà un piano di query diversa.

Si potrebbe 'costruire' una query LINQ dinamica. Per esempio l'API potrebbe assomigliare a questo:

Il primo parametro: un'espressione che indica su quale proprietà si cerca Gli altri parametri:. I id o qualsiasi altra cosa si sta cercando

 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");

L'implementazione costruisce una query dinamica in questo modo:

var result = from SimpleObject t 
  where t.Id = 42 || t.Id==77 ... t.Id == N
  select t

Poiché tutto è combinato con il OR può essere valutata direttamente sugli indici. Non ha bisogno di attivazione. Esempio-Implementazioni:

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

}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top