public async Task<ISqlSchemaMappingCompilerOutput> CompileAsync(
            ISqlSchemaMappingCompilerInput input,
            ICompilerMessageBuilder messageProvider = null)
        {
            if(ReferenceEquals(input, null))
                throw new ArgumentNullException(nameof(input));

            messageProvider = messageProvider ?? new CompilerMessageBuilder();

            try
            {
                this.m_output = new SqlSchemaMappingCompilerOutput();
                var args = new XArgs(this.m_output, new Policies(), new CompilerMessageBuilder());
                
                                
                this.m_input = new SqlSchemaMappingCompilerInputAdapter(input, messageProvider, this.m_output);
                if (this.m_output.Errors.Any()) return this.m_output;

                this.m_output.DataRowInterfaces.AddRange(m_input.DataRowInterfaces);
                this.m_output.OperationRequestContracts.AddRange(m_input.OperationResponseInterfaces);
                this.m_output.OperationResponseContracts.AddRange(m_input.OperationResponseInterfaces);
                
                await this.CompileTableTypesAsync(args);

                return this.m_output;
            }
            finally
            {
                this.m_input = null;
                this.m_output = null;
                this.m_interfaceNames.Clear();
            }            
        }
        private async Task CompileTableTypesAsync(XArgs args)
        {
            var selectCommand = new SqlCommand() { CommandType = CommandType.Text };
            var adapter = new SqlDataAdapter(selectCommand);
            foreach (var selector in this.m_input.TableTypeSelectors)
            {
                string fullName = $"[{selector.Schema}].[{selector.Name}]";
                selectCommand.CommandText = $"declare @schema as {fullName}; select * from @schema";
                var schema = new DataTable();
                var fillSchemaTask = this.m_session.FillSchemaAsync(adapter, schema, SchemaType.Source);

                var tableTypeInfo = new TableInfo(fullName);
                foreach (var mapping in selector.DataRowMappings)
                {
                    tableTypeInfo.InterfaceNames.Add(mapping.InterfaceName);
                }

                await fillSchemaTask;

                IEnumerable<DataColumnInfo> sourceFields = schema.Columns.OfType<DataColumn>().Select(dc => new DataColumnInfo(dc));


                foreach (DataColumn column in schema.Columns)
                {
                    var columnInfo = new DataColumnInfo(column);
                    foreach (var dataRowMapping in selector.DataRowMappings)
                    {
                        try
                        {
                            var interfaceProperty = dataRowMapping.GetInterfacePropertyNamesPair(column);
                            columnInfo.Mappings.Add(interfaceProperty);
                        }
                        catch (InterfacePropertyNotFoundException ex)
                        {
                            throw new NotImplementedException();
                        }
                        catch (InterfacePropertyTypeMismatchException ex)
                        {
                            args.Listener.Error(args.MessageBuilder.InterfacePropertyTypeMismatch(ex));
                        }
                        catch (PropertyTypeNotSupportedException ex)
                        {
                            args.Listener.Error(args.MessageBuilder.PropertyTypeNotSupported(ex.PropertyTypeName));
                        }
                        catch (Exception ex)
                        {
                            throw;
                        }
                    }

                    tableTypeInfo.Columns.Add(columnInfo);
                }

                this.m_output.TableTypeInfos.Add(tableTypeInfo);
            }
        }