private MessageTypeHandler <T> BuildMessageTypeHandler <T>(BaseTypeRegistration baseTypeRegistration) { Type messageType = typeof(T); var typeRegistration = (TypeRegistration <T>)baseTypeRegistration; var descriptorProperty = messageType.GetProperties(BindingFlags.Public | BindingFlags.Static) .FirstOrDefault(x => x.Name == "Descriptor"); var descriptor = (MessageDescriptor)descriptorProperty.GetValue(null); var fields = descriptor.Fields.InFieldNumberOrder(); var fieldByFieldNumber = fields .Select((field) => new KeyValuePair <int, FieldDescriptor>(field.FieldNumber, field)) .ToDictionary(x => x.Key, x => x.Value); var idParameter = Expression.Parameter(typeof(ulong), "identity"); var entityParameter = Expression.Parameter(messageType, "entity"); var factory = Expression.Lambda <Func <ulong, T> >(typeRegistration.FactoryExpressionBuilder(idParameter), idParameter).Compile(); var keyGetter = Expression.Lambda <Func <T, ulong> >(typeRegistration.KeyGetterExpressionBuilder(entityParameter), entityParameter).Compile(); return(new MessageTypeHandler <T>() { TypeRegistration = typeRegistration, Factory = factory, KeyGetter = keyGetter, FieldsByParameterNumber = fields .Where(x => string.Compare(x.Name, baseTypeRegistration.KeyMember.Name, true) != 0) .Select(FieldHandlerBuilder(typeRegistration)) .ToDictionary(x => x.Key, x => x.Value) }); }
private void SetTypeRegistrationForType(BaseTypeRegistration registration) { var reflectedMethod = typeof(SchemaRegistry) .GetMethod($"{nameof(SchemaRegistry.SetTypeRegistrationForTypeGeneric)}", PrivateInstance) .MakeGenericMethod(registration.Type); reflectedMethod.Invoke(this, new object[] { registration }); }
private void SetTypeRegistrationForTypeGeneric <T>(BaseTypeRegistration registrationInfo) { var type = typeof(T); var keyedRegistrationInfo = registrationInfo as TypeRegistration <T>; Func <Expression, Expression> keyGetterExpressionBuilder; Func <Expression, Expression> factoryExpressionBuilder; MemberInfo keyMember = registrationInfo.KeyMember; if (keyedRegistrationInfo != null) { if (keyMember == null || keyedRegistrationInfo.KeyGetterExpressionBuilder == null || keyedRegistrationInfo.FactoryExpressionBuilder == null) { throw new InvalidOperationException( $"If you provide a custom {nameof(TypeRegistration<int>.KeyGetterExpressionBuilder)} " + $"or {nameof(TypeRegistration<int>.KeyGetterExpressionBuilder)}, you must include both, as well as " + $"the {nameof(TypeRegistration<int>.KeyMember)} parameter." ); } keyGetterExpressionBuilder = keyedRegistrationInfo.KeyGetterExpressionBuilder; factoryExpressionBuilder = keyedRegistrationInfo.FactoryExpressionBuilder; } else { try { var constructor = type.GetConstructors().First(x => x.GetParameters().Count() == 0); keyMember = keyMember != null ? keyMember : (MemberInfo)type.GetProperty("Id"); var constructInstance = Expression.New(constructor); keyGetterExpressionBuilder = (parameter) => Expression.PropertyOrField(parameter, "Id"); factoryExpressionBuilder = (parameter) => Expression.MemberInit(constructInstance, Expression.Bind(keyMember, parameter)); } catch (Exception ex) { throw new InvalidOperationException( $"Unable to create {nameof(keyGetterExpressionBuilder)} and {nameof(factoryExpressionBuilder)} for type {type.FullName}. " + $"Either give it a ulong property named `Id` or specify getter and setter with an IKeyedTypeRegistration.", ex ); } } var registration = new TypeRegistration <T>() { Type = type, TypeId = registrationInfo.TypeId, AggregateId = registrationInfo.AggregateId, KeyMember = keyMember, KeyGetterExpressionBuilder = keyGetterExpressionBuilder, FactoryExpressionBuilder = factoryExpressionBuilder, }; ByType <T> .Value = registration; }
private Func <FieldDescriptor, int, KeyValuePair <ushort, FieldHandler> > FieldHandlerBuilder(BaseTypeRegistration typeRegistration) { return((discriptor, fieldIndex) => { var messageType = typeRegistration.Type; var titleCaseName = $"{discriptor.Name.Substring(0, 1).ToUpper()}{discriptor.Name.Substring(1)}"; var property = messageType.GetProperty(titleCaseName); Type propertyOuterType = null; Type propertyType; var isRepeated = property.PropertyType.GetTypeInfo().IsGenericType ? property.PropertyType.GetGenericTypeDefinition() == typeof(RepeatedField <>) : false; if (isRepeated) { propertyOuterType = property.PropertyType; propertyType = propertyOuterType.GetGenericArguments()[0]; } else { propertyType = property.PropertyType; } var isReference = typeof(IMessage).IsAssignableFrom(propertyType); var isPrimitive = _primitiveSerializers.ContainsKey(propertyType); if (!isReference && !isPrimitive) { throw new InvalidOperationException($"Unable to find a primitive serializer for type {property.PropertyType.FullName}."); } var definition = new FieldHandlerDefinition() { FieldDescriptor = discriptor, MessageTypeRegistration = typeRegistration, Property = property, MessageType = messageType, PropertyOuterType = propertyOuterType, PropertyType = propertyType, IsReference = isReference, IsRepeated = isRepeated, PrimitiveSerializer = isPrimitive ? _primitiveSerializers[propertyType] : null }; FieldHandler toReturn; if (isReference) { var buildBuildReferenceField = typeof(ProtobufDatomSerializer) .GetMethod($"{nameof(ProtobufDatomSerializer.BuildReferenceField)}", PrivateInstance) .MakeGenericMethod(messageType, propertyType); toReturn = buildBuildReferenceField.Invoke(this, new object[] { definition }) as FieldHandler; } else { var buildBuildSimpleField = typeof(ProtobufDatomSerializer) .GetMethod($"{nameof(ProtobufDatomSerializer.BuildSimpleField)}", PrivateInstance) .MakeGenericMethod(messageType, propertyType); toReturn = buildBuildSimpleField.Invoke(this, new object[] { definition }) as FieldHandler; } Func <byte[], string> debugDeserializer; if (isPrimitive) { var valueParameter = Expression.Parameter(typeof(byte[]), "value"); var deserialize = _primitiveSerializers[propertyType].GetDeserializeExpression(valueParameter); var toStringMethod = propertyType.GetMethods().First(x => x.Name == "ToString"); var deserializeToString = Expression.Call(deserialize, toStringMethod); debugDeserializer = Expression.Lambda <Func <byte[], string> >(deserializeToString, valueParameter).Compile(); } else { debugDeserializer = (value) => BitConverter.ToUInt64(value, 0).ToString(); } _debugFormatters[new Tuple <ushort, ushort>(typeRegistration.TypeId, (ushort)discriptor.FieldNumber)] = (datom) => { var valueFormat = isReference ? $"{propertyType.Name}[{{0}}]" : "{0}"; var fieldArrayIndex = isRepeated ? $"[{datom.ParameterArrayIndex}]": string.Empty; return $"TX[{datom.TransactionId}] {datom.Action} {typeRegistration.Type.Name}[{datom.Identity}]" + $".{discriptor.Name}{fieldArrayIndex} = {string.Format(valueFormat, debugDeserializer(datom.Value))}"; }; return new KeyValuePair <ushort, FieldHandler>((ushort)discriptor.FieldNumber, toReturn); }); }