获取的财产,作为字符串,从防爆pression<&Func键LT;的TModel,TProperty>>

编程入门 行业动态 更新时间:2024-10-26 04:24:45
本文介绍了获取的财产,作为字符串,从防爆pression<&Func键LT;的TModel,TProperty>>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我使用一些强类型的前pressions是得到系列化,让我的用户界面code有强类型的排序和搜索前pressions。这些类型的防爆pression< Func键<的TModel,TProperty>> 和用作这样的: SortOption.Field =(P = > p.FirstName); 。我已经得到这个工作完全为这个简单的例子。

在code,我使用的解析名字财产离开那里实际上是重复使用的,我们使用第三方产品的一些现有的功能和它的伟大工程,直到我们开始deeply-工作嵌套属性( SortOption.Field =(p => p.Address.State.Abbreviation); )。这code有一些非常不同的假设在需要支持深层嵌套的属性。

至于这code做什么,我真的不明白它,而不是改变了code,我想我应该从头开始编写此功能。但是,我不知道一个的好的方式做到这一点。我想我们可以做的事情比做toString()和执行字符串解析更好。那么,这样做是为了处理琐碎和深度嵌套的情况下,一个好办法?

要求:

  • 鉴于前pression P => p.FirstName 我需要名字的字符串。
  • 鉴于前pression P => p.Address.State.Abbreviation 我需要的字符串Address.State.Abbreviation

虽然它不是一个回答我的问题很重要,我怀疑我的序列化/反序列化code可能是别人谁发现在未来的这个问题很有用,所以下面是。同样,这code是没有问题的重要 - 我只是想这可能帮助别人。需要注意的是 DynamicEx pression.ParseLambda 来自Dynamic LINQ 东西, Property.PropertyToString()是这个问题是关于什么的。

///<总结>///这定义了一个框架,通过跨越序列层,排序逻辑被执行。///< /总结>///< typeparam名=的TModel>这是您要筛选对象的类型< / typeparam>///< typeparam NAME =TProperty>这是LT您过滤对象的属性&; / typeparam>[Serializable接口]公共类SortOption<的TModel,TProperty> :ISerializable的,其中的TModel:类{    ///<总结>    ///便捷构造。    ///< /总结>    ///< PARAM NAME =财产方式>排序酒店< /参数>    ///< PARAM NAME =isAscending>表示如果排序应升序或降序< /参数>    ///&所述; PARAM NAME =优先权>表示的排序优先级,其中0是大于10℃的更高的优先级; /参数>    公共SortOption(前pression<&Func键LT;的TModel,TProperty>>物业,布尔isAscending = TRUE,INT优先级= 0)    {        属性=财产;        IsAscending = isAscending;        优先级=优先权;    }    ///<总结>    ///默认构造函数。    ///< /总结>    公共SortOption()        :这个(空)    {    }    ///<总结>    ///这是在物体上的字段筛选。    ///< /总结>    公共防爆pression<&Func键LT;的TModel,TProperty>>物业{搞定;组; }    ///<总结>    ///这表明,如果排序应升序或降序。    ///< /总结>    公共BOOL IsAscending {搞定;组; }    ///<总结>    ///这表示排序优先级,其中0是比10更高的优先级。    ///< /总结>    公众诠释优先{搞定;组; }    ISerializable的的#区域实施    ///<总结>    ///这是反序列化SortOption时调用的构造函数。    ///< /总结>    保护SortOption(的SerializationInfo信息,的StreamingContext上下文)    {        IsAscending = info.GetBoolean(IsAscending);        优先= info.GetInt32(优先级);        //我们只是在属性名坚持这一点。因此,让我们重新从拉姆达防爆pression。        属性= DynamicEx pression.ParseLambda<的TModel,TProperty>(info.GetString(「该物业」),默认(的TModel),默认(TProperty));    }    ///<总结>    ///填充一个<见CREF =T:System.Runtime.Serialization.SerializationInfo/>用序列化目标对象所需的数据。    ///< /总结>    ///< PARAM NAME =信息>的<见CREF =T:System.Runtime.Serialization.SerializationInfo/>来填充数据。 < /参数>    ///< PARAM NAME =背景>在目的地(参见LT;见CREF =T:System.Runtime.Serialization.StreamingContext/&)对于此序列化。 < /参数>    公共无效GetObjectData使用(的SerializationInfo信息,的StreamingContext上下文)    {        //在那里只要坚持属性名称。我们将基于在另一端重建前pression。        info.AddValue(属性,Property.PropertyToString());        info.AddValue(IsAscending,IsAscending);        info.AddValue(优先级,优先);    }    #endregion}

解决方案

下面的诀窍:这种形式的任何前pression ...

的obj => obj.A.B.C //等。

...实际上只是一堆嵌套的 MemberEx pression 的对象。

首先你得:

MemberEx pression:obj.A.B.C防爆pression:// obj.A.B MemberEx pression会员:C

评估防爆pression 上面的为 MemberEx pression 的为您提供:

