/// <summary> /// Checks if a constant can be converted from one record data type to another record data type. /// </summary> /// <param name="newValue">Value to convert.</param> /// <param name="oldDataType">Old data type.</param> protected virtual void CheckConstantStructuralRecord(Object newValue, StructuralDataType oldDataType) { var newStructuralType = newValue.TypeSlim as StructuralTypeSlim; if (newStructuralType == null || oldDataType.Properties.Count != newStructuralType.Properties.Count) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The number of record entries on '{0}' and '{1}' does not match.", oldDataType, newValue.TypeSlim)); } var oldProperties = oldDataType.Properties; if (!Helpers.TryGetStructuralPropertyTypes(newValue.TypeSlim, out var newPropertyTypes)) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Cannot convert object of type '{0}' to type '{1}'.", oldDataType.UnderlyingType, newValue.TypeSlim)); } var n = newPropertyTypes.Count; for (var i = 0; i < n; i++) { var oldProperty = oldProperties[i]; var newPropertyType = newPropertyTypes[i]; var oldValue = oldProperty.GetValue(newValue.Value); var newPropertyValue = Object.Create(oldValue, newPropertyType, oldProperty.Type.UnderlyingType); CheckConstant(newPropertyValue, oldProperty.Type); } }
protected override LambdaExpression VisitStructural(StructuralDataType type) { if (type == null) { throw new ArgumentNullException(nameof(type)); } if (!_visited.Add(type.UnderlyingType)) { return(Expression.Parameter(typeof(DataTypeBinarySerializer), "serializer").Let(serializerParameter => Expression.Parameter(typeof(Stream), "stream").Let(streamParameter => Expression.Lambda( Expression.Convert( Expression.Call(serializerParameter, ReflectionConstants.Deserialize, Expression.Constant(type.UnderlyingType), streamParameter), type.UnderlyingType ), serializerParameter, streamParameter ) ) )); } try { return(base.VisitStructural(type)); } finally { _visited.Remove(type.UnderlyingType); } }
protected override LambdaExpression MakeStructural(StructuralDataType type, ReadOnlyCollection <Tuple <DataProperty, LambdaExpression> > properties) { return(type.StructuralKind switch { StructuralDataTypeKinds.Anonymous or StructuralDataTypeKinds.Tuple => MakeAnonymousOrTuple(type, properties), StructuralDataTypeKinds.Entity or StructuralDataTypeKinds.Record => MakeEntityOrRecord(type, properties), _ => throw new NotSupportedException("Only record, anonymous, entity, and tuples are supported structural types."), });
/// <summary> /// Checks if a constant can be converted from one tuple data type to another tuple data type. /// </summary> /// <param name="newValue">Value to convert.</param> /// <param name="oldDataType">Old data type.</param> protected virtual void CheckConstantStructuralTuple(Object newValue, StructuralDataType oldDataType) { var newTupleType = newValue.TypeSlim as GenericTypeSlim; if (newTupleType == null || oldDataType.Properties.Count != newTupleType.GenericArgumentCount) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The number of tuple components on '{0}' and '{1}' does not match.", oldDataType, newTupleType)); } CheckConstantStructuralByPropertyOrderInstantiation(newValue, oldDataType); }
protected override DataType VisitStructural(StructuralDataType type) { if (_processed.Add(type.UnderlyingType)) { if (type.StructuralKind == StructuralDataTypeKinds.Entity) { _enqueue(new KeyValuePair <Type, StructuralDataType>(type.UnderlyingType, type)); } return(base.VisitStructural(type)); } return(type); }
protected override void CheckConstantStructuralCore(Object newValue, StructuralDataType oldDataType) { var oldPropertyMap = oldDataType.Properties.ToDictionary(p => p.Name, p => p); var newStructuralType = (StructuralTypeSlim)newValue.TypeSlim; var newProperties = newStructuralType.Properties; for (int i = 0, n = newProperties.Count; i < n; i++) { var newProperty = newProperties[i]; var oldProperty = oldPropertyMap[newProperty.Name]; var oldValue = oldProperty.GetValue(newValue.Value); var newPropertyValue = Object.Create(oldValue, newProperty.PropertyType, oldProperty.Type.UnderlyingType); CheckConstant(newPropertyValue, oldProperty.Type); } }
/// <summary> /// Converts a constant from one structural data type to another data type. /// </summary> /// <param name="originalValue">Original value to convert.</param> /// <param name="oldDataType">Old data type.</param> /// <param name="newDataType">New data type.</param> /// <returns>Converted constant.</returns> protected virtual object ConvertConstantStructural(object originalValue, StructuralDataType oldDataType, StructuralDataType newDataType) { if (oldDataType.StructuralKind == newDataType.StructuralKind) { switch (oldDataType.StructuralKind) { case StructuralDataTypeKinds.Anonymous: return(ConvertConstantStructuralAnonymous(originalValue, oldDataType, newDataType)); case StructuralDataTypeKinds.Tuple: return(ConvertConstantStructuralTuple(originalValue, oldDataType, newDataType)); case StructuralDataTypeKinds.Record: return(ConvertConstantStructuralRecord(originalValue, oldDataType, newDataType)); } } return(ConvertConstantStructuralCore(originalValue, oldDataType, newDataType)); }
/// <summary> /// Checks if a constant can be converted from one structural data type to another data type. /// </summary> /// <param name="newValue">Value to convert.</param> /// <param name="oldDataType">Old data type.</param> protected virtual void CheckConstantStructural(Object newValue, StructuralDataType oldDataType) { switch (oldDataType.StructuralKind) { case StructuralDataTypeKinds.Anonymous: CheckConstantStructuralAnonymous(newValue, oldDataType); return; case StructuralDataTypeKinds.Tuple: CheckConstantStructuralTuple(newValue, oldDataType); return; case StructuralDataTypeKinds.Record: CheckConstantStructuralRecord(newValue, oldDataType); return; } CheckConstantStructuralCore(newValue, oldDataType); }
public void DataTypeVisitor_Change() { var v = new IncompleteVisitor(); var d1 = DataType.FromType(typeof(int[])); Assert.ThrowsException <NotImplementedException>(() => v.Visit(d1)); var d2 = DataType.FromType(typeof(bool[])); Assert.AreSame(d2, v.Visit(d2)); var d3 = DataType.FromType(new { a = 1 }.GetType()); Assert.ThrowsException <NotImplementedException>(() => v.Visit(d3)); var d4 = DataType.FromType(new { a = true }.GetType()); Assert.AreSame(d4, v.Visit(d4)); var d5 = new StructuralDataType(typeof(Foo), new[] { new DataProperty(typeof(Foo).GetField("Bar"), "Bar", DataType.FromType(typeof(int))) }.ToReadOnly(), StructuralDataTypeKinds.Entity); Assert.ThrowsException <NotImplementedException>(() => v.Visit(d5)); }
protected override object ConvertConstantStructuralCore(object originalValue, StructuralDataType oldDataType, StructuralDataType newDataType) { throw new NotImplementedException(); }
protected override object ConvertConstantStructuralCore(object originalValue, StructuralDataType oldDataType, StructuralDataType newDataType) { var oldPropertyMap = oldDataType.Properties.ToDictionary(p => p.Name, p => p); var newProperties = newDataType.Properties; var count = newProperties.Count; var args = new object[count]; for (var i = 0; i < count; i++) { var newProperty = newProperties[i]; var oldProperty = oldPropertyMap[newProperty.Name]; var oldValue = oldProperty.GetValue(originalValue); var newValue = ConvertConstant(oldValue, oldProperty.Type, newProperty.Type); args[i] = newValue; } var res = newDataType.CreateInstance(args); return(res); }
protected override object ConvertConstantStructuralCore(object originalValue, StructuralDataType oldDataType, StructuralDataType newDataType) { var res = newDataType.CreateInstance(); ConstantsMap[originalValue] = res; // Allow cycles var oldPropertyMap = oldDataType.Properties.ToDictionary(p => p.Name, p => p); var newProperties = newDataType.Properties; for (int i = 0, n = newProperties.Count; i < n; i++) { var newProperty = newProperties[i]; var oldProperty = oldPropertyMap[newProperty.Name]; var oldValue = oldProperty.GetValue(originalValue); var newValue = ConvertConstant(oldValue, oldProperty.Type, newProperty.Type); newProperty.SetValue(res, newValue); } return(res); }
protected override Type MakeStructural(StructuralDataType type, ReadOnlyCollection <Tuple <string, Type> > properties) { return(RuntimeCompiler.CreateAnonymousType(properties.Select(p => new KeyValuePair <string, Type>(p.Item1, p.Item2)))); }
/// <summary> /// Converts a constant from one record data type to another record data type. /// </summary> /// <param name="originalValue">Original value to convert.</param> /// <param name="oldDataType">Old record data type.</param> /// <param name="newDataType">New record data type.</param> /// <returns>Converted constant.</returns> protected virtual object ConvertConstantStructuralRecord(object originalValue, StructuralDataType oldDataType, StructuralDataType newDataType) { if (oldDataType.Properties.Count != newDataType.Properties.Count) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The number of record entries on '{0}' and '{1}' does not match.", oldDataType, newDataType)); } var oldProperties = oldDataType.Properties; var newProperties = newDataType.Properties; var result = newDataType.CreateInstance(); ConstantsMap[originalValue] = result; // Allow cycles var n = newProperties.Count; for (var i = 0; i < n; i++) { var oldProperty = oldProperties[i]; var newProperty = newProperties[i]; var oldValue = oldProperty.GetValue(originalValue); var newValue = ConvertConstant(oldValue, oldProperty.Type, newProperty.Type); newProperty.SetValue(result, newValue); } return(result); }
/// <summary> /// Converts a constant from one structural data type to another data type. This method is called when the structural type kind needs to change. /// </summary> /// <param name="originalValue">Original value to convert.</param> /// <param name="oldDataType">Old data type.</param> /// <param name="newDataType">New data type.</param> /// <returns>Converted constant.</returns> protected abstract object ConvertConstantStructuralCore(object originalValue, StructuralDataType oldDataType, StructuralDataType newDataType);
/// <summary> /// Converts a constant from one tuple data type to another tuple data type. /// </summary> /// <param name="originalValue">Original value to convert.</param> /// <param name="oldDataType">Old tuple data type.</param> /// <param name="newDataType">New tuple data type.</param> /// <returns>Converted constant.</returns> protected virtual object ConvertConstantStructuralTuple(object originalValue, StructuralDataType oldDataType, StructuralDataType newDataType) { if (oldDataType.Properties.Count != newDataType.Properties.Count) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The number of tuple components on '{0}' and '{1}' does not match.", oldDataType, newDataType)); } return(ConvertConstantStructuralByPropertyOrderInstantiation(originalValue, oldDataType, newDataType)); }
/// <summary> /// Converts a constant by instantiating the new data type based on the converted values of the original data type's properties, in declaration order. /// </summary> /// <param name="originalValue">Original value to convert.</param> /// <param name="oldDataType">Old data type.</param> /// <param name="newDataType">New data type.</param> /// <returns>Converted constant.</returns> /// <remarks>Anonymous types and tuple data types obey to the prerequisite of property ordering according to the constructor parameter list.</remarks> protected object ConvertConstantStructuralByPropertyOrderInstantiation(object originalValue, StructuralDataType oldDataType, StructuralDataType newDataType) { var oldProperties = oldDataType.Properties; var newProperties = newDataType.Properties; var n = newProperties.Count; var args = new object[n]; for (var i = 0; i < n; i++) { var oldProperty = oldProperties[i]; var newProperty = newProperties[i]; var oldValue = oldProperty.GetValue(originalValue); var newValue = ConvertConstant(oldValue, oldProperty.Type, newProperty.Type); args[i] = newValue; } var result = newDataType.CreateInstance(args); return(result); }
/// <summary> /// Visit new expressions for structural data types and transforms occurrences of entity types. /// </summary> /// <param name="node">The expression to transform.</param> /// <param name="oldType">The original type of the new expression.</param> /// <param name="newDataType">The new data type of the new expression.</param> /// <returns>The new expression with the original type replaced with the new type.</returns> protected virtual Expression VisitNewStructuralDataType(NewExpression node, Type oldType, StructuralDataType newDataType) { var newType = newDataType.UnderlyingType; var oldConstructor = node.Constructor; var oldParameters = oldConstructor.GetParameters(); var count = oldParameters.Length; var oldArguments = node.Arguments; var memberAssignments = new Dictionary <MemberInfo, Expression>(count); for (var i = 0; i < count; i++) { var oldParameter = oldParameters[i]; var oldArgument = oldArguments[i]; var mapping = oldParameter.GetCustomAttribute <MappingAttribute>(inherit: false); Debug.Assert(mapping != null); var newProperty = newType.GetProperty(mapping.Uri); Debug.Assert(newProperty != null); if (memberAssignments.ContainsKey(newProperty)) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Parameter '{0}' on the constructor of '{1}' has the same mapping attribute '{2}' of another parameter.", oldParameter.Name, oldType, mapping.Uri)); } var newArgument = Visit(oldArgument); memberAssignments[newProperty] = newArgument; } return(CreateNewExpression(newType, memberAssignments)); }
/// <summary> /// Checks if a constant can be converted from one structural data type to another data type. This method is called when the structural type kind needs to change. /// </summary> /// <param name="newValue">Value to convert.</param> /// <param name="oldDataType">Old data type.</param> protected abstract void CheckConstantStructuralCore(Object newValue, StructuralDataType oldDataType);
/// <summary> /// Visit member initializer expressions for structural data types and transforms occurrences of entity types. /// </summary> /// <param name="node">The expression to transform.</param> /// <param name="oldType">The original type of the member initializer expression.</param> /// <param name="newDataType">The new data type of the member initializer expression.</param> /// <returns>The member initializer expression with the original type replaced with the new type.</returns> protected virtual Expression VisitMemberInitStructuralDataType(MemberInitExpression node, Type oldType, StructuralDataType newDataType) { var newType = newDataType.UnderlyingType; var memberAssignments = default(IDictionary <MemberInfo, Expression>); var newExpression = Visit(node.NewExpression); switch (newExpression.NodeType) { case ExpressionType.MemberInit: memberAssignments = GetMemberAssignments((MemberInitExpression)newExpression); break; case ExpressionType.New: memberAssignments = GetMemberAssignments((NewExpression)newExpression); break; } return(CreateNewExpressionFromBindings(newType, node.Bindings, memberAssignments)); }
/// <summary> /// Checks if a constant can be converted based on the converted values of the original data type's properties, in declaration order. /// </summary> /// <param name="newValue">Value to convert.</param> /// <param name="oldDataType">Old data type.</param> /// <remarks>Anonymous types and tuple data types obey to the prerequisite of property ordering according to the constructor parameter list.</remarks> protected void CheckConstantStructuralByPropertyOrderInstantiation(Object newValue, StructuralDataType oldDataType) { var oldProperties = oldDataType.Properties; if (!Helpers.TryGetStructuralPropertyTypes(newValue.TypeSlim, out var newPropertyTypes)) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Cannot convert object of type '{0}' to type '{1}'.", oldDataType.UnderlyingType, newValue.TypeSlim)); } var n = newPropertyTypes.Count; for (var i = 0; i < n; i++) { var oldProperty = oldProperties[i]; var newPropertyType = newPropertyTypes[i]; var oldValue = oldProperty.GetValue(newValue.Value); var newPropertyValue = Object.Create(oldValue, newPropertyType, oldProperty.Type.UnderlyingType); CheckConstant(newPropertyValue, oldProperty.Type); } }
protected override DataType VisitStructural(StructuralDataType type) { _assertStructural(type.UnderlyingType); return(base.VisitStructural(type)); }