Ich habe einen einfachen benutzerdefinierten Queryprovider, die einen Ausdruck nimmt, übersetzen sie in SQL und fragen eine SQL-Datenbank.Pass LINQ Ausdruck zu einem anderen Queryprovider
Ich möchte einen kleinen Cache in der Queryprovider erstellen, dass speichert Objekte häufig zugegriffen, so Abruf ohne Datenbank-Hit passieren kann.
Die Queryprovider hat die Methode
public object Execute(System.Linq.Expressions.Expression expression)
{
/// Builds an SQL statement from the expression,
/// executes it and returns matching objects
}
Der Cache als ein Feld in dieser Klasse Queryprovider sitzt und ist eine einfache generische Liste.
Wenn ich die List.AsQueryable Methode verwenden und den obigen Ausdruck in den Execute-Methode des Anbieters List.AsQueryable gebe es funktioniert nicht wie gewünscht. Es sieht so aus, als wenn ein Ausdruck kompiliert wird, wird der anfängliche QueryProvider ein integraler Bestandteil.
Ist es möglich, einen Ausdruck zu einem nachfolgenden Queryprovider und führen um den Ausdruck zu passieren, wie gewünscht?
Der Aufruf Code sieht vage wie folgt:
public class QueryProvider<Entity>()
{
private List<TEntity> cache = new List<Entity>();
public object Execute(System.Linq.Expressions.Expression expression)
{
/// check whether expression expects single or multiple result
bool isSingle = true;
if (isSingle)
{
var result = this.cache.AsQueryable<Entity>().Provider.Execute(expression);
if (result != null)
return result;
}
/// cache failed, hit database
var qt = new QueryTranslator();
string sql = qt.Translate(expression);
/// .... hit database
}
}
Es macht keinen Fehler zurück, anstatt sie in Schleife stecken bleibt, wo derselbe Anbieter über genannt wird und immer wieder.
Hier einige mehr Code zu zeigen, was ich zu tun habe versucht:
Collection:
class Collection<Entity>
{
internal List<Entity> cacheOne { get; private set; }
internal Dictionary<Guid, Entity> cacheTwo { get; private set; }
internal Collection()
{
this.cacheOne = new List<Entity>();
this.cacheTwo = new Dictionary<Guid, Entity>();
}
public IQueryable<Entity> Query()
{
return new Query<Entity>(this.cacheOne, this.cacheTwo);
}
}
Abfrage:
class Query<Entity> : IQueryable<Entity>
{
internal Query(List<Entity> cacheOne, Dictionary<Guid, Entity> cacheTwo)
{
this.Provider = new QueryProvider<Entity>(cacheOne, cacheTwo);
this.Expression = Expression.Constant(this);
}
internal Query(IQueryProvider provider, Expression expression)
{
this.Provider = provider;
if (expression != null)
this.Expression = expression;
}
public IEnumerator<Entity> GetEnumerator()
{
return this.Provider.Execute<IEnumerator<Entity>>(this.Expression);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public Type ElementType
{
get { return typeof(Entity); }
}
public System.Linq.Expressions.Expression Expression { get; private set; }
public IQueryProvider Provider { get; private set; }
}
Queryprovider:
class QueryProvider<Entity> : IQueryProvider
{
private List<Entity> cacheOne;
private Dictionary<Guid, Entity> cacheTwo;
internal QueryProvider(List<Entity> cacheOne, Dictionary<Guid, Entity> cacheTwo)
{
this.cacheOne = cacheOne;
this.cacheTwo = cacheTwo;
}
public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
{
return new Query<TElement>(this, expression);
}
public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
{
throw new NotImplementedException();
}
public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
{
return (TResult)this.Execute(expression);
}
public object Execute(System.Linq.Expressions.Expression expression)
{
Iterator<Entity> iterator = new Iterator<Entity>(expression, cacheOne, cacheTwo);
return (iterator as IEnumerable<Entity>).GetEnumerator();
}
}
Iterator:
class Iterator<Entity> : IEnumerable<Entity>
{
private Expression expression;
private List<Entity> cacheOne;
private Dictionary<Guid, Entity> cacheTwo;
internal Iterator(Expression expression, List<Entity> cacheOne, Dictionary<Guid, Entity> cacheTwo)
{
this.expression = expression;
this.cacheOne = cacheOne;
this.cacheTwo = cacheTwo;
}
public IEnumerator<Entity> GetEnumerator()
{
foreach (var result in (IEnumerable<Entity>)this.cacheOne.AsQueryable<Entity>().Provider.Execute(expression))
{
yield return result;
}
foreach (var more in (IEnumerable<Entity>)this.cacheTwo.Values.AsQueryable<Entity>().Provider.Execute(expression))
{
yield return more;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Programm:
class Program
{
static void Main(string[] args)
{
/// Create collection + caches
var collection = new Collection<Giraffe>();
collection.cacheOne.AddRange(new Giraffe[] {
new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(2011, 03, 21), Height = 192, Name = "Percy" },
new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(2005, 12, 25), Height = 188, Name = "Santa" },
new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(1999, 04, 01), Height=144, Name="Clown" }
});
var cachetwo = new List<Giraffe>(new Giraffe[] {
new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(1980, 03,03), Height = 599, Name="Big Ears" },
new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(1985, 04, 02), Height= 209, Name="Pug" }
});
foreach (var giraffe in cachetwo)
collection.cacheTwo.Add(giraffe.Id, giraffe);
/// Iterate through giraffes born before a certain date
foreach (var result in collection.Query().Where(T => T.DateOfBirth < new DateTime(2006, 01, 01)))
{
Console.WriteLine(result.Name);
}
}
}
Giraffe:
class Giraffe
{
public Guid Id { get; set; }
public string Name { get; set; }
public long Height { get; set; }
public DateTime DateOfBirth { get; set; }
}
Sonderfälle z.B. SingleAndDefault usw. sind weggelassen. Der Teil, den ich arbeiten möchte, passiert in Iterator, wo er zuerst den QueryProvider der Liste ausführt, bevor er das Dictionary ausführt.
Einer der beiden Abfragbare Objekte könnten sonst eine Datenbank, oder so etwas.
Konnten Sie den aufrufenden Code hinzufügen? –
Telefoncode hinzugefügt – Anthony
Können Sie ein Beispiel für den Linq-Ausdruck angeben, mit dem Sie den QueryProvider aufrufen? (Ich versuche, Ihren Code lokal zu rekonstruieren). Implementieren Sie auch die generische Version von Execute? 'public TResult Execute (Ausdruck System.Linq.Expressions.Expression) {...}' –
CodingWithSpike