/// <inheritdoc />
        public Task BulkInsertAsync <T>(DbContext ctx,
                                        IEntityType entityType,
                                        IEnumerable <T> entities,
                                        SqlBulkInsertOptions options,
                                        CancellationToken cancellationToken = default)
            where T : class
        {
            if (entityType == null)
            {
                throw new ArgumentNullException(nameof(entityType));
            }
            if (entityType.IsQueryType)
            {
                throw new InvalidOperationException("The provided 'entities' are of 'Query Type' that do not have a table to insert into. Use the other overload that takes the 'tableName' as a parameter.");
            }

            var relational = entityType.Relational();

            return(BulkInsertAsync(ctx, entityType, entities, relational.Schema, relational.TableName, options, cancellationToken));
        }
        /// <inheritdoc />
        public async Task BulkInsertAsync <T>(DbContext ctx,
                                              IEntityType entityType,
                                              IEnumerable <T> entities,
                                              string schema,
                                              string tableName,
                                              SqlBulkInsertOptions options,
                                              CancellationToken cancellationToken = default)
            where T : class
        {
            if (ctx == null)
            {
                throw new ArgumentNullException(nameof(ctx));
            }
            if (entities == null)
            {
                throw new ArgumentNullException(nameof(entities));
            }
            if (tableName == null)
            {
                throw new ArgumentNullException(nameof(tableName));
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var factory    = ctx.GetService <IEntityDataReaderFactory>();
            var properties = GetPropertiesForInsert(options.EntityMembersProvider, entityType);
            var sqlCon     = (SqlConnection)ctx.Database.GetDbConnection();
            var sqlTx      = (SqlTransaction)ctx.Database.CurrentTransaction?.GetDbTransaction();

            using (var reader = factory.Create(entities, properties))
                using (var bulkCopy = new SqlBulkCopy(sqlCon, options.SqlBulkCopyOptions, sqlTx))
                {
                    bulkCopy.DestinationTableName = $"[{tableName}]";

                    if (!String.IsNullOrWhiteSpace(schema))
                    {
                        bulkCopy.DestinationTableName = $"[{schema}].{bulkCopy.DestinationTableName}";
                    }

                    bulkCopy.EnableStreaming = options.EnableStreaming;

                    if (options.BulkCopyTimeout.HasValue)
                    {
                        bulkCopy.BulkCopyTimeout = (int)options.BulkCopyTimeout.Value.TotalSeconds;
                    }

                    if (options.BatchSize.HasValue)
                    {
                        bulkCopy.BatchSize = options.BatchSize.Value;
                    }

                    foreach (var property in reader.Properties)
                    {
                        var index = reader.GetPropertyIndex(property);
                        bulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(index, property.Relational().ColumnName));
                    }

                    await ctx.Database.OpenConnectionAsync(cancellationToken).ConfigureAwait(false);

                    await bulkCopy.WriteToServerAsync(reader, cancellationToken).ConfigureAwait(false);
                }
        }