mirror of
https://github.com/DapperLib/Dapper.git
synced 2026-10-17 05:12:13 +08:00
aecffecae1
* attempt NRT makeover - annotate NRTs on GridReader API - add protected access to some of the GridReader internals - switch GridReader callbacks to be more type-independent docs lib updates; deal with yellow warnings and test failures (SQL server certs and cancellation surfacing differently) fix break simplify proposed GridReader changes to protected OnBeforeGrid and OnAfterGrid[Async] Builds: Bump library versions (#1935) Tests: Upgrade dependencies for Dependabot (#1936) I'll do a pass at all of these later, but getting CVE versions out of the pipe. Resolving 4 alerts here: https://github.com/DapperLib/Dapper/security/dependabot merge and lib updates allow Identity to be constructed on-the-fly inside GridReader fix build warnings include readme rev minor - enable [SkipLocalsInit] everywhere - use NET5_0_OR_GREATER for remoting check # Conflicts: # Dapper/CommandDefinition.cs # Dapper/DefaultTypeMap.cs # Dapper/SqlMapper.Async.cs # Dapper/SqlMapper.IDataReader.cs # Dapper/SqlMapper.Link.cs # Dapper/SqlMapper.cs # Directory.Build.props * shipped, not unshipped * fixup SqlBuilder; use is null / is not null * use central package management (#1949) * Nullable test tweaks * Nuget: let's just remove it! * 2 test fixes from review * fix FindExplicitConstructor * add GetPropertySetterOrThrow * fix NRT on FindConstructor * DapperRow: value is object? * use NotNullWhen * make constructor problem more obvious * test fixes --------- Co-authored-by: Nick Craver <nrcraver@gmail.com>
154 lines
6.2 KiB
C#
154 lines
6.2 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Provides provider-agnostic access to bulk-copy services
|
|
/// </summary>
|
|
public abstract class BulkCopy : IDisposable
|
|
{
|
|
/// <summary>
|
|
/// Attempt to create a BulkCopy instance for the connection provided
|
|
/// </summary>
|
|
public static BulkCopy? TryCreate(DbConnection connection)
|
|
{
|
|
if (connection is 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a BulkCopy instance for the connection provided
|
|
/// </summary>
|
|
public static BulkCopy Create(DbConnection connection)
|
|
{
|
|
var bcp = TryCreate(connection);
|
|
if (bcp is null)
|
|
{
|
|
if (connection is null) throw new ArgumentNullException(nameof(connection));
|
|
throw new NotSupportedException("Unable to create BulkCopy for " + connection.GetType().FullName);
|
|
}
|
|
return bcp;
|
|
}
|
|
|
|
///// <summary>
|
|
///// Provide an external registration for a given connection type
|
|
///// </summary>
|
|
//public static void Register(Type type, Func<DbConnection, BulkCopy> factory)
|
|
//{
|
|
// throw new NotImplementedException();
|
|
//}
|
|
|
|
private static readonly ConcurrentDictionary<Type, Func<DbConnection, object>?> s_bcpFactory
|
|
= new ConcurrentDictionary<Type, Func<DbConnection, object>?>();
|
|
|
|
internal static Func<DbConnection, object>? 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 is not null)
|
|
{
|
|
var ctor = bcpType.GetConstructor(new[] { connectionType });
|
|
if (ctor is null) return null;
|
|
|
|
var p = Expression.Parameter(typeof(DbConnection), "conn");
|
|
var body = Expression.New(ctor, Expression.Convert(p, connectionType));
|
|
return Expression.Lambda<Func<DbConnection, object>>(body, p).Compile();
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Name of the destination table on the server.
|
|
/// </summary>
|
|
public abstract string DestinationTableName { get; set; }
|
|
/// <summary>
|
|
/// Write a set of data to the server
|
|
/// </summary>
|
|
public abstract void WriteToServer(DataTable source);
|
|
/// <summary>
|
|
/// Write a set of data to the server
|
|
/// </summary>
|
|
public abstract void WriteToServer(DataRow[] source);
|
|
/// <summary>
|
|
/// Write a set of data to the server
|
|
/// </summary>
|
|
public abstract void WriteToServer(IDataReader source);
|
|
/// <summary>
|
|
/// Write a set of data to the server
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Grandfathered")]
|
|
public abstract Task WriteToServerAsync(DbDataReader source, CancellationToken cancellationToken = default);
|
|
/// <summary>
|
|
/// Write a set of data to the server
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Grandfathered")]
|
|
public abstract Task WriteToServerAsync(DataTable source, CancellationToken cancellationToken = default);
|
|
/// <summary>
|
|
/// Write a set of data to the server
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Grandfathered")]
|
|
public abstract Task WriteToServerAsync(DataRow[] source, CancellationToken cancellationToken = default);
|
|
/// <summary>
|
|
/// Add a mapping between two columns by name
|
|
/// </summary>
|
|
public abstract void AddColumnMapping(string sourceColumn, string destinationColumn);
|
|
/// <summary>
|
|
/// Add a mapping between two columns by position
|
|
/// </summary>
|
|
public abstract void AddColumnMapping(int sourceColumn, int destinationColumn);
|
|
/// <summary>
|
|
/// The underlying untyped object providing the bulk-copy service
|
|
/// </summary>
|
|
public abstract object Wrapped { get; }
|
|
|
|
/// <summary>
|
|
/// Enables or disables streaming from a data-reader
|
|
/// </summary>
|
|
public bool EnableStreaming { get; set; }
|
|
/// <summary>
|
|
/// Number of rows in each batch
|
|
/// </summary>
|
|
public int BatchSize { get; set; }
|
|
/// <summary>
|
|
/// Number of seconds for the operation to complete before it times out.
|
|
/// </summary>
|
|
public int BulkCopyTimeout { get; set; }
|
|
|
|
/// <summary>
|
|
/// Release any resources associated with this instance
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release any resources associated with this instance
|
|
/// </summary>
|
|
protected abstract void Dispose(bool disposing);
|
|
}
|
|
}
|