我正在寻找有关如何使用LINQ以类型安全的方式实现此目的的帮助。
我需要在包含许多列的“性能”表上执行搜索。根据为搜索指定的条件,我需要选择列并对具有给定值的列执行搜索。
private static IQueryable<Investment> PerformanceSearch(IQueryable<Investment> investments, **??? searchColumn**, double minValue, double maxValue)
{
var entity = ExtendedEntities.Current;
investments = from inv in entity.Investments
join performance in entity.Performances on inv.InvestmentID equals perfromance.InvestmentID
where **performance.searchColumn** >= minValue && **performance.searchColumn** = maxValue
return investments;
}
现在我正在寻求你的帮助:
如何以类型安全的方式将列“searchColumn”传递给此方法?我正在考虑创建一个字典对象,以适应某种方式来维护实体框架中的列名。但不确定如何实现这一目标。
如何使用传递的columnName和应用where子句执行LINQ查询。
我无法使用If Else或Switch案例如下所示是可能的搜索列列表...
/*
* Search Columns can be:
* "Return1Month", "Return2Months", "Return3Months", ... almost 10 more and
* "Risk1Month", "Risk2Months", "Risk3Months", ... almost 10 more and
* "TrackingError1Month", "TrackingError2Months", "TrackingError3Months", ... almost 10 more and
* 2 more similar set of columns ...
*/
我花时间在Stackoverflow,微软和其他博客上,并考虑使用动态LINQ,但它不是类型安全的。它似乎可以使用表达式实现,但无法实现。
感谢任何建议。
编辑 -
需要提及的另一项 - 所有搜索列都出现在“效果”表中。
答案 0 :(得分:4)
请注意,LINQ表达式是以强类型方式动态构建LINQ查询的最佳方式。您放弃动态LINQ库是绝对正确的! LINQ表达式一开始很难掌握,但我向你保证,最终的回报是非常值得的。
这是一个使用LINQ表达式来完成你想要的东西的例子。您会注意到它不包含任何字符串列名,switch语句,帮助程序类或枚举。您需要导入System.Linq.Expressions
命名空间才能使其正常工作:
编辑:该示例现在包括按一个联接表上的列进行过滤,同时从另一个表中选择一个元素。我还从方法中删除了investments
参数,因为您实际上并不需要传入该参数。您只是直接在方法中访问EF表(我替换_performance
和{{ 1}})。
_investments
您可以使用这个简单的控制台应用程序以这种方式调用 public static IQueryable<Investment> PerformanceSearch(Expression<Func<Performance, double>> searchColumn, double minValue, double maxValue) {
// LINQ Expression that represents the column passed in searchColumn
// x.Return1Month
MemberExpression columnExpression = searchColumn.Body as MemberExpression;
// LINQ Expression to represent the parameter of the lambda you pass in
// x
ParameterExpression parameterExpression = (ParameterExpression)columnExpression.Expression;
// Expressions to represent min and max values
Expression minValueExpression = Expression.Constant(minValue);
Expression maxValueExpression = Expression.Constant(maxValue);
// Expressions to represent the boolean operators
// x.Return1Month >= minValue
Expression minComparisonExpression = Expression.GreaterThanOrEqual(columnExpression, minValueExpression);
// x.Return1Month <= maxValue
Expression maxComparisonExpression = Expression.LessThanOrEqual(columnExpression, maxValueExpression);
// (x.Return1Month >= minValue) && (x.Return1Month <= maxValue)
Expression filterExpression = Expression.AndAlso(minComparisonExpression, maxComparisonExpression);
// x => (x.Return1Month >= minValue) && (x.Return1Month <= maxValue)
Expression<Func<Performance, bool>> filterLambdaExpression = Expression.Lambda<Func<Performance, bool>>(filterExpression, parameterExpression);
// use the completed expression to filter your collection
// This requires that your collection is an IQueryable.
// I believe that EF tables are already IQueryable, so you can probably
// drop the .AsQueryable calls and it will still work fine.
var query = (from i in _investments
join p in _performance.AsQueryable().Where(filterLambdaExpression)
on i.InvestmentId equals p.InvestmentId
select i);
return query.AsQueryable();
}
:
PerformanceSearch
此示例非常通用,允许您将 private static IList<Investment> _investments;
private static IList<Performance> _performance;
static void Main(string[] args) {
// Simulate your two Entity Framework tables
BuildMockDataset();
// Return1Month is on Performance, but I return IQueryable<Investment>;
var results = PerformanceSearch(x => x.Return1Month, 300, 1000);
}
属性从double
传递为Performance
,将最小值和最大值指定为searchColumn
。
答案 1 :(得分:2)
我认为你应该只使用 Func&lt; TIn,TOut&gt; 参数(在这种情况下不需要的表达式)来做到这一点。无论列的类型如何,使函数通用都是类型安全的。这就是我在想的......
private static IQueryable<Investment> PerformanceSearch<TMember>(
IQueryable<Investment> investments,
Func<Performance,TMember> SearchColumn,
TMember minValue,
TMember maxValue)
{
var entity = ExtendedEntities.Current;
investments = from inv in entity.Investments
join perfromance in entity.Performances on inv.InvestmentID equals perfromance.InvestmentID
where SearchColumn(perfromance) >= minValue && SearchColumn(perfromance) <= maxValue
return investments;
}
然后你会这样调用它:
var results = PerformanceSearch<double>(investments, p => p.Return1Month, 10.0, 20.0);
答案 2 :(得分:1)
private static IQueryable<Investment> PerformanceSearch(IQueryable<Investment> investments, string searchColumn, double minValue, double maxValue)
{
var entity = ExtendedEntities.Current;
investments = from inv in entity.Investments
join perfromance in entity.Performances on inv.InvestmentID equals perfromance.InvestmentID
where
(
(searchColumn = "Return1Month" && perfromance.Return1Month >= minValue && perfromance.Return1Month <= maxValue) ||
(searchColumn = "Return2Months" && perfromance.Return2Months >= minValue && perfromance.Return2Months <= maxValue) ||
(searchColumn = "Return3Months" && perfromance.Return3Months >= minValue && perfromance.Return3Months <= maxValue) ||
(searchColumn = "Risk1Month" && perfromance.Risk1Month >= minValue && perfromance.Risk1Month <= maxValue)
// continue like this for as many columns, unless you want to use reflection
)
return investments;
}
另一个选项是我们用于动态报告系统的东西,即时代码生成和编译:
http://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx
答案 3 :(得分:1)
您可以构建一个包含强类型where子句的字典,如下所示:
var wheres = new Dictionary<string, Expression<Func<Performance, bool>>>()
{
{ "Return1Month", p => p.Return1Month >= minValue && p.Return1Month <= minValue },
{ "Return2Months", p => p.Return2Months >= minValue && p.Return2Months <= minValue },
{ "Return3Months", p => p.Return3Months >= minValue && p.Return3Months <= minValue },
{ "Risk1Month", p => p.Risk1Month >= minValue && p.Risk1Month <= minValue },
{ "TrackingError1Month", p => p.TrackingError1Month >= minValue && p.TrackingError1Month <= minValue },
/* etc */
};
完整的方法如下所示:
private static IQueryable<Investment> PerformanceSearch(IQueryable<Investment> investments, string searchColumn, double minValue, double maxValue)
{
var entity = ExtendedEntities.Current;
var wheres = new Dictionary<string, Expression<Func<Performance, bool>>>()
{
{ "Return1Month", p => p.Return1Month >= minValue && p.Return1Month <= minValue },
{ "Return2Months", p => p.Return2Months >= minValue && p.Return2Months <= minValue },
{ "Return3Months", p => p.Return3Months >= minValue && p.Return3Months <= minValue },
{ "Risk1Month", p => p.Risk1Month >= minValue && p.Risk1Month <= minValue },
{ "TrackingError1Month", p => p.TrackingError1Month >= minValue && p.TrackingError1Month <= minValue },
/* etc */
};
var investments = (
from inv in entity.Investments
join perfromance in entity.Performances.Where(wheres[searchColumn]) on inv.InvestmentID equals perfromance.InvestmentID
select inv;
return investments;
}
与实际的数据库调用相比,为每个调用构建字典非常快,所以不要过于担心它。如果您确实担心,请将字典设为静态私有字段。