|
1 | | -# Newbe.ObjectVisitor |
| 1 | +# Newbe.ObjectVisitor |
| 2 | + |
| 3 | +You can visit all properties about your class or struct by this lib with high performance as you visit properties in hard coding way. |
| 4 | + |
| 5 | +For example, here is object in your code. |
| 6 | + |
| 7 | +```cs |
| 8 | +var order = new OrderInfo(); |
| 9 | +``` |
| 10 | + |
| 11 | +And, you want to print all properties of the order. |
| 12 | + |
| 13 | +```cs |
| 14 | +for(var pInfo in typeof(OrderInfo).GetProperties()) |
| 15 | +{ |
| 16 | + Console.Writeline($"{pInfo.Name}: {pInfo.GetValue(order)}"); |
| 17 | +} |
| 18 | +``` |
| 19 | + |
| 20 | +By using this lib, you can handle it in this way: |
| 21 | + |
| 22 | +```cs |
| 23 | +// call .V what is a static extension method |
| 24 | +// you get a visitor object for order |
| 25 | +var visitor = order.V(); |
| 26 | + |
| 27 | +visitor.ForEach(context=>{ |
| 28 | + var name = context.Name; |
| 29 | + var value = context.Value; |
| 30 | + Console.Writeline($"{name}: {value}"); |
| 31 | +}).Run(); |
| 32 | + |
| 33 | +// you can also make it in one line |
| 34 | +order.V().ForEach(c=> Console.Writeline($"{c.Name}: {c.Value}")).Run(); |
| 35 | +``` |
| 36 | + |
| 37 | +"Why I need this?" |
| 38 | + |
| 39 | +- It is faster. This lib impletment with [Expression Trees](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/) that cost 1/10 time as in Reflection way. |
| 40 | +- It is readable. This lib can generate a lambda func to handle the code flow you create that just as your hard coding without reflection. |
| 41 | +- It is extendable. If you can visit all properties of a object in easy way, you can validate them as you wish, mask some value if there are something sensitive, |
| 42 | + |
| 43 | +## Status |
| 44 | + |
| 45 | +Planning. This project is in planning. All you can see below is in the plan. |
| 46 | + |
| 47 | +## API (plan) |
| 48 | + |
| 49 | +```cs |
| 50 | +var o = new Yueluo(); |
| 51 | + |
| 52 | +using Newbe.ObjectVisitor; |
| 53 | + |
| 54 | +// V is a static extension method |
| 55 | +var visitor = o.V(); |
| 56 | + |
| 57 | +// create visitor from factory method |
| 58 | +var visitor = ObjectVisitorFactory.Create(typeof(Yueluo)); |
| 59 | + |
| 60 | +// this is the most simple structure about this lib |
| 61 | +// there are Name, Value, PropertyInfo, SourceObj, SourceObjType and etc in the context |
| 62 | +o.V().ForEach((context)=>{}).Run(); |
| 63 | + |
| 64 | +// you can modify value if return a new value |
| 65 | +o.V().ForEach((context)=>context.Value.SubString(0,1)).Run(); |
| 66 | + |
| 67 | +// get debug info about expression now |
| 68 | +var debugInfo = o.V().ForEach((context)=>{}).GetDebugInfo(); |
| 69 | + |
| 70 | +// generate code in C# as a string about expression now |
| 71 | +var code = o.V().ForEach((context)=>{}).GenerateCode(); |
| 72 | + |
| 73 | +// generate a lambda func |
| 74 | +var func = o.V().ForEach((context)=>{}).GetLambda(); |
| 75 | + |
| 76 | +// foreach properties with specified type |
| 77 | +o.V().ForEach<string>((context)=>{}).Run(); |
| 78 | + |
| 79 | +// using linq to filter |
| 80 | +o.V().AsEnumerable().Where((context)=>context.Name == "YueLuo").ForEach((context)=>{}).Run(); |
| 81 | + |
| 82 | +// suppending visiting sub object |
| 83 | +o.V().SuppendSubObject().ForEach((context)=>{}).Run(); |
| 84 | + |
| 85 | +// suppending visiting enumerable object |
| 86 | +o.V().SuppendEnumerable().ForEach((context)=>{}).Run(); |
| 87 | + |
| 88 | + |
| 89 | +/** |
| 90 | + sample to join all properties to string |
| 91 | +*/ |
| 92 | +var sb = new StringBuilder(); |
| 93 | +o.V().ForEach((context)=>{ |
| 94 | + sb.Append(context.Name); |
| 95 | + sb.Append(context.Value); |
| 96 | + sb.Append(Enviroment.Newline); |
| 97 | +}).Run(); |
| 98 | +var s = sb.ToString(); |
| 99 | + |
| 100 | +// quick style for above |
| 101 | +var s = o.V().FormatString(); |
| 102 | + |
| 103 | +// Deconstruct as C# 7 but more flexible |
| 104 | +var destructor1 = Destructor<Yueluo> |
| 105 | + .Property(x=>x.Name) |
| 106 | + .Property(x=>x.Age) |
| 107 | + |
| 108 | +var destructor2 = Destructor<Yueluo> |
| 109 | + .Property(x=>x.Name) |
| 110 | + .Property(x=>(long)x.Age) |
| 111 | + |
| 112 | +var destructor3 = Destructor<Yueluo> |
| 113 | + .Property(x=>x.Name) |
| 114 | + .Property(x=>x.NickName) |
| 115 | + .Property(x=>x.Age) |
| 116 | + |
| 117 | +var (name, age) = o.V().Destruct(destructor1).Run(); |
| 118 | +var (name, ageInLong) = o.V().Destruct(destructor2).Run(); |
| 119 | +var (name, nickName, age) = o.V().Destruct(destructor3).Run(); |
| 120 | + |
| 121 | +// namespace for operation with collections |
| 122 | +using Newbe.ObjectVisitor.Collections; |
| 123 | + |
| 124 | +/** |
| 125 | + collect properties into a dictionary |
| 126 | +*/ |
| 127 | + |
| 128 | +var dic1 = o.V().CollectAsDictionary().Run(); |
| 129 | +// quick style for above |
| 130 | +var dic1 = o.V().ToDictionary(); |
| 131 | + |
| 132 | +/** |
| 133 | + apply value from a dictionary to object |
| 134 | +*/ |
| 135 | +o.V().ApplyFromDictionary(dic).Run(); |
| 136 | +// quick style for above |
| 137 | +o.V().FromDictionary(dic); |
| 138 | + |
| 139 | + |
| 140 | +// namespace for data validation |
| 141 | +using Newbe.ObjectVisitor.Validation; |
| 142 | + |
| 143 | +// create rule to validation |
| 144 | +var rule = ValidateRule<Yueluo> |
| 145 | + .GetBuilder() |
| 146 | + .Property(x=>x.Name).Required().Length(2,10) |
| 147 | + .Property(x=>x.Age).Range(0, int.MaxValue) |
| 148 | + .Property(x=>x.Password).Validate(value=>ValidatePassword(value)) |
| 149 | + .Property(x=>x.Level).Validate(value=>value + 1 >= 0) |
| 150 | + .Build(); |
| 151 | + |
| 152 | +o.V().Validate(rule).Run(); |
| 153 | +o.Validate(rule); |
| 154 | + |
| 155 | + |
| 156 | +// validate data in flunet api |
| 157 | +// attribute-based enabled by default |
| 158 | +o.V().Validate(v=> |
| 159 | + v |
| 160 | + .Property(x=>x.Name).Required().Length(2,10) |
| 161 | + .Property(x=>x.Age).Range(0, int.MaxValue) |
| 162 | + .Property(x=>x.Password).Validate(value=>ValidatePassword(value)) |
| 163 | + .Property(x=>x.Level).Validate(value=>value + 1 >= 0) |
| 164 | +).Run(); |
| 165 | + |
| 166 | +// suppending attribute-based validation |
| 167 | +o.V().SuppendAttributeValidation() |
| 168 | + .Validate(v=> |
| 169 | + v |
| 170 | + .Property(x=>x.Name).Required().Length(2,10) |
| 171 | + .Property(x=>x.Age).Range(0, int.MaxValue) |
| 172 | + .Property(x=>x.Password).Validate(value=>ValidatePassword(value)) |
| 173 | + .Property(x=>x.Level).Validate(value=>value + 1 >= 0) |
| 174 | +).Run(); |
| 175 | + |
| 176 | +// suppending sub-object validation |
| 177 | +// validate whole object |
| 178 | +o.V().SuppendSubObject() |
| 179 | + .SuppendAttributeValidation() |
| 180 | + .Validate(v=> |
| 181 | + v |
| 182 | + .Validate(x=>x.NewPassword == x.OldPassword) |
| 183 | + .Validate(x=>ValidateFormDb(x)) |
| 184 | + .Property(x=>x.Name).Required().Length(2,10) |
| 185 | + .Property(x=>x.Age).Range(0, int.MaxValue) |
| 186 | + .Property(x=>x.Age).If(x=>x.Name == "123").Range(0, int.MaxValue) |
| 187 | + .Property(x=>x.Password).Validate(value=>ValidatePassword(value)) |
| 188 | + .Property(x=>x.Level).Validate(value=>value + 1 >= 0) |
| 189 | +).Run(); |
| 190 | + |
| 191 | +// namespace for Task |
| 192 | +using Newbe.ObjectVisitor.Task; |
| 193 | + |
| 194 | +// async way |
| 195 | +await o.V().ForEachAsync((context)=>{}).RunAsync(); |
| 196 | + |
| 197 | +// controlling concurrency |
| 198 | +await o.V().ForEachAsync((context)=>{}).WhenAsync(tasks=>Task.WhenAll(tasks)).RunAsync(); |
| 199 | + |
| 200 | +// namespace for Microsoft.Extensions.DependencyInjection |
| 201 | +using Newbe.ObjectVistory.DepencyInjection; |
| 202 | + |
| 203 | +// inject services to the properties of this object |
| 204 | +this.V().ForEach(context=>this.ServiceProvider.GetService(context.PropertyInfo.PorpertyType)).Run(); |
| 205 | + |
| 206 | +// quick style for above |
| 207 | +this.V().PropertyInject(this.ServiceProvider); |
| 208 | + |
| 209 | +``` |
| 210 | + |
| 211 | +## Benchmark |
| 212 | + |
| 213 | +TODO |
| 214 | + |
| 215 | +visit properties vs Reflection vs Directly |
| 216 | + |
| 217 | +validate vs FluentValidation |
| 218 | + |
| 219 | +mapper vs AutoMapper |
| 220 | + |
| 221 | +## Stargazers over time |
| 222 | + |
| 223 | +[](https://starchart.cc/newbe36524/Newbe.ObjectVisitor) |
0 commit comments