| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Diagnostics.Contracts; |
| | | 6 | | using System.Linq; |
| | | 7 | | using System.Reflection; |
| | | 8 | | using CoreWCF.Runtime; |
| | | 9 | | |
| | | 10 | | namespace CoreWCF.Description |
| | | 11 | | { |
| | | 12 | | internal static class CustomAttributeProvider |
| | | 13 | | { |
| | | 14 | | |
| | | 15 | | public static object[] GetDualCustomAttributes(this ICustomAttributeProvider provider, Type attributeType, bool |
| | | 16 | | { |
| | 56149 | 17 | | object[] attributes = provider.GetCustomAttributes(inherit); |
| | 56147 | 18 | | if (attributes == null || attributes.Length == 0) |
| | | 19 | | { |
| | 17377 | 20 | | return attributes; |
| | | 21 | | } |
| | | 22 | | |
| | 93945 | 23 | | object[] result = attributes.Where(attribute => attributeType.IsAssignableFrom(attribute.GetType())).ToArray |
| | | 24 | | |
| | 38770 | 25 | | if (result.Length == 0) |
| | | 26 | | { |
| | | 27 | | // Only if we don't find the CoreWCF attribute, look for the S.SM attribute |
| | 33360 | 28 | | if (attributeType == typeof(ServiceContractAttribute)) |
| | | 29 | | { |
| | 3974 | 30 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMServic |
| | 2074 | 31 | | for (int i = 0; i < result.Length; i++) |
| | | 32 | | { |
| | 226 | 33 | | result[i] = ConvertFromServiceModelServiceContractAttribute(result[i]); |
| | | 34 | | } |
| | | 35 | | } |
| | 32549 | 36 | | else if (attributeType == typeof(OperationContractAttribute)) |
| | | 37 | | { |
| | 7258 | 38 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMOperat |
| | 6412 | 39 | | for (int i = 0; i < result.Length; i++) |
| | | 40 | | { |
| | 191 | 41 | | result[i] = ConvertFromServiceModelOperationContractAttribute(result[i]); |
| | | 42 | | } |
| | | 43 | | } |
| | 29534 | 44 | | else if (attributeType == typeof(MessageContractAttribute)) |
| | | 45 | | { |
| | 22 | 46 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMMessag |
| | 44 | 47 | | for (int i = 0; i < result.Length; i++) |
| | | 48 | | { |
| | 11 | 49 | | result[i] = ConvertFromServiceModelMessageContractAttribute(result[i]); |
| | | 50 | | } |
| | | 51 | | } |
| | 29523 | 52 | | else if (attributeType == typeof(MessageHeaderAttribute)) |
| | | 53 | | { |
| | 230 | 54 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMMessag |
| | 230 | 55 | | attribute.GetType().FullName.Equals(ServiceReflector.SMMessag |
| | 242 | 56 | | for (int i = 0; i < result.Length; i++) |
| | | 57 | | { |
| | 6 | 58 | | result[i] = ConvertFromServiceModelMessageHeaderAttribute(result[i]); |
| | | 59 | | } |
| | | 60 | | } |
| | 29408 | 61 | | else if (attributeType == typeof(MessageBodyMemberAttribute)) |
| | | 62 | | { |
| | 198 | 63 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMMessag |
| | 214 | 64 | | for (int i = 0; i < result.Length; i++) |
| | | 65 | | { |
| | 8 | 66 | | result[i] = ConvertFromServiceModelMessageBodyMemberAttribute(result[i]); |
| | | 67 | | } |
| | | 68 | | } |
| | 29309 | 69 | | else if (attributeType == typeof(MessagePropertyAttribute)) |
| | | 70 | | { |
| | 10 | 71 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMMessag |
| | 16 | 72 | | for (int i = 0; i < result.Length; i++) |
| | | 73 | | { |
| | 3 | 74 | | result[i] = ConvertFromServiceModelMessagePropertyAttribute(result[i]); |
| | | 75 | | } |
| | | 76 | | } |
| | 29304 | 77 | | else if (attributeType == typeof(MessageParameterAttribute)) |
| | | 78 | | { |
| | 342 | 79 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMMessag |
| | 398 | 80 | | for (int i = 0; i < result.Length; i++) |
| | | 81 | | { |
| | 28 | 82 | | result[i] = ConvertFromServiceModelMessageParameterAttribute(result[i]); |
| | | 83 | | } |
| | | 84 | | } |
| | 29133 | 85 | | else if (attributeType == typeof(XmlSerializerFormatAttribute)) |
| | | 86 | | { |
| | 12719 | 87 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMXmlSer |
| | 11682 | 88 | | for (int i = 0; i < result.Length; i++) |
| | | 89 | | { |
| | 80 | 90 | | result[i] = ConvertFromServiceModelXmlSerializerFormatAttribute(result[i]); |
| | | 91 | | } |
| | | 92 | | } |
| | 23372 | 93 | | else if (attributeType == typeof(FaultContractAttribute)) |
| | | 94 | | { |
| | 5606 | 95 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMFaultCo |
| | 5176 | 96 | | for (int i = 0; i < result.Length; i++) |
| | | 97 | | { |
| | 4 | 98 | | result[i] = ConvertFromServiceModelFaultContractAttribute(result[i]); |
| | | 99 | | } |
| | | 100 | | } |
| | 20788 | 101 | | else if (attributeType == typeof(ServiceKnownTypeAttribute)) |
| | | 102 | | { |
| | 7417 | 103 | | result = attributes.Where(attribute => attribute.GetType().FullName.Equals(ServiceReflector.SMServic |
| | 6722 | 104 | | for (int i = 0; i < result.Length; i++) |
| | | 105 | | { |
| | 18 | 106 | | result[i] = ConvertFromServiceModelServiceKnownTypeAttribute(result[i]); |
| | | 107 | | } |
| | | 108 | | } |
| | | 109 | | } |
| | 38770 | 110 | | return result; |
| | | 111 | | } |
| | | 112 | | |
| | | 113 | | private static ServiceContractAttribute ConvertFromServiceModelServiceContractAttribute(object attr) |
| | | 114 | | { |
| | | 115 | | Fx.Assert(attr.GetType().FullName.Equals(ServiceReflector.SMServiceContractAttributeFullName), "Expected att |
| | 226 | 116 | | bool hasProtectionLevel = GetProperty<bool>(attr, "HasProtectionLevel"); |
| | 226 | 117 | | if (hasProtectionLevel) |
| | | 118 | | { |
| | | 119 | | // ProtectionLevel isn't supported yet so if it was set on the S.SM.SCA, then we can't do the mapping so |
| | 0 | 120 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new PlatformNotSupportedException("System.Serv |
| | | 121 | | } |
| | | 122 | | |
| | 226 | 123 | | var sca = new ServiceContractAttribute(); |
| | 226 | 124 | | string tmpStr = GetProperty<string>(attr, nameof(ServiceContractAttribute.ConfigurationName)); |
| | 226 | 125 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 126 | | { |
| | 0 | 127 | | sca.ConfigurationName = tmpStr; |
| | | 128 | | } |
| | | 129 | | |
| | 226 | 130 | | tmpStr = GetProperty<string>(attr, nameof(ServiceContractAttribute.Name)); |
| | 226 | 131 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 132 | | { |
| | 36 | 133 | | sca.Name = tmpStr; |
| | | 134 | | } |
| | | 135 | | |
| | 226 | 136 | | sca.Namespace = GetProperty<string>(attr, nameof(ServiceContractAttribute.Namespace)); |
| | 226 | 137 | | sca.SessionMode = GetProperty<SessionMode>(attr, nameof(ServiceContractAttribute.SessionMode)); |
| | 226 | 138 | | sca.CallbackContract = GetProperty<Type>(attr, nameof(ServiceContractAttribute.CallbackContract)); |
| | 226 | 139 | | return sca; |
| | | 140 | | } |
| | | 141 | | |
| | | 142 | | |
| | | 143 | | private static MessageContractAttribute ConvertFromServiceModelMessageContractAttribute(object attr) |
| | | 144 | | { |
| | | 145 | | Fx.Assert(attr.GetType().FullName.Equals(ServiceReflector.SMMessageContractAttributeFullName), "Expected att |
| | 11 | 146 | | var messageContract = new MessageContractAttribute |
| | 11 | 147 | | { |
| | 11 | 148 | | IsWrapped = GetProperty<bool>(attr, nameof(MessageContractAttribute.IsWrapped)) |
| | 11 | 149 | | }; |
| | 11 | 150 | | string tmpStr = GetProperty<string>(attr, nameof(MessageContractAttribute.WrapperName)); |
| | 11 | 151 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 152 | | { |
| | 11 | 153 | | messageContract.WrapperName = tmpStr; |
| | | 154 | | } |
| | 11 | 155 | | tmpStr = GetProperty<string>(attr, nameof(MessageContractAttribute.WrapperNamespace)); |
| | 11 | 156 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 157 | | { |
| | 11 | 158 | | messageContract.WrapperNamespace = tmpStr; |
| | | 159 | | } |
| | 11 | 160 | | return messageContract; |
| | | 161 | | } |
| | | 162 | | |
| | | 163 | | private static MessageHeaderAttribute ConvertFromServiceModelMessageHeaderAttribute(object attr) |
| | | 164 | | { |
| | 6 | 165 | | string attributeFullName = attr.GetType().FullName; |
| | | 166 | | Fx.Assert(attributeFullName.Equals(ServiceReflector.SMMessageHeaderAttributeFullName) || |
| | | 167 | | attributeFullName.Equals(ServiceReflector.SMMessageHeaderArrayAttributeFullName), |
| | | 168 | | "Expected attribute of type System.ServiceModel.MessageHeaderAttribute or System.ServiceModel.Mess |
| | | 169 | | |
| | 6 | 170 | | bool hasProtectionLevel = GetProperty<bool>(attr, "HasProtectionLevel"); |
| | 6 | 171 | | if (hasProtectionLevel) |
| | | 172 | | { |
| | | 173 | | // ProtectionLevel isn't supported yet so if it was set on the S.SM.SCA, then we can't do the mapping so |
| | 0 | 174 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new PlatformNotSupportedException("System.Serv |
| | | 175 | | } |
| | | 176 | | |
| | | 177 | | MessageHeaderAttribute messageHeader; |
| | | 178 | | Type declaringPropertyType; // Used so that we call GetProperty on the SM MessageHeaderAttribute type and no |
| | 6 | 179 | | if (attributeFullName.Equals(ServiceReflector.SMMessageHeaderAttributeFullName)) |
| | | 180 | | { |
| | 6 | 181 | | messageHeader = new MessageHeaderAttribute(); |
| | 6 | 182 | | declaringPropertyType = attr.GetType(); |
| | | 183 | | } |
| | | 184 | | else |
| | | 185 | | { |
| | 0 | 186 | | messageHeader = new MessageHeaderArrayAttribute(); |
| | 0 | 187 | | declaringPropertyType = attr.GetType().BaseType; |
| | | 188 | | } |
| | | 189 | | |
| | 6 | 190 | | string tmpStr = GetProperty<string>(attr, declaringPropertyType, nameof(MessageHeaderAttribute.Actor)); |
| | 6 | 191 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 192 | | { |
| | 0 | 193 | | messageHeader.Actor = tmpStr; |
| | | 194 | | } |
| | 6 | 195 | | tmpStr = GetProperty<string>(attr, declaringPropertyType, nameof(MessageHeaderAttribute.Name)); |
| | 6 | 196 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 197 | | { |
| | 3 | 198 | | messageHeader.Name = tmpStr; |
| | | 199 | | } |
| | 6 | 200 | | tmpStr = GetProperty<string>(attr, declaringPropertyType, nameof(MessageHeaderAttribute.Namespace)); |
| | 6 | 201 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 202 | | { |
| | 6 | 203 | | messageHeader.Namespace = tmpStr; |
| | | 204 | | } |
| | 6 | 205 | | messageHeader.MustUnderstand = GetProperty<bool>(attr, declaringPropertyType, nameof(MessageHeaderAttribute. |
| | 6 | 206 | | messageHeader.Relay = GetProperty<bool>(attr, nameof(MessageHeaderAttribute.Relay)); |
| | 6 | 207 | | return messageHeader; |
| | | 208 | | } |
| | | 209 | | |
| | | 210 | | private static MessageBodyMemberAttribute ConvertFromServiceModelMessageBodyMemberAttribute(object attr) |
| | | 211 | | { |
| | | 212 | | Fx.Assert(attr.GetType().FullName.Equals(ServiceReflector.SMMessageBodyMemberAttributeFullName), "Expected a |
| | 8 | 213 | | var messageBody = new MessageBodyMemberAttribute(); |
| | 8 | 214 | | string tmpStr = GetProperty<string>(attr, nameof(MessageBodyMemberAttribute.Name)); |
| | 8 | 215 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 216 | | { |
| | 0 | 217 | | messageBody.Name = tmpStr; |
| | | 218 | | } |
| | 8 | 219 | | tmpStr = GetProperty<string>(attr, nameof(MessageBodyMemberAttribute.Namespace)); |
| | 8 | 220 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 221 | | { |
| | 6 | 222 | | messageBody.Namespace = tmpStr; |
| | | 223 | | } |
| | 8 | 224 | | int order = GetProperty<int>(attr, nameof(MessageBodyMemberAttribute.Order)); |
| | 8 | 225 | | if (order >= 0) |
| | | 226 | | { |
| | 0 | 227 | | messageBody.Order = order; |
| | | 228 | | } |
| | 8 | 229 | | return messageBody; |
| | | 230 | | } |
| | | 231 | | |
| | | 232 | | private static MessagePropertyAttribute ConvertFromServiceModelMessagePropertyAttribute(object attr) |
| | | 233 | | { |
| | | 234 | | Fx.Assert(attr.GetType().FullName.Equals(ServiceReflector.SMMessagePropertyAttributeFullName), "Expected att |
| | 3 | 235 | | var messageProperty = new MessagePropertyAttribute(); |
| | 3 | 236 | | string tmpStr = GetProperty<string>(attr, nameof(MessagePropertyAttribute.Name)); |
| | 3 | 237 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 238 | | { |
| | 1 | 239 | | messageProperty.Name = tmpStr; |
| | | 240 | | } |
| | | 241 | | |
| | 3 | 242 | | return messageProperty; |
| | | 243 | | } |
| | | 244 | | |
| | | 245 | | private static MessageParameterAttribute ConvertFromServiceModelMessageParameterAttribute(object attr) |
| | | 246 | | { |
| | | 247 | | Fx.Assert(attr.GetType().FullName.Equals(ServiceReflector.SMMessageParameterAttributeFullName), "Expected at |
| | 28 | 248 | | var messageParameter = new MessageParameterAttribute(); |
| | 28 | 249 | | string tmpStr = GetProperty<string>(attr, nameof(MessageParameterAttribute.Name)); |
| | 28 | 250 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 251 | | { |
| | 28 | 252 | | messageParameter.Name = tmpStr; |
| | | 253 | | } |
| | | 254 | | |
| | 28 | 255 | | return messageParameter; |
| | | 256 | | } |
| | | 257 | | |
| | | 258 | | private static XmlSerializerFormatAttribute ConvertFromServiceModelXmlSerializerFormatAttribute(object attr) |
| | | 259 | | { |
| | | 260 | | Fx.Assert(attr.GetType().FullName.Equals(ServiceReflector.SMXmlSerializerFormatAttributeFullName), "Expected |
| | 80 | 261 | | var xmlSerializerFormatAttribute = new XmlSerializerFormatAttribute(); |
| | | 262 | | |
| | 80 | 263 | | xmlSerializerFormatAttribute.Style = GetProperty<OperationFormatStyle>(attr, nameof(XmlSerializerFormatAttri |
| | 80 | 264 | | xmlSerializerFormatAttribute.SupportFaults = GetProperty<bool>(attr, nameof(XmlSerializerFormatAttribute.Sup |
| | 80 | 265 | | xmlSerializerFormatAttribute.Use = GetProperty<OperationFormatUse>(attr, nameof(XmlSerializerFormatAttribute |
| | | 266 | | |
| | 80 | 267 | | return xmlSerializerFormatAttribute; |
| | | 268 | | } |
| | | 269 | | |
| | | 270 | | public static FaultContractAttribute ConvertFromServiceModelFaultContractAttribute(object attr) |
| | | 271 | | { |
| | | 272 | | Fx.Assert(attr.GetType().FullName.Equals(ServiceReflector.SMFaultContractAttributeFullName), "Expected attri |
| | 4 | 273 | | bool hasProtectionLevel = GetProperty<bool>(attr, "HasProtectionLevel"); |
| | 4 | 274 | | if (hasProtectionLevel) |
| | | 275 | | { |
| | | 276 | | // ProtectionLevel isn't supported yet so if it was set on the S.SM.SCA, then we can't do the mapping so |
| | 0 | 277 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new PlatformNotSupportedException("System.Serv |
| | | 278 | | } |
| | | 279 | | |
| | 4 | 280 | | var detailType = GetProperty<Type>(attr, nameof(FaultContractAttribute.DetailType)); |
| | 4 | 281 | | var faultContractAttribute = new FaultContractAttribute(detailType); |
| | | 282 | | |
| | 4 | 283 | | string tmpStr = GetProperty<string>(attr, nameof(FaultContractAttribute.Action)); |
| | 4 | 284 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 285 | | { |
| | 0 | 286 | | faultContractAttribute.Action = tmpStr; |
| | | 287 | | } |
| | 4 | 288 | | tmpStr = GetProperty<string>(attr, nameof(FaultContractAttribute.Name)); |
| | 4 | 289 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 290 | | { |
| | 4 | 291 | | faultContractAttribute.Name = tmpStr; |
| | | 292 | | } |
| | 4 | 293 | | tmpStr = GetProperty<string>(attr, nameof(FaultContractAttribute.Namespace)); |
| | 4 | 294 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 295 | | { |
| | 2 | 296 | | faultContractAttribute.Namespace = tmpStr; |
| | | 297 | | } |
| | | 298 | | |
| | 4 | 299 | | return faultContractAttribute; |
| | | 300 | | } |
| | | 301 | | |
| | | 302 | | private static OperationContractAttribute ConvertFromServiceModelOperationContractAttribute(object attr) |
| | | 303 | | { |
| | | 304 | | Fx.Assert(attr.GetType().FullName.Equals(ServiceReflector.SMOperationContractAttributeFullName), "Expected a |
| | 191 | 305 | | bool hasProtectionLevel = GetProperty<bool>(attr, "HasProtectionLevel"); |
| | 191 | 306 | | if (hasProtectionLevel) |
| | | 307 | | { |
| | | 308 | | // ProtectionLevel isn't supported yet so if it was set on the S.SM.SCA, then we can't do the mapping so |
| | 0 | 309 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new PlatformNotSupportedException("System.Serv |
| | | 310 | | } |
| | | 311 | | |
| | 191 | 312 | | var oca = new OperationContractAttribute(); |
| | 191 | 313 | | string tmpStr = GetProperty<string>(attr, nameof(OperationContractAttribute.Name)); |
| | 191 | 314 | | if (!string.IsNullOrEmpty(tmpStr)) |
| | | 315 | | { |
| | 78 | 316 | | oca.Name = tmpStr; |
| | | 317 | | } |
| | | 318 | | |
| | 191 | 319 | | tmpStr = GetProperty<string>(attr, nameof(OperationContractAttribute.Action)); |
| | 191 | 320 | | if (tmpStr != null) // String.Empty apparently is fine |
| | | 321 | | { |
| | 78 | 322 | | oca.Action = tmpStr; |
| | | 323 | | } |
| | | 324 | | |
| | 191 | 325 | | tmpStr = GetProperty<string>(attr, nameof(OperationContractAttribute.ReplyAction)); |
| | 191 | 326 | | if (tmpStr != null) // String.Empty apparently is fine |
| | | 327 | | { |
| | 78 | 328 | | oca.ReplyAction = tmpStr; |
| | | 329 | | } |
| | | 330 | | |
| | 191 | 331 | | oca.AsyncPattern = GetProperty<bool>(attr, nameof(OperationContractAttribute.AsyncPattern)); |
| | 191 | 332 | | oca.IsOneWay = GetProperty<bool>(attr, nameof(OperationContractAttribute.IsOneWay)); |
| | 191 | 333 | | oca.IsInitiating = GetProperty<bool>(attr, nameof(OperationContractAttribute.IsInitiating)); |
| | 191 | 334 | | oca.IsTerminating = GetProperty<bool>(attr, nameof(OperationContractAttribute.IsTerminating)); |
| | 191 | 335 | | return oca; |
| | | 336 | | } |
| | | 337 | | |
| | | 338 | | private static ServiceKnownTypeAttribute ConvertFromServiceModelServiceKnownTypeAttribute(object attr) |
| | | 339 | | { |
| | | 340 | | Fx.Assert(attr.GetType().FullName.Equals(ServiceReflector.SMServiceKnownTypeAttributeFullName), "Expected at |
| | 18 | 341 | | var skta = new ServiceKnownTypeAttribute(); |
| | | 342 | | |
| | 18 | 343 | | skta.MethodName= GetProperty<string>(attr, nameof(ServiceKnownTypeAttribute.MethodName)); |
| | 18 | 344 | | skta.Type = GetProperty<Type>(attr, nameof(ServiceKnownTypeAttribute.Type)); |
| | 18 | 345 | | skta.DeclaringType = GetProperty<Type>(attr, nameof(ServiceKnownTypeAttribute.DeclaringType)); |
| | | 346 | | |
| | 18 | 347 | | return skta; |
| | | 348 | | } |
| | | 349 | | |
| | | 350 | | private static TProp GetProperty<TProp>(object obj, string propName) |
| | | 351 | | { |
| | | 352 | | Fx.Assert(obj != null, "Expected non-null object"); |
| | 3298 | 353 | | return GetProperty<TProp>(obj, obj.GetType(), propName); |
| | | 354 | | } |
| | | 355 | | |
| | | 356 | | private static TProp GetProperty<TProp>(object obj, Type declaringPropertyType, string propName) |
| | | 357 | | { |
| | | 358 | | Fx.Assert(obj != null, "Expected non-null object"); |
| | | 359 | | PropertyInfo propInfo; |
| | 3322 | 360 | | if (typeof(TProp).IsEnum) |
| | | 361 | | { |
| | 386 | 362 | | propInfo = declaringPropertyType.GetProperty(propName); |
| | | 363 | | Fx.Assert(propInfo != null, $"Could not find property with name {propName} on object of type {obj.GetTyp |
| | 386 | 364 | | return (TProp)Enum.ToObject(typeof(TProp), propInfo.GetValue(obj)); |
| | | 365 | | } |
| | | 366 | | else |
| | | 367 | | { |
| | 2936 | 368 | | propInfo = declaringPropertyType.GetProperty(propName, typeof(TProp)); |
| | | 369 | | Fx.Assert(propInfo != null, $"Could not find property with name {propName} of type {typeof(TProp).FullNa |
| | 2936 | 370 | | return (TProp)propInfo.GetValue(obj); |
| | | 371 | | } |
| | | 372 | | } |
| | | 373 | | } |
| | | 374 | | } |