MemberEx pression:obj.A.B防爆pression:// obj.A MemberEx pression会员:乙

最后,上面的是的(在顶),你有:

MemberEx pression:obj.A防爆pression:OBJ //注意:不是MemberEx pression会员:一

因此​​,似乎很清楚,要解决这个问题的方法是通过检查的在防爆pression 属性 MemberEx pression ,直到它不再是本身的点 MemberEx pression 。

更新:似乎有对你的问题的补充旋。这可能是因为你有一些拉姆达的看起来的像 Func键< T,INT> ...

P =>页

...但的实际的一个 Func键< T,对象> ;在这种情况下,编译器将上述前pression转换为:

P =>转换(p.Age)

调整为这个问题其实并不一样坚韧,因为它看起来。看看我的更新code为对付它的方法之一。请注意,通过抽象code为得到一个 MemberEx pression 掳到其自己的方法( TryFindMemberEx pression ),这种方法保持了 GetFullPropertyName 方法还算干净,并允许您在将来添加额外的检查 - 如果,也许,你会发现自己面临着的新的的,你没有原先占的场景 - 而不必通过太多code涉水

要说明:此code为我工作

// code调整为prevent水平溢出静态字符串GetFullPropertyName< T,TProperty>(防爆pression<&Func键LT; T,TProperty>> EXP){    MemberEx pression memberExp;    如果(!TryFindMemberEx pression(exp.Body,出memberExp))        返回的String.Empty;    VAR memberNames =新的堆栈<串GT;();    做    {        memberNames.Push(memberExp.Member.Name);    }    而(TryFindMemberEx pression(memberExp.Ex pression,出memberExp));    返回的string.join(,memberNames.ToArray()。);}// code调整为prevent水平溢出私人静态布尔TryFindMemberEx pression(防爆pression EXP,出MemberEx pression memberExp){    memberExp = EXP作为MemberEx pression;    如果(memberExp!= NULL)    {        // heyo!这是很容易做到        返回true;    }    //如果编译器创建的自动转换,    //它会是这个样子?    // OBJ =>转换(obj.Property)[例如,INT - >目的]    // 要么:    // OBJ => ConvertChecked(obj.Property)[例如,INT - >长]    // ...这是在IsConversion检查情况    如果(IsConversion(实验)及&放大器; exp为UnaryEx pression)    {        memberExp =((UnaryEx pression)EXP).Operand作为MemberEx pression;        如果(memberExp!= NULL)        {            返回true;        }    }    返回false;}私人静态布尔IsConversion(前pression EXP){    返回(        exp.NodeType ==防爆pressionType.Convert ||        exp.NodeType ==防爆pressionType.ConvertChecked    );}

用法:

防爆pression<&Func键LT;人,串GT;> simpleExp = P => p.FirstName;防爆pression<&Func键LT;人,串GT;> complexExp = P => p.Address.State.Abbreviation;防爆pression<&Func键LT;人,对象>> ageExp = P =>页;Console.WriteLine(GetFullPropertyName(simpleExp));Console.WriteLine(GetFullPropertyName(complexExp));Console.WriteLine(GetFullPropertyName(ageExp));

输出:

姓Address.State.Abbreviation年龄

I use some strongly-typed expressions that get serialized to allow my UI code to have strongly-typed sorting and searching expressions. These are of type Expression<Func<TModel,TProperty>> and are used as such: SortOption.Field = (p => p.FirstName);. I've gotten this working perfectly for this simple case.

The code that I'm using for parsing the "FirstName" property out of there is actually reusing some existing functionality in a third-party product that we use and it works great, until we start working with deeply-nested properties(SortOption.Field = (p => p.Address.State.Abbreviation);). This code has some very different assumptions in the need to support deeply-nested properties.

As for what this code does, I don't really understand it and rather than changing that code, I figured I should just write from scratch this functionality. However, I don't know of a good way to do this. I suspect we can do something better than doing a ToString() and performing string parsing. So what's a good way to do this to handle the trivial and deeply-nested cases?

Requirements:

  • Given the expression p => p.FirstName I need a string of "FirstName".
  • Given the expression p => p.Address.State.Abbreviation I need a string of "Address.State.Abbreviation"

While it's not important for an answer to my question, I suspect my serialization/deserialization code could be useful to somebody else who finds this question in the future, so it is below. Again, this code is not important to the question - I just thought it might help somebody. Note that DynamicExpression.ParseLambda comes from the Dynamic LINQ stuff and Property.PropertyToString() is what this question is about.

