Wie kann man ein update Linq-Ausdruck mit zusätzlichen Parametern?
Ich habe ein Linq-Ausdruck, die verändert werden können, abhängig von bestimmten Bedingungen. Ein Beispiel, was ich gerne tun würde (leer ist das bit ich bin nicht sicher):
Expression<Func<Project, bool>> filter = (Project p) => p.UserName == "Bob";
if(showArchived)
{
//update filter to add && p.Archived
}
//query the database when the filter is built
IEnumerable<Project> projects = unitOfWork.ProjectRepository.Get(filter);
Wie aktualisiere ich die filter-zusätzliche Parameter?
Im moment werden alle Datensätze abgerufen, dann benutze ich ein Where
zur weiteren Filterung der Ergebnisse. Jedoch, dass die Ergebnisse in mehr Abfragen in der Datenbank als unbedingt erforderlich sind.
IEnumerable<Project> projects = unitOfWork.ProjectRepository.Get(filter);
if(showArchived)
{
projects = projects.Where(p => p.Archived);
}
Get-Methode nutzt die GenericRepository Muster:
public class GenericRepository<TEntity> where TEntity : class
{
internal ProgrammeDBContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(ProgrammeDBContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
{
return dbSet.SqlQuery(query, parameters).ToList();
}
}
Update
Einige extension-Methoden, basierend auf der code unten von Marc Gravell und David B, löst das problem für mich
public static class LinqExtensionMethods
{
public static Expression<Func<T, bool>> CombineOr<T>(params Expression<Func<T, bool>>[] filters)
{
return filters.CombineOr();
}
public static Expression<Func<T, bool>> CombineOr<T>(this IEnumerable<Expression<Func<T, bool>>> filters)
{
if (!filters.Any())
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
Expression<Func<T, bool>> firstFilter = filters.First();
var lastFilter = firstFilter;
Expression<Func<T, bool>> result = null;
foreach (var nextFilter in filters.Skip(1))
{
var nextExpression = new ReplaceVisitor(lastFilter.Parameters[0], nextFilter.Parameters[0]).Visit(lastFilter.Body);
result = Expression.Lambda<Func<T, bool>>(Expression.OrElse(nextExpression, nextFilter.Body), nextFilter.Parameters);
lastFilter = nextFilter;
}
return result;
}
public static Expression<Func<T, bool>> CombineAnd<T>(params Expression<Func<T, bool>>[] filters)
{
return filters.CombineAnd();
}
public static Expression<Func<T, bool>> CombineAnd<T>(this IEnumerable<Expression<Func<T, bool>>> filters)
{
if (!filters.Any())
{
Expression<Func<T, bool>> alwaysTrue = x => true;
return alwaysTrue;
}
Expression<Func<T, bool>> firstFilter = filters.First();
var lastFilter = firstFilter;
Expression<Func<T, bool>> result = null;
foreach (var nextFilter in filters.Skip(1))
{
var nextExpression = new ReplaceVisitor(lastFilter.Parameters[0], nextFilter.Parameters[0]).Visit(lastFilter.Body);
result = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(nextExpression, nextFilter.Body), nextFilter.Parameters);
lastFilter = nextFilter;
}
return result;
}
class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
}
- Was ist der return-Typ und interlans von
ProjectRepository.Get(filter);
? - Was ist
showAchieved
? Tut es zähltprojects
variable? - showArchived ist nur ein boolean
- Jetzt haben Sie Hinzugefügt Bekommen, die ToList() ist ein Schmerz; ich habe einen Ausdruck Hinzugefügt-rewriting Beispiel, dass Sie sollten in der Lage sein zu verwenden, um die beiden zu verbinden-Filter, bevor - Aufruf von Get
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn ich die Frage verstehen, dann am ehesten hier ist das problem:
Jede Arbeit auf
projects
wird mitEnumerable
, nichtQueryable
; sollte es wohl auch sein:Letzteres ist composable, und
.Where
sollte so funktionieren, wie Sie erwarten, den Aufbau einer restriktiven Abfrage vor senden es an den server.Ihre andere option ist, zu schreiben, die filter zu kombinieren, bevor Sie Sie versenden:
Oder neu geschrieben, so dass eine bequeme Nutzung:
Enumerable.Where
undQueryable.Where
Angelegenheiten sehr viel - können Sie klären, was Sie sagen, ist egal?IEnumberable<T>
undIQueryable<T>
. Aber der AufrufGet
sollte wohl nicht auf die Datenbank überhaupt, wenn es wieder eine Abfrage, können Sie zeigen, wieGet
geschrieben?Get
Methode hier, gibt es ein bestimmtes Objekt.IEnumerable<T>
oderIQuerible<T>
hier sind nur Referenzen, zeigen Sie auf das Objekt. Das konkrete Objekt wird Verhalten aufWhere
wie er es tun sollte. Wenn es querible es wird generiert prologed Ausdruck Baum in jedem Fall.IEnumerable<T>
(auch wenn es eigentlich etwas, dieIQueryable<T>
), dann wird es mitEnumerable.Where
, was ist LINQ-to-Objects. Es wird nicht "verfassen" in der Abfrage. Sorry, aber was Sie sagen ist falsch.filter = filter.Combine(x => x.B > 2.5);
etc - also muss ein return-Wert, den Sie fangen. Und man könnte es nennen wollenAndAlso
oder soAndAlso
im Ausdruck selbst (Bearbeiten dass)Ich denke, Sie wollen Filter kombinieren auf diese Weise:
Dieser code erlaubt Ihnen, kombinieren Sie Ihre Filter.
All das hängt davon ab wie funktioniert
ProjectRepository.Get()
sich Verhalten und was es gibt. Den üblichen Weg (zum Beispiel, LINQ to SQL macht etwas in dieser Art) ist, dass es gibt eineIQueryable<T>
und können Sie (unter anderem) hinzufügen mehrWhere()
Klauseln vor dem senden an den server in form von einer SQL-Abfrage, mit der alleWhere()
Klauseln enthalten. Wenn dies der Fall ist, Markieren Sie die Lösung (verwenden SieIQuerybale<T>
) wird für Sie arbeiten.Aber wenn die
Get()
Methode führt die Abfrage auf der Grundlage derfilter
sofort, du musst es den ganzen filter in den Ausdruck. Zu tun, dass Sie verwenden können,PredicateBuilder
.Wenn Sie
Get
Methode retrives die Daten und kehrt in den Speicher Objekte, die Sie tun konnte, soBEARBEITEN
Nur darauf hinweisen. Wenn Sie
.ToList()
Methode zählt dieQueryable
, macht also eine Datenbank-Anfrage.Loszuwerden
ToList()
und Sie werden just fine.