public static TResult BindSingleValueToArray <TResult>(this EntityProperty value, Type arrayType, Func <object, TResult> onBound, Func <TResult> onFailedToBind) { #region Refs object ComposeFromBase <TBase>(Type composedType, Type genericCompositionType, Func <Type, TBase, object> instantiate) { return(value.BindSingleValueToArray(typeof(TBase), objects => { var guids = (TBase[])objects; var resourceType = arrayType.GenericTypeArguments.First(); var instantiatableType = genericCompositionType.MakeGenericType(resourceType); var refs = guids .Select( guidValue => { var instance = instantiate(instantiatableType, guidValue); // Activator.CreateInstance(instantiatableType, new object[] { guidValue }); return instance; }) .ToArray(); var typedRefs = refs.CastArray(arrayType); return typedRefs; }, () => throw new Exception("BindArray failed to bind to Guids?"))); } if (arrayType.IsSubClassOfGeneric(typeof(IRef <>))) { var values = ComposeFromBase <Guid>(typeof(IRef <>), typeof(EastFive.Ref <>), (instantiatableType, guidValue) => Activator.CreateInstance(instantiatableType, new object[] { guidValue })); return(onBound(values)); } object ComposeOptionalFromBase <TBase>(Type composedType, Type genericCompositionType, Type optionalBaseType) { var values = ComposeFromBase <Guid?>(composedType, genericCompositionType, (instantiatableType, guidValueMaybe) => { if (!guidValueMaybe.HasValue) { return(Activator.CreateInstance(instantiatableType, new object[] { })); } var guidValue = guidValueMaybe.Value; var resourceType = arrayType.GenericTypeArguments.First(); var instantiatableRefType = optionalBaseType.MakeGenericType(resourceType); var refValue = Activator.CreateInstance(instantiatableRefType, new object[] { guidValue }); var result = Activator.CreateInstance(instantiatableType, new object[] { refValue }); return(result); }); return(values); } if (arrayType.IsSubClassOfGeneric(typeof(IRefOptional <>))) { var values = ComposeOptionalFromBase <Guid?>(typeof(IRefOptional <>), typeof(EastFive.RefOptional <>), typeof(EastFive.Ref <>)); return(onBound(values)); } #endregion if (arrayType.IsArray) { var arrayElementType = arrayType.GetElementType(); var values = value.BinaryValue .FromByteArray() .Select( bytes => { var ep = new EntityProperty(bytes); var arrayValues = ep.BindSingleValueToArray <object>(arrayElementType, v => v, () => Array.CreateInstance(arrayElementType, 0)); // var arrayValues = bytes.FromEdmTypedByteArray(arrayElementType); return(arrayValues); }) .ToArray(); //var values = value.BinaryValue.FromEdmTypedByteArray(arrayElementType); return(onBound(values)); } return(arrayType.IsNullable( nulledType => { if (typeof(Guid) == nulledType) { var values = value.BinaryValue.ToNullablesFromByteArray <Guid>( (byteArray) => { if (byteArray.Length == 16) { return new Guid(byteArray); } return default(Guid); }); return onBound(values); } if (typeof(decimal) == nulledType) { var values = value.BinaryValue.ToNullablesFromByteArray <decimal>( byteArray => { if (byteArray.TryConvertToDecimal(out decimal decimalValue)) { return decimalValue; } return default(decimal); }); return onBound(values); } if (typeof(DateTime) == nulledType) { var values = value.BinaryValue.ToNullableDateTimesFromByteArray(); return onBound(values); } var arrayOfObj = value.BinaryValue.FromEdmTypedByteArray(arrayType); var arrayOfType = arrayOfObj.CastArray(arrayType); return onBound(arrayOfType); throw new Exception($"Cannot serialize a nullable array of `{nulledType.FullName}`."); }, () => { if (typeof(Guid) == arrayType) { var values = value.BinaryValue.ToGuidsFromByteArray(); return onBound(values); } if (typeof(byte) == arrayType) { return onBound(value.BinaryValue); } if (typeof(bool) == arrayType) { var boolArray = value.BinaryValue .Select(b => b != 0) .ToArray(); return onBound(boolArray); } if (typeof(DateTime) == arrayType) { var values = value.BinaryValue.ToDateTimesFromByteArray(); return onBound(values); } if (typeof(double) == arrayType) { var values = value.BinaryValue.ToDoublesFromByteArray(); return onBound(values); } if (typeof(decimal) == arrayType) { var values = value.BinaryValue.ToDecimalsFromByteArray(); return onBound(values); } if (typeof(int) == arrayType) { var values = value.BinaryValue.ToIntsFromByteArray(); return onBound(values); } if (typeof(long) == arrayType) { var values = value.BinaryValue.ToLongsFromByteArray(); return onBound(values); } if (typeof(string) == arrayType) { var values = value.BinaryValue.ToStringNullOrEmptysFromUTF8ByteArray(); return onBound(values); } if (arrayType.IsEnum) { var values = value.BinaryValue.ToEnumsFromByteArray(arrayType); return onBound(values); } if (typeof(object) == arrayType) { var values = value.BinaryValue.FromEdmTypedByteArray(arrayType); return onBound(values); } return arrayType .GetAttributesInterface <IBind <EntityProperty> >(true) .First <IBind <EntityProperty>, TResult>( (epSerializer, next) => { var values = value.BinaryValue.FromEdmTypedByteArray(typeof(byte[])); var boundValues = values .Where(valueObject => valueObject is byte[]) .Select( valueObject => { var valueBytes = valueObject as byte[]; var valueEp = new EntityProperty(valueBytes); return epSerializer.Bind(valueEp, arrayType, v => v, () => arrayType.GetDefault()); }) .CastArray(arrayType); return onBound(boundValues); }, () => { throw new Exception($"Cannot serialize array of `{arrayType.FullName}`."); }); })); }
public static TResult Bind <TResult>(this EntityProperty value, Type type, Func <object, TResult> onBound, Func <TResult> onFailedToBind) { if (type.IsArray) { var arrayType = type.GetElementType(); return(value.BindSingleValueToArray(arrayType, onBound, onFailedToBind)); } #region Basic values if (typeof(Guid) == type) { if (value.PropertyType == EdmType.Guid) { var guidValue = value.GuidValue; return(onBound(guidValue)); } return(onBound(default(Guid))); // This seems to be the best move in case of data migration // return onFailedToBind(); } // TODO: Type check the rest of these like GUID if (typeof(long) == type) { var longValue = value.Int64Value; return(onBound(longValue)); } if (typeof(int) == type) { var intValue = value.Int32Value; return(onBound(intValue)); } if (typeof(float) == type) { var floatValue = (float)value.DoubleValue; return(onBound(floatValue)); } if (typeof(double) == type) { var floatValue = value.DoubleValue; return(onBound(floatValue)); } if (typeof(string) == type) { if (value.PropertyType != EdmType.String) { return(onBound(default(string))); } var stringValue = value.StringValue; return(onBound(stringValue)); } if (typeof(DateTime) == type) { var dtValue = value.DateTime; return(onBound(dtValue)); } if (typeof(TimeZoneInfo) == type) { if (value.PropertyType != EdmType.String) { return(onFailedToBind()); } var stringValue = value.StringValue; try { var tzi = TimeZoneInfo.FindSystemTimeZoneById(stringValue); return(onBound(tzi)); } catch (Exception) { return(onFailedToBind()); } } if (typeof(Uri) == type) { var strValue = value.StringValue; if (Uri.TryCreate(strValue, UriKind.RelativeOrAbsolute, out Uri uriValue)) { return(onBound(uriValue)); } return(onBound(uriValue)); } if (typeof(Type) == type) { var typeValueString = value.StringValue; var typeValue = Type.GetType(typeValueString); return(onBound(typeValue)); } if (type.IsEnum) { var enumNameString = value.StringValue; var enumValue = Enum.Parse(type, enumNameString); return(onBound(enumValue)); } if (typeof(bool) == type) { var boolValue = value.BooleanValue; return(onBound(boolValue)); } #region refs object IRefInstance(Guid guidValue) { var resourceType = type.GenericTypeArguments.First(); return(EntityPropertyExtensions.IRefInstance(guidValue, resourceType)); } if (type.IsSubClassOfGeneric(typeof(IRef <>))) { var guidValue = value.GuidValue.Value; var instance = IRefInstance(guidValue); return(onBound(instance)); } if (type.IsSubClassOfGeneric(typeof(IRefOptional <>))) { Guid?GetIdMaybe() { if (value.PropertyType == EdmType.String) { if (Guid.TryParse(value.StringValue, out Guid id)) { return(id); } return(default(Guid?)); } if (value.PropertyType == EdmType.Binary) { return(default(Guid?)); } return(value.GuidValue); } var guidValueMaybe = GetIdMaybe(); var resourceType = type.GenericTypeArguments.First(); var instantiatableType = typeof(EastFive.RefOptional <>) .MakeGenericType(resourceType); if (!guidValueMaybe.HasValue) { var refOpt = Activator.CreateInstance(instantiatableType, new object[] { }); return(onBound(refOpt)); } var guidValue = guidValueMaybe.Value; var refValue = IRefInstance(guidValue); var instance = Activator.CreateInstance(instantiatableType, new object[] { refValue }); return(onBound(instance)); } if (type.IsSubClassOfGeneric(typeof(IRefs <>))) { var guidValues = value.BinaryValue.ToGuidsFromByteArray(); var resourceType = type.GenericTypeArguments.First(); var instantiatableType = typeof(EastFive.Refs <>).MakeGenericType(resourceType); var instance = Activator.CreateInstance(instantiatableType, new object[] { guidValues }); return(onBound(instance)); } #endregion if (typeof(object) == type) { switch (value.PropertyType) { case EdmType.Binary: return(onBound(value.BinaryValue)); case EdmType.Boolean: if (value.BooleanValue.HasValue) { return(onBound(value.BooleanValue.Value)); } break; case EdmType.DateTime: if (value.DateTime.HasValue) { return(onBound(value.DateTime.Value)); } break; case EdmType.Double: if (value.DoubleValue.HasValue) { return(onBound(value.DoubleValue.Value)); } break; case EdmType.Guid: if (value.GuidValue.HasValue) { var nullGuidKey = new Guid(EDMExtensions.NullGuidKey); if (value.GuidValue.Value == nullGuidKey) { return(onBound(null)); } return(onBound(value.GuidValue.Value)); } break; case EdmType.Int32: if (value.Int32Value.HasValue) { return(onBound(value.Int32Value.Value)); } break; case EdmType.Int64: if (value.Int64Value.HasValue) { return(onBound(value.Int64Value.Value)); } break; case EdmType.String: return(onBound(value.StringValue)); } return(onBound(value.PropertyAsObject)); } #endregion return(type.IsNullable( nullableType => { if (typeof(Guid) == nullableType) { var guidValue = value.GuidValue; return onBound(guidValue); } if (typeof(long) == nullableType) { var longValue = value.Int64Value; return onBound(longValue); } if (typeof(int) == nullableType) { var intValue = value.Int32Value; return onBound(intValue); } if (typeof(bool) == nullableType) { var boolValue = value.BooleanValue; return onBound(boolValue); } if (typeof(float) == nullableType) { var floatValue = value.DoubleValue.HasValue ? (float)value.DoubleValue.Value : default(float?); return onBound(floatValue); } if (typeof(double) == nullableType) { var floatValue = value.DoubleValue; return onBound(floatValue); } if (typeof(decimal) == nullableType) { var doubleValueMaybe = value.DoubleValue; var decimalMaybeValue = doubleValueMaybe.HasValue ? (decimal)doubleValueMaybe.Value : default(decimal?); return onBound(decimalMaybeValue); } if (typeof(DateTime) == nullableType) { var dateTimeValueMaybe = value.DateTime; return onBound(dateTimeValueMaybe); } return onFailedToBind(); }, () => onFailedToBind())); }