LINQexpression式返回属性值?

我正在尝试创build一个通用函数来帮助我从本地列表中使用LINQ to SQL来select数千条logging。 SQL Server(2005年至less)限制查询2100参数,我想select更多的logging。

这将是一个很好的例子用法:

var some_product_numbers = new int[] { 1,2,3 ... 9999 }; Products.SelectByParameterList(some_product_numbers, p => p.ProductNumber); 

这是我的(非工作)实现:

 public static IEnumerable<T> SelectByParameterList<T, PropertyType>(Table<T> items, IEnumerable<PropertyType> parameterList, Expression<Func<T, PropertyType>> property) where T : class { var groups = parameterList .Select((Parameter, index) => new { GroupID = index / 2000, //2000 parameters per request Parameter } ) .GroupBy(x => x.GroupID) .AsEnumerable(); var results = groups .Select(g => new { Group = g, Parameters = g.Select(x => x.Parameter) } ) .SelectMany(g => /* THIS PART FAILS MISERABLY */ items.Where(item => g.Parameters.Contains(property.Compile()(item))) ); return results; } 

我见过很多使用expression式构build谓词的例子。 在这种情况下,我只想执行委托来返回当前ProductNumber的值。 或者说,我想将其转换成SQL查询(它在非通用forms工作正常)。

我知道,编译expression式只是让我回到了一个(作为Func传递委托),但我不确定如何将parameter passing给“未编译的”expression式。

谢谢你的帮助!

****编辑:**让我进一步澄清:

这是我想要概括的一个工作示例:

 var local_refill_ids = Refills.Select(r => r.Id).Take(20).ToArray(); var groups = local_refill_ids .Select((Parameter, index) => new { GroupID = index / 5, //5 parameters per request Parameter } ) .GroupBy(x => x.GroupID) .AsEnumerable(); var results = groups .Select(g => new { Group = g, Parameters = g.Select(x => x.Parameter) } ) .SelectMany(g => Refills.Where(r => g.Parameters.Contains(r.Id)) ) .ToArray() ; 

结果在这个SQL代码中:

 SELECT [t0].[Id], ... [t0].[Version] FROM [Refill] AS [t0] WHERE [t0].[Id] IN (@p0, @p1, @p2, @p3, @p4) ... That query 4 more times (20 / 5 = 4) 

我已经想出了一个方法来将查询分块 – 即你给它4000个值,所以它可以做4个请求1000个; 以完整的Northwind例子。 请注意,这可能不适用于entity framework,由于Expression.Invoke – 但在LINQ to SQL的罚款:

 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace ConsoleApplication5 { /// SAMPLE USAGE class Program { static void Main(string[] args) { // get some ids to play with... string[] ids; using(var ctx = new DataClasses1DataContext()) { ids = ctx.Customers.Select(x => x.CustomerID) .Take(100).ToArray(); } // now do our fun select - using a deliberately small // batch size to prove it... using (var ctx = new DataClasses1DataContext()) { ctx.Log = Console.Out; foreach(var cust in ctx.Customers .InRange(x => x.CustomerID, 5, ids)) { Console.WriteLine(cust.CompanyName); } } } } /// THIS IS THE INTERESTING BIT public static class QueryableChunked { public static IEnumerable<T> InRange<T, TValue>( this IQueryable<T> source, Expression<Func<T, TValue>> selector, int blockSize, IEnumerable<TValue> values) { MethodInfo method = null; foreach(MethodInfo tmp in typeof(Enumerable).GetMethods( BindingFlags.Public | BindingFlags.Static)) { if(tmp.Name == "Contains" && tmp.IsGenericMethodDefinition && tmp.GetParameters().Length == 2) { method = tmp.MakeGenericMethod(typeof (TValue)); break; } } if(method==null) throw new InvalidOperationException( "Unable to locate Contains"); foreach(TValue[] block in values.GetBlocks(blockSize)) { var row = Expression.Parameter(typeof (T), "row"); var member = Expression.Invoke(selector, row); var keys = Expression.Constant(block, typeof (TValue[])); var predicate = Expression.Call(method, keys, member); var lambda = Expression.Lambda<Func<T,bool>>( predicate, row); foreach(T record in source.Where(lambda)) { yield return record; } } } public static IEnumerable<T[]> GetBlocks<T>( this IEnumerable<T> source, int blockSize) { List<T> list = new List<T>(blockSize); foreach(T item in source) { list.Add(item); if(list.Count == blockSize) { yield return list.ToArray(); list.Clear(); } } if(list.Count > 0) { yield return list.ToArray(); } } } } 

最简单的方法:使用LINQKit (免费,非限制性许可证)

工作版本的代码:

 public static IEnumerable<T> SelectByParameterList<T, PropertyType>(this Table<T> items, IEnumerable<PropertyType> parameterList, Expression<Func<T, PropertyType>> propertySelector, int blockSize) where T : class { var groups = parameterList .Select((Parameter, index) => new { GroupID = index / blockSize, //# of parameters per request Parameter } ) .GroupBy(x => x.GroupID) .AsEnumerable(); var selector = LinqKit.Linq.Expr(propertySelector); var results = groups .Select(g => new { Group = g, Parameters = g.Select(x => x.Parameter) } ) .SelectMany(g => /* AsExpandable() extension method requires LinqKit DLL */ items.AsExpandable().Where(item => g.Parameters.Contains(selector.Invoke(item))) ); return results; } 

用法示例:

  Guid[] local_refill_ids = Refills.Select(r => r.Id).Take(20).ToArray(); IEnumerable<Refill> results = Refills.SelectByParameterList(local_refill_ids, r => r.Id, 10); //runs 2 SQL queries with 10 parameters each 

再次感谢你们的帮助!

LINQ到SQL仍然通过标准的SQL参数工作,所以写一个奇特的expression式不会帮助。 这里有三个常见的选项:

  • 将ID打包成(例如)csv / tsv; 作为varchar(max)传递下来,并使用udf将其(在服务器)拆分成表variables; 连接到表variables
  • 在SQL Server 2008中使用表值参数
  • 有一个服务器上的表,你可以推入ID(也许通过SqlBulkCopy)(也许与“会议指导”或类似的); join这张表

第一个是最简单的; 得到一个“分裂csv udf”是微不足道的(只是search它)。 将udf拖到数据上下文中并从那里消耗。

IQuerable传递给Contains函数而不是列表或数组。 请看下面的例子

 var df_handsets = db.DataFeed_Handsets.Where(m => m.LaunchDate != null). Select(m => m.Name); var Make = (from m in db.MobilePhones where (m.IsDeleted != true || m.IsDeleted == null) && df_handsets.Contains(m.Name) orderby m.Make select new { Value = m.Make, Text = m.Make }).Distinct(); 

当你传递列表或数组时,它以参数的forms传递,当列表项数大于2100时,它超过了计数。

你可以创build你自己的QueryProvider

 public class QueryProvider : IQueryProvider { // Translates LINQ query to SQL. private readonly Func<IQueryable, DbCommand> _translator; // Executes the translated SQL and retrieves results. private readonly Func<Type, string, object[], IEnumerable> _executor; public QueryProvider( Func<IQueryable, DbCommand> translator, Func<Type, string, object[], IEnumerable> executor) { this._translator = translator; this._executor = executor; } #region IQueryProvider Members public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { return new Queryable<TElement>(this, expression); } public IQueryable CreateQuery(Expression expression) { throw new NotImplementedException(); } public TResult Execute<TResult>(Expression expression) { bool isCollection = typeof(TResult).IsGenericType && typeof(TResult).GetGenericTypeDefinition() == typeof(IEnumerable<>); var itemType = isCollection // TResult is an IEnumerable`1 collection. ? typeof(TResult).GetGenericArguments().Single() // TResult is not an IEnumerable`1 collection, but a single item. : typeof(TResult); var queryable = Activator.CreateInstance( typeof(Queryable<>).MakeGenericType(itemType), this, expression) as IQueryable; IEnumerable queryResult; // Translates LINQ query to SQL. using (var command = this._translator(queryable)) { var parameters = command.Parameters.OfType<DbParameter>() .Select(parameter => parameter) .ToList(); var query = command.CommandText; var newParameters = GetNewParameterList(ref query, parameters); queryResult = _executor(itemType,query,newParameters); } return isCollection ? (TResult)queryResult // Returns an IEnumerable`1 collection. : queryResult.OfType<TResult>() .SingleOrDefault(); // Returns a single item. } public object Execute(Expression expression) { throw new NotImplementedException(); } #endregion private static object[] GetNewParameterList(ref string query, List<DbParameter> parameters) { var newParameters = new List<DbParameter>(parameters); foreach (var dbParameter in parameters.Where(p => p.DbType == System.Data.DbType.Int32)) { var name = dbParameter.ParameterName; var value = dbParameter.Value != null ? dbParameter.Value.ToString() : "NULL"; var pattern = String.Format("{0}[^0-9]", dbParameter.ParameterName); query = Regex.Replace(query, pattern, match => value + match.Value.Replace(name, "")); newParameters.Remove(dbParameter); } for (var i = 0; i < newParameters.Count; i++) { var parameter = newParameters[i]; var oldName = parameter.ParameterName; var pattern = String.Format("{0}[^0-9]", oldName); var newName = "@p" + i; query = Regex.Replace(query, pattern, match => newName + match.Value.Replace(oldName, "")); } return newParameters.Select(x => x.Value).ToArray(); } } static void Main(string[] args) { using (var dc=new DataContext()) { var provider = new QueryProvider(dc.GetCommand, dc.ExecuteQuery); var serviceIds = Enumerable.Range(1, 2200).ToArray(); var tasks = new Queryable<Task>(provider, dc.Tasks).Where(x => serviceIds.Contains(x.ServiceId) && x.CreatorId==37 && x.Creator.Name=="12312").ToArray(); } }