/// <summary> /// This defines a framework to pass, across serialized tiers, sorting logic to be performed. /// </summary> /// <typeparam name="TModel">This is the object type that you are filtering.</typeparam> /// <typeparam name="TProperty">This is the property on the object that you are filtering.</typeparam> [Serializable] public class SortOption<TModel, TProperty> : ISerializable where TModel : class { /// <summary> /// Convenience constructor. /// </summary> /// <param name="property">The property to sort.</param> /// <param name="isAscending">Indicates if the sorting should be ascending or descending</param> /// <param name="priority">Indicates the sorting priority where 0 is a higher priority than 10.</param> public SortOption(Expression<Func<TModel, TProperty>> property, bool isAscending = true, int priority = 0) { Property = property; IsAscending = isAscending; Priority = priority; } /// <summary> /// Default Constructor. /// </summary> public SortOption() : this(null) { } /// <summary> /// This is the field on the object to filter. /// </summary> public Expression<Func<TModel, TProperty>> Property { get; set; } /// <summary> /// This indicates if the sorting should be ascending or descending. /// </summary> public bool IsAscending { get; set; } /// <summary> /// This indicates the sorting priority where 0 is a higher priority than 10. /// </summary> public int Priority { get; set; } #region Implementation of ISerializable /// <summary> /// This is the constructor called when deserializing a SortOption. /// </summary> protected SortOption(SerializationInfo info, StreamingContext context) { IsAscending = info.GetBoolean("IsAscending"); Priority = info.GetInt32("Priority"); // We just persisted this by the PropertyName. So let's rebuild the Lambda Expression from that. Property = DynamicExpression.ParseLambda<TModel, TProperty>(info.GetString("Property"), default(TModel), default(TProperty)); } /// <summary> /// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object. /// </summary> /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data. </param> /// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization. </param> public void GetObjectData(SerializationInfo info, StreamingContext context) { // Just stick the property name in there. We'll rebuild the expression based on that on the other end. info.AddValue("Property", Property.PropertyToString()); info.AddValue("IsAscending", IsAscending); info.AddValue("Priority", Priority); } #endregion }

解决方案

Here's the trick: any expression of this form...

obj => obj.A.B.C // etc.

...is really just a bunch of nested MemberExpression objects.

First you've got:

MemberExpression: obj.A.B.C Expression: obj.A.B // MemberExpression Member: C

Evaluating Expression above as a MemberExpression gives you:

MemberExpression: obj.A.B Expression: obj.A // MemberExpression Member: B

Finally, above that (at the "top") you have:

MemberExpression: obj.A Expression: obj // note: not a MemberExpression Member: A

So it seems clear that the way to approach this problem is by checking the Expression property of a MemberExpression up until the point where it is no longer itself a MemberExpression.

UPDATE: It seems there is an added spin on your problem. It may be that you have some lambda that looks like a Func<T, int>...

p => p.Age

...but is actually a Func<T, object>; in this case, the compiler will convert the above expression to:

p => Convert(p.Age)

Adjusting for this issue actually isn't as tough as it might seem. Take a look at my updated code for one way to deal with it. Notice that by abstracting the code for getting a MemberExpression away into its own method (TryFindMemberExpression), this approach keeps the GetFullPropertyName method fairly clean and allows you to add additional checks in the future -- if, perhaps, you find yourself facing a new scenario which you hadn't originally accounted for -- without having to wade through too much code.

To illustrate: this code worked for me.

// code adjusted to prevent horizontal overflow static string GetFullPropertyName<T, TProperty> (Expression<Func<T, TProperty>> exp) { MemberExpression memberExp; if (!TryFindMemberExpression(exp.Body, out memberExp)) return string.Empty; var memberNames = new Stack<string>(); do { memberNames.Push(memberExp.Member.Name); } while (TryFindMemberExpression(memberExp.Expression, out memberExp)); return string.Join(".", memberNames.ToArray()); } // code adjusted to prevent horizontal overflow private static bool TryFindMemberExpression (Expression exp, out MemberExpression memberExp) { memberExp = exp as MemberExpression; if (memberExp != null) { // heyo! that was easy enough return true; } // if the compiler created an automatic conversion, // it'll look something like... // obj => Convert(obj.Property) [e.g., int -> object] // OR: // obj => ConvertChecked(obj.Property) [e.g., int -> long] // ...which are the cases checked in IsConversion if (IsConversion(exp) && exp is UnaryExpression) { memberExp = ((UnaryExpression)exp).Operand as MemberExpression; if (memberExp != null) { return true; } } return false; } private static bool IsConversion(Expression exp) { return ( exp.NodeType == ExpressionType.Convert || exp.NodeType == ExpressionType.ConvertChecked ); }

Usage:

Expression<Func<Person, string>> simpleExp = p => p.FirstName; Expression<Func<Person, string>> complexExp = p => p.Address.State.Abbreviation; Expression<Func<Person, object>> ageExp = p => p.Age; Console.WriteLine(GetFullPropertyName(simpleExp)); Console.WriteLine(GetFullPropertyName(complexExp)); Console.WriteLine(GetFullPropertyName(ageExp));

Output:

FirstName Address.State.Abbreviation Age

更多推荐

获取的财产,作为字符串,从防爆pression&LT;&Func键LT;的TModel,TProperty&GT;&GT;

本文发布于:2023-11-10 00:36:22,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1573878.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:字符串   财产   pression   LT   GT

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!