我有一个引用数据库,其中包含天体网格上物体的坐标。我想查询数据库并找到“接近”(在特定角度距离内)给定点的对象。
我试过这个查询:
const double WithinOneMinute = 1.0 / 60.0; // 1 minute of arc
var db = CompositionRoot.GetTargetDatabase();
var targets = from item in db.Targets
where item.RightAscension.IsCloseTo(ra.Value, WithinOneMinute)
&& item.Declination.IsCloseTo(dec.Value, WithinOneMinute)
select item;
var found = targets.ToList();
失败是因为 LINQ 查询提供程序不理解我的 IsCloseTo
扩展方法,该方法实现为:
public static bool IsCloseTo(this double comparand, double comparison, double tolerance = EightDecimalPlaces)
{
var difference = Math.Abs(comparand - comparison);
return (difference <= tolerance);
}
所以我目前陷入了想法。有人做过这样的事情吗?
最佳答案
正如您已经注意到的,自定义函数不能用作查询表达式树的一部分。因此,您要么必须在查询中手动嵌入函数逻辑,从而引入大量代码重复,要么切换到方法语法并使用返回整个表达式的辅助方法。
后面可以使用System.Linq.Expressions
的方法手动完成,但这并不自然,需要大量知识。让我向您介绍一种\n 更简单的方法。
目标是实现这样的扩展方法
public static IQueryable<T> WhereIsCloseTo<T>(this IQueryable<T> source, Expression<Func<T, double>> comparand, double comparison, double tolerance = EightDecimalPlaces)
{
return source.Where(...);
}
并按如下方式使用
var targets = db.Targets
.WhereIsCloseTo(item => item.RightAscension, ra.Value, WithinOneMinute)
.WhereIsCloseTo(item => item.Declination, dec.Value, WithinOneMinute);
请注意,使用此方法您不能使用 &&
, 但链接 Where
产生相同的结果。
首先,让我们提供与原始函数等效的表达式
public static Expression<Func<double, bool>> IsCloseTo(double comparison, double tolerance = EightDecimalPlaces)
{
return comparand => Math.Abs(comparand - comparison) >= tolerance;
}
问题是不能在我们的方法中直接使用,因为它需要Expression<Func<T, bool>>
.
幸运的是,这可以通过使用我对 Define part of an Expression as a variable in c# 的回答中的一个小辅助实用程序轻松完成。 :
public static class ExpressionUtils
{
public static Expression<Func<TOuter, TResult>> Bind<TOuter, TInner, TResult>(this Expression<Func<TOuter, TInner>> source, Expression<Func<TInner, TResult>> resultSelector)
{
var body = new ParameterExpressionReplacer { source = resultSelector.Parameters[0], target = source.Body }.Visit(resultSelector.Body);
var lambda = Expression.Lambda<Func<TOuter, TResult>>(body, source.Parameters);
return lambda;
}
public static Expression<Func<TOuter, TResult>> ApplyTo<TInner, TResult, TOuter>(this Expression<Func<TInner, TResult>> source, Expression<Func<TOuter, TInner>> innerSelector)
{
return innerSelector.Bind(source);
}
class ParameterExpressionReplacer : ExpressionVisitor
{
public ParameterExpression source;
public Expression target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == source ? target : base.VisitParameter(node);
}
}
}
现在我们拥有了所需的一切,所以我们的方法实现起来很简单,如下所示:
public static IQueryable<T> WhereIsCloseTo<T>(this IQueryable<T> source, Expression<Func<T, double>> comparand, double comparison, double tolerance = EightDecimalPlaces)
{
return source.Where(IsCloseTo(comparison, tolerance).ApplyTo(comparand));
}
关于c# - 如何查询基于双列为 "close to"某个值的数据集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35430724/