c# – LINQ Expression Tree Any()里面的Where()

c# – LINQ Expression Tree Any()里面的Where(),第1张

概述我正在尝试生成以下LINQ查询: //Query the database for all AdAccountAlerts that haven't had notifications sent out//Then get the entity (AdAccount) the alert pertains to, and find all accounts that//are subscrib 我正在尝试生成以下liNQ查询:
//query the database for all AdAccountAlerts that haven't had notifications sent out//Then get the entity (AdAccount) the alert pertains to,and find all accounts that//are subscribing to alerts on that entity.var x = dataContext.Alerts.Where(a => a.NotificationsSent == null)  .OfType<AdAccountAlert>()  .ToList()  .GroupJoin(dataContext.AlertSubscriptions,a => new Tuple<int,string>(a.AdAccountID,typeof(AdAccount).name),s => new Tuple<int,string>(s.EntityID,s.EntityType),(Alert,Subscribers) => new Tuple<AdAccountAlert,IEnumerable<AlertSubscription>> (Alert,Subscribers))  .Where(s => s.Item2.Any())  .ToDictionary(kvp => (Alert)kvp.Item1,kvp => kvp.Item2.Select(s => s.Username));

使用表达式树(当我需要使用反射和运行时类型时,这似乎是我能做到这一点的唯一方法).请注意,在实际代码中(见下文),AdAccountAlert实际上是通过反射和for循环动态的.

我的问题:我可以生成.Where()子句的所有内容.由于类型不兼容,whereExpression方法调用会爆炸.通常我知道要放在那里,但Any()方法调用让我感到困惑.我尝试了所有我能想到的类型而且没有运气.任何有关.Where()和.ToDictionary()的帮助都将受到赞赏.

这是我到目前为止所拥有的:

var alertTypes = AppDomain.CurrentDomain.GetAssemblIEs()  .Single(a => a.Fullname.StartsWith("Alerts.EntitIEs"))  .GetTypes()  .Where(t => typeof(Alert).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface);var alertSubscribers = new Dictionary<Alert,IEnumerable<string>>();//Using tuples for joins to keep everything strongly-typedvar subscribableType = typeof(Tuple<int,string>);var doubleTuple = Type.GetType("System.Tuple`2,mscorlib",true);foreach (var alertType in alertTypes){  Type foreignKeyType = GetForeignKeyType(alertType);  if (foreignKeyType == null)    continue;  Iqueryable<Alert> unnotifIEDalerts = dataContext.Alerts.Where(a => a.NotificationsSent == null);  //Generates: .OfType<alertType>()  MethodCallExpression alertsOfType = Expression.Call(typeof(Enumerable).getmethod("OfType").MakeGenericmethod(alertType),unnotifIEDalerts.Expression);  //Generates: .ToList(),which is required for joins on Tuples  MethodCallExpression unnotifIEDalertsList = Expression.Call(typeof(Enumerable).getmethod("ToList").MakeGenericmethod(alertType),alertsOfType);  //Generates: a => new { a.{EntityID},EntityType = typeof(AdAccount).name }  ParameterExpression alertParameter = Expression.Parameter(alertType,"a");  MemberExpression adAccountID = Expression.Property(alertParameter,alertType.GetProperty(alertType.GetForeignKeyID()));  NewExpression outerJoinObject = Expression.New(subscribableType.GetConstructor(new Type[] { typeof(int),typeof(string)}),adAccountID,Expression.Constant(foreignKeyType.name));  LambdaExpression outerSelector = Expression.Lambda(outerJoinObject,alertParameter);  //Generates: s => new { s.EntityID,s.EntityType }  Type alertSubscriptionType = typeof(AlertSubscription);  ParameterExpression subscriptionParameter = Expression.Parameter(alertSubscriptionType,"s");  MemberExpression entityID = Expression.Property(subscriptionParameter,alertSubscriptionType.GetProperty("EntityID"));  MemberExpression entityType = Expression.Property(subscriptionParameter,alertSubscriptionType.GetProperty("EntityType"));  NewExpression innerJoinObject = Expression.New(subscribableType.GetConstructor(new Type[] { typeof(int),typeof(string) }),entityID,entityType);  LambdaExpression innerSelector = Expression.Lambda(innerJoinObject,subscriptionParameter);  //Generates: (Alert,Subscribers) => new Tuple<Alert,IEnumerable<AlertSubscription>>(Alert,Subscribers)  var joinResultType = doubleTuple.MakeGenericType(new Type[] { alertType,typeof(IEnumerable<AlertSubscription>) });  ParameterExpression alertTupleParameter = Expression.Parameter(alertType,"Alert");  ParameterExpression subscribersTupleParameter = Expression.Parameter(typeof(IEnumerable<AlertSubscription>),"Subscribers");  NewExpression joinResultObject = Expression.New(    joinResultType.GetConstructor(new Type[] { alertType,typeof(IEnumerable<AlertSubscription>) }),alertTupleParameter,subscribersTupleParameter);  LambdaExpression resultsSelector = Expression.Lambda(joinResultObject,subscribersTupleParameter);  //Generates:  //  .GroupJoin(dataContext.AlertSubscriptions,//    a => new { a.AdAccountID,typeof(AdAccount).name },//    s => new { s.EntityID,s.EntityType },//    (Alert,Subscribers))  Iqueryable<AlertSubscription> alertSubscriptions = dataContext.AlertSubscriptions.Asqueryable();  MethodCallExpression joinExpression = Expression.Call(typeof(Enumerable),"GroupJoin",new Type[]    {      alertType,alertSubscriptions.ElementType,outerSelector.Body.Type,resultsSelector.ReturnType    },unnotifIEDalertsList,alertSubscriptions.Expression,outerSelector,innerSelector,resultsSelector);  //Generates: .Where(s => s.Item2.Any())  ParameterExpression subscribersParameter = Expression.Parameter(resultsSelector.ReturnType,"s");  MemberExpression tupleSubscribers = Expression.Property(subscribersParameter,resultsSelector.ReturnType.GetProperty("Item2"));  MethodCallExpression hasSubscribers = Expression.Call(typeof(Enumerable),"Any",new Type[] { alertSubscriptions.ElementType },tupleSubscribers);  LambdaExpression whereLambda = Expression.Lambda(hasSubscribers,subscriptionParameter);  MethodCallExpression whereExpression = Expression.Call(typeof(Enumerable),"Where",new Type[] { joinResultType },joinExpression,whereLambda);
解决方法 请注意:ToList()之后的所有内容都不适用于Iqueryable< T>但是在IEnumerable< T>上.因此,不需要创建表达式树.它当然不是EF或类似的解释.

如果您查看编译器为原始查询生成的代码,您会看到它仅在第一次调用ToList之前生成表达式树.

例:

以下代码:

var query = new List<int>().Asqueryable();query.Where(x => x > 0).ToList().FirstOrDefault(x => x > 10);

由编译器翻译为:

Iqueryable<int> query = new List<int>().Asqueryable<int>();Iqueryable<int> arg_4D_0 = query;ParameterExpression parameterExpression = Expression.Parameter(typeof(int),"x");arg_4D_0.Where(Expression.Lambda<Func<int,bool>>(Expression.GreaterThan(parameterExpression,Expression.Constant(0,typeof(int))),new ParameterExpression[]{    parameterExpression})).ToList<int>().FirstOrDefault((int x) => x > 10);

请注意它如何为ToList之前的所有内容生成表达式.包含它之后的所有内容都只是对扩展方法的正常调用.

如果你没有在你的代码中模仿这个,你实际上会将一个Enumerable.ToList调用发送到liNQ提供程序 – 然后它会尝试转换为sql并失败.

总结

以上是内存溢出为你收集整理的c# – LINQ Expression Tree Any()里面的Where()全部内容,希望文章能够帮你解决c# – LINQ Expression Tree Any()里面的Where()所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1243095.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-06
下一篇 2022-06-06

发表评论

登录后才能评论

评论列表(0条)

保存