Example #1
0
        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;
                }
            }
        }
Example #2
0
        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);
        }