using System; using System.Collections.Concurrent; using System.Data; using System.Data.Common; using System.Linq.Expressions; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Dapper.ProviderTools.Internal; namespace Dapper.ProviderTools { /// /// Provides provider-agnostic access to bulk-copy services /// public abstract class BulkCopy : IDisposable { /// /// Attempt to create a BulkCopy instance for the connection provided /// public static BulkCopy? TryCreate(DbConnection connection) { if (connection == null) return null; var type = connection.GetType(); if (!s_bcpFactory.TryGetValue(type, out var func)) { s_bcpFactory[type] = func = CreateBcpFactory(type); } var obj = func?.Invoke(connection); return DynamicBulkCopy.Create(obj); } /// /// Create a BulkCopy instance for the connection provided /// public static BulkCopy Create(DbConnection connection) { var bcp = TryCreate(connection); if (bcp == null) { if (connection == null) throw new ArgumentNullException(nameof(connection)); throw new NotSupportedException("Unable to create BulkCopy for " + connection.GetType().FullName); } return bcp; } ///// ///// Provide an external registration for a given connection type ///// //public static void Register(Type type, Func factory) //{ // throw new NotImplementedException(); //} private static readonly ConcurrentDictionary?> s_bcpFactory = new ConcurrentDictionary?>(); internal static Func? CreateBcpFactory(Type connectionType) { try { var match = Regex.Match(connectionType.Name, "^(.+)Connection$"); if (match.Success) { var prefix = match.Groups[1].Value; var bcpType = connectionType.Assembly.GetType($"{connectionType.Namespace}.{prefix}BulkCopy"); if (bcpType != null) { var ctor = bcpType.GetConstructor(new[] { connectionType }); if (ctor == null) return null; var p = Expression.Parameter(typeof(DbConnection), "conn"); var body = Expression.New(ctor, Expression.Convert(p, connectionType)); return Expression.Lambda>(body, p).Compile(); } } } catch { } return null; } /// /// Name of the destination table on the server. /// public abstract string DestinationTableName { get; set; } /// /// Write a set of data to the server /// public abstract void WriteToServer(DataTable source); /// /// Write a set of data to the server /// public abstract void WriteToServer(DataRow[] source); /// /// Write a set of data to the server /// public abstract void WriteToServer(IDataReader source); /// /// Write a set of data to the server /// public abstract Task WriteToServerAsync(DbDataReader source, CancellationToken cancellationToken = default); /// /// Write a set of data to the server /// public abstract Task WriteToServerAsync(DataTable source, CancellationToken cancellationToken = default); /// /// Write a set of data to the server /// public abstract Task WriteToServerAsync(DataRow[] source, CancellationToken cancellationToken = default); /// /// Add a mapping between two columns by name /// public abstract void AddColumnMapping(string sourceColumn, string destinationColumn); /// /// Add a mapping between two columns by position /// public abstract void AddColumnMapping(int sourceColumn, int destinationColumn); /// /// The underlying untyped object providing the bulk-copy service /// public abstract object Wrapped { get; } /// /// Enables or disables streaming from a data-reader /// public bool EnableStreaming { get; set; } /// /// Number of rows in each batch /// public int BatchSize { get; set; } /// /// Number of seconds for the operation to complete before it times out. /// public int BulkCopyTimeout { get; set; } /// /// Release any resources associated with this instance /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Release any resources associated with this instance /// protected abstract void Dispose(bool disposing); } }