static CompositeMemberHandler <T>[] CreateMemberHandlers(PostgresCompositeType pgType, ConnectorTypeMapper typeMapper, INpgsqlNameTranslator nameTranslator) { var pgFields = pgType.Fields; var clrType = typeof(T); var clrMemberHandlers = new CompositeMemberHandler <T> [pgFields.Count]; var clrMemberHandlerCount = 0; var clrMemberHandlerType = IsValueType <T> .Value ? typeof(CompositeStructMemberHandler <,>) : typeof(CompositeClassMemberHandler <,>); foreach (var clrProperty in clrType.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { CreateMemberHandler(clrProperty, clrProperty.PropertyType); } foreach (var clrField in clrType.GetFields(BindingFlags.Instance | BindingFlags.Public)) { CreateMemberHandler(clrField, clrField.FieldType); } if (clrMemberHandlerCount != pgFields.Count) { var notMappedFields = string.Join(", ", clrMemberHandlers .Select((member, memberIndex) => member == null ? $"'{pgFields[memberIndex].Name}'" : null) .Where(member => member != null)); throw new InvalidOperationException($"PostgreSQL composite type {pgType.DisplayName} contains fields {notMappedFields} which could not match any on CLR type {clrType.Name}"); } return(clrMemberHandlers); void CreateMemberHandler(MemberInfo clrMember, Type clrMemberType) { var attr = clrMember.GetCustomAttribute <PgNameAttribute>(); var name = attr?.PgName ?? nameTranslator.TranslateMemberName(clrMember.Name); for (var pgFieldIndex = pgFields.Count - 1; pgFieldIndex >= 0; --pgFieldIndex) { var pgField = pgFields[pgFieldIndex]; if (pgField.Name != name) { continue; } if (clrMemberHandlers[pgFieldIndex] != null) { throw new AmbiguousMatchException($"Multiple class members are mapped to the '{pgField.Name}' field."); } if (!typeMapper.TryGetByOID(pgField.Type.OID, out var handler)) { throw new NpgsqlException($"PostgreSQL composite type {pgType.DisplayName} has field {pgField.Type.DisplayName} with an unknown type (OID = {pgField.Type.OID})."); } clrMemberHandlerCount++; clrMemberHandlers[pgFieldIndex] = (CompositeMemberHandler <T>)Activator.CreateInstance( clrMemberHandlerType.MakeGenericType(clrType, clrMemberType), BindingFlags.Instance | BindingFlags.Public, binder: null, args: new object[] { clrMember, pgField.Type, handler }, culture: null) !; break; } } }
static CompositeConstructorHandler <T>?CreateConstructorHandler(PostgresCompositeType pgType, ConnectorTypeMapper typeMapper, INpgsqlNameTranslator nameTranslator) { var pgFields = pgType.Fields; var clrType = typeof(T); ConstructorInfo?clrDefaultConstructor = null; foreach (var clrConstructor in clrType.GetConstructors()) { var clrParameters = clrConstructor.GetParameters(); if (clrParameters.Length != pgFields.Count) { if (clrParameters.Length == 0) { clrDefaultConstructor = clrConstructor; } continue; } var clrParameterHandlerCount = 0; var clrParametersMapped = new ParameterInfo[pgFields.Count]; foreach (var clrParameter in clrParameters) { var attr = clrParameter.GetCustomAttribute <PgNameAttribute>(); var name = attr?.PgName ?? (clrParameter.Name is string clrName ? nameTranslator.TranslateMemberName(clrName) : null); if (name is null) { break; } for (var pgFieldIndex = pgFields.Count - 1; pgFieldIndex >= 0; --pgFieldIndex) { var pgField = pgFields[pgFieldIndex]; if (pgField.Name != name) { continue; } if (clrParametersMapped[pgFieldIndex] != null) { throw new AmbiguousMatchException($"Multiple constructor parameters are mapped to the '{pgField.Name}' field."); } clrParameterHandlerCount++; clrParametersMapped[pgFieldIndex] = clrParameter; break; } } if (clrParameterHandlerCount < pgFields.Count) { continue; } var clrParameterHandlers = new CompositeParameterHandler[pgFields.Count]; for (var pgFieldIndex = 0; pgFieldIndex < pgFields.Count; ++pgFieldIndex) { var pgField = pgFields[pgFieldIndex]; if (!typeMapper.TryGetByOID(pgField.Type.OID, out var handler)) { throw new NpgsqlException($"PostgreSQL composite type {pgType.DisplayName} has field {pgField.Type.DisplayName} with an unknown type (OID = {pgField.Type.OID})."); } var clrParameter = clrParametersMapped[pgFieldIndex]; var clrParameterHandlerType = typeof(CompositeParameterHandler <>) .MakeGenericType(clrParameter.ParameterType); clrParameterHandlers[pgFieldIndex] = (CompositeParameterHandler)Activator.CreateInstance( clrParameterHandlerType, BindingFlags.Instance | BindingFlags.Public, binder: null, args: new object[] { handler, clrParameter }, culture: null) !; } return(CompositeConstructorHandler <T> .Create(pgType, clrConstructor, clrParameterHandlers)); } if (clrDefaultConstructor is null && !clrType.IsValueType) { throw new InvalidOperationException($"No parameterless constructor defined for type '{clrType}'."); } return(null); }