@@ -88,6 +88,7 @@ public override string ConvertName(string name) {
8888}
8989public class EntityConverter {
9090
91+ private const int MAX_DESERIALIZATION_RECURSION_DEPTH = 100 ;
9192 private readonly ConcurrentDictionary < Type , EntityInfo > _cache ;
9293 private static readonly JsonSerializerOptions _options = new ( ) {
9394 PropertyNamingPolicy = new OnefuzzNamingPolicy ( ) ,
@@ -124,8 +125,8 @@ public static JsonSerializerOptions GetJsonSerializerOptions() {
124125 }
125126
126127 private static IEnumerable < EntityProperty > GetEntityProperties < T > ( ParameterInfo parameterInfo ) {
127- var name = parameterInfo . Name . EnsureNotNull ( $ "Invalid paramter { parameterInfo } ") ;
128- var parameterType = parameterInfo . ParameterType . EnsureNotNull ( $ "Invalid paramter { parameterInfo } ") ;
128+ var name = parameterInfo . Name . EnsureNotNull ( $ "Invalid parameter { parameterInfo } ") ;
129+ var parameterType = parameterInfo . ParameterType . EnsureNotNull ( $ "Invalid parameter { parameterInfo } ") ;
129130 var isRowkey = parameterInfo . GetCustomAttribute ( typeof ( RowKeyAttribute ) ) != null ;
130131 var isPartitionkey = parameterInfo . GetCustomAttribute ( typeof ( PartitionKeyAttribute ) ) != null ;
131132
@@ -135,7 +136,7 @@ private static IEnumerable<EntityProperty> GetEntityProperties<T>(ParameterInfo
135136
136137 ( TypeDiscrimnatorAttribute , ITypeProvider ) ? discriminator = null ;
137138 if ( discriminatorAttribute != null ) {
138- var t = ( ITypeProvider ) ( Activator . CreateInstance ( discriminatorAttribute . ConverterType ) ?? throw new Exception ( "unable to retrive the type provider" ) ) ;
139+ var t = ( ITypeProvider ) ( Activator . CreateInstance ( discriminatorAttribute . ConverterType ) ?? throw new Exception ( "unable to retrieve the type provider" ) ) ;
139140 discriminator = ( discriminatorAttribute , t ) ;
140141 }
141142
@@ -222,7 +223,7 @@ public TableEntity ToTableEntity<T>(T typedEntity) where T : EntityBase {
222223 }
223224
224225
225- private object ? GetFieldValue ( EntityInfo info , string name , TableEntity entity ) {
226+ private object ? GetFieldValue ( EntityInfo info , string name , TableEntity entity , int iterationCount ) {
226227 var ef = info . properties [ name ] . First ( ) ;
227228 if ( ef . kind == EntityPropertyKind . PartitionKey || ef . kind == EntityPropertyKind . RowKey ) {
228229 // partition & row keys must always be strings
@@ -285,7 +286,23 @@ public TableEntity ToTableEntity<T>(T typedEntity) where T : EntityBase {
285286 var outputType = ef . type ;
286287 if ( ef . discriminator != null ) {
287288 var ( attr , typeProvider ) = ef . discriminator . Value ;
288- var v = GetFieldValue ( info , attr . FieldName , entity ) ?? throw new Exception ( $ "No value for { attr . FieldName } ") ;
289+ if ( iterationCount > MAX_DESERIALIZATION_RECURSION_DEPTH ) {
290+ var tags = GenerateTableEntityTags ( entity ) ;
291+ tags . AddRange ( new ( string , string ) [ ] {
292+ ( "outputType" , outputType ? . Name ?? string . Empty ) ,
293+ ( "fieldName" , fieldName )
294+ } ) ;
295+ throw new OrmMaxRecursionDepthReachedException ( $ "MAX_DESERIALIZATION_RECURSION_DEPTH reached. Too many iterations deserializing { info . type } . { PrintTags ( tags ) } ") ;
296+ }
297+ if ( attr . FieldName == name ) {
298+ var tags = GenerateTableEntityTags ( entity ) ;
299+ tags . AddRange ( new ( string , string ) [ ] {
300+ ( "outputType" , outputType ? . Name ?? string . Empty ) ,
301+ ( "fieldName" , fieldName )
302+ } ) ;
303+ throw new OrmInvalidDiscriminatorFieldException ( $ "Discriminator field is the same as the field being deserialized { name } . { PrintTags ( tags ) } ") ;
304+ }
305+ var v = GetFieldValue ( info , attr . FieldName , entity , ++ iterationCount ) ?? throw new Exception ( $ "No value for { attr . FieldName } ") ;
289306 outputType = typeProvider . GetTypeInfo ( v ) ;
290307 }
291308
@@ -302,8 +319,13 @@ public TableEntity ToTableEntity<T>(T typedEntity) where T : EntityBase {
302319 return JsonSerializer . Deserialize ( value , outputType , options : _options ) ;
303320 }
304321 }
305- } catch ( Exception ex ) {
306- throw new InvalidOperationException ( $ "Unable to get value for property '{ name } ' (entity field '{ fieldName } ')", ex ) ;
322+ } catch ( Exception ex )
323+ when ( ex is not OrmException ) {
324+ var tags = GenerateTableEntityTags ( entity ) ;
325+ tags . AddRange ( new ( string , string ) [ ] {
326+ ( "fieldName" , fieldName )
327+ } ) ;
328+ throw new InvalidOperationException ( $ "Unable to get value for property '{ name } ' (entity field '{ fieldName } '). { PrintTags ( tags ) } ", ex ) ;
307329 }
308330 }
309331
@@ -313,7 +335,7 @@ public T ToRecord<T>(TableEntity entity) where T : EntityBase {
313335
314336 object ? [ ] parameters ;
315337 try {
316- parameters = entityInfo . properties . Select ( grouping => GetFieldValue ( entityInfo , grouping . Key , entity ) ) . ToArray ( ) ;
338+ parameters = entityInfo . properties . Select ( grouping => GetFieldValue ( entityInfo , grouping . Key , entity , 0 ) ) . ToArray ( ) ;
317339 } catch ( Exception ex ) {
318340 throw new InvalidOperationException ( $ "Unable to extract properties from TableEntity for { typeof ( T ) } ", ex ) ;
319341 }
@@ -361,6 +383,31 @@ public T ToRecord<T>(TableEntity entity) where T : EntityBase {
361383 return Expression . Lambda < Func < T , object ? > > ( call , paramter ) . Compile ( ) ;
362384 }
363385
386+ private static List < ( string , string ) > GenerateTableEntityTags ( TableEntity entity ) {
387+ var entityKeys = string . Join ( ',' , entity . Keys ) ;
388+ var partitionKey = entity . ContainsKey ( EntityPropertyKind . PartitionKey . ToString ( ) ) ? entity . GetString ( EntityPropertyKind . PartitionKey . ToString ( ) ) : string . Empty ;
389+ var rowKey = entity . ContainsKey ( EntityPropertyKind . RowKey . ToString ( ) ) ? entity . GetString ( EntityPropertyKind . RowKey . ToString ( ) ) : string . Empty ;
364390
391+ return new List < ( string , string ) > {
392+ ( "entityKeys" , entityKeys ) ,
393+ ( "partitionKey" , partitionKey ) ,
394+ ( "rowKey" , rowKey )
395+ } ;
396+ }
397+
398+ private static string PrintTags ( List < ( string , string ) > ? tags ) {
399+ return tags != null ? string . Join ( ", " , tags . Select ( x => $ "{ x . Item1 } ={ x . Item2 } ") ) : string . Empty ;
400+ }
401+ }
402+
403+ public class OrmInvalidDiscriminatorFieldException : OrmException {
404+ public OrmInvalidDiscriminatorFieldException ( string message ) : base ( message ) { }
405+ }
406+
407+ public class OrmMaxRecursionDepthReachedException : OrmException {
408+ public OrmMaxRecursionDepthReachedException ( string message ) : base ( message ) { }
409+ }
365410
411+ public class OrmException : Exception {
412+ public OrmException ( string message ) : base ( message ) { }
366413}
0 commit comments