【问题标题】:Connecting to a SQLLite db with SqlBulkCopy使用 SqlBulkCopy 连接到 SQLite 数据库
【发布时间】:2017-11-08 21:42:57
【问题描述】:

我正在用 c# 为一个函数编写单元测试,该函数负责使用 System.Data.SqlClient.SqlBulkCopy 将 DataTable 复制到数据库服务器。

我使用 SQLLite 进行单元测试,并希望使用 SqlBulkCopy 连接到内存数据库中的 SQLLite,然后将该测试数据批量复制到 SQLLite 数据库中。

但是,我似乎无法正确获取连接字符串。

我最初尝试过

var bcp = new SqlBulkCopy("FullUri=file::memory:?cache=shared")

然后

var bcp = new SqlBulkCopy("Data Source=:memory:;Cache=Shared")

哪个不认识Cache 所以我尝试了

var bcp = new SqlBulkCopy("Data Source=:memory:")

出于绝望,只是在尝试连接数据库时超时。

我想在这里完成的事情可能吗?如果是,有人可以帮我处理连接字符串吗?

【问题讨论】:

    标签: c# sqlite unit-testing sqlbulkcopy


    【解决方案1】:

    您不能将SqlBulkCopy 用于 SQLite。 SqlBulkCopy 已为 SQL Server 完成。

    通常,显着提高 SQLite 性能的诀窍是确保使用事务。


    免责声明:我是.NET Bulk Operations的所有者

    此库不是免费的,但允许您轻松执行和自定义所有批量操作:

    • 批量插入
    • 批量删除
    • 批量更新
    • 批量合并

    例子

    // Easy to use
    var bulk = new BulkOperation(connection);
    bulk.BulkInsert(dt);
    bulk.BulkUpdate(dt);
    bulk.BulkDelete(dt);
    bulk.BulkMerge(dt);
    
    // Easy to customize
    var bulk = new BulkOperation<Customer>(connection);
    bulk.BatchSize = 1000;
    bulk.ColumnInputExpression = c => new { c.Name,  c.FirstName };
    bulk.ColumnOutputExpression = c => c.CustomerID;
    bulk.ColumnPrimaryKeyExpression = c => c.Code;
    bulk.BulkMerge(customers);
    

    编辑:回复评论

    我想从 SQLite 加载一个数据表,然后将其“批量复制”到其他数据库中

    这种情况是可能的,但需要2个连接

    DbConnection sourceConnection = // connection from the source
    DbConnection destinationConnection = // connection from the destination
    
    // Fill the DataTable using the sourceConnection
    dt = ...;
    
    // BulkInsert using the destinationConnection
    var bulk = new BulkOperation(destinationConnection);
    bulk.BulkInsert(dt);
    

    【讨论】:

    • 我想从 SQLite 加载一个数据表,然后在其他数据库(MySQL、Oracle、Postgre 和 SQL Server)中“批量复制”它。我认为 BulkOperation 是可能的,但代码示例会有很大帮助
    • 我已尝试使用 Z.BulkOperations NuGet 包将 100,000 条简单记录(Id INTEGER、Name TEXT)插入 SQLite 数据库。在我的机器上使用 BulkOperation.BulkInsert(DataTable 或实体)比通过 SQLiteCommand 手动插入记录慢大约 2 倍。这太糟糕了,因为我希望有一些解决方案可以让我比使用 SQLiteCommand 更快地将记录批量插入 SQLite 数据库。
    • 您好,David,如果您可以在此处提供可运行的解决方案:info@zzzprojects.com,我们将很乐意查看它并找出您的情况为什么会变慢。
    【解决方案2】:

    答案是您无法将 SqlBulkCopy 连接到 SQLite 实例。

    我为解决我的问题(对使用 SqlBulkCopy 的代码的一部分进行单元测试)所做的工作是围绕 SqlBulkCopy 创建一个包装器,该包装器是使用 SqlBulkCopy 实现的,用于生产代码,并在测试代码中使用模拟批量复制。有效解耦对 SqlBulkCopy 本身的依赖。

    具体来说,我创建了

    public interface IBulkCopy : IDisposable {
        string DestinationTableName { get; set; }
        void CreateColumnMapping(string from, string to);
        Task WriteToServerAsync(DataTable dt);
    }
    

    然后,我将其实现为

    public class SQLBulkCopy : IBulkCopy { 
    
        private SqlBulkCopy _sbc;
    
        public string DestinationTableName {
            get { return _sbc.DestinationTableName; }
            set { _sbc.DestinationTableName = value; }
        }
    
        public SQLBulkCopy(IDBContext ctx) { 
            _sbc = new SqlBulkCopy((SqlConnection)ctx.GetConnection());
        }
    
        public void CreateColumnMapping(string from, string to) {
            _sbc.ColumnMappings.Add(new SqlBulkCopyColumnMapping(from, to));
        }
    
        public Task WriteToServerAsync(DataTable dt) {
            return _sbc.WriteToServerAsync(dt);
        }
    
    }
    

    在我的测试实用程序中,我只用插入模拟了“批量复制”:

    class MockBulkCopy : IBulkCopy {
    
        private IDBContext _context;
    
        public MockBulkCopyHelper(IDBContext context) {
            _context = context;
        }
    
        public string DestinationTableName { get; set; }
    
        public void CreateColumnMapping(string fromName, string toName) {
            //We don't need a column mapping for raw SQL Insert statements.
            return;
        }
    
        public virtual Task WriteToServerAsync(DataTable dt) {
            return Task.Run(() => {
                using (var cn = _context.GetConnection()) {
                    using (var cmd = cn.CreateCommand()) {
                        cmd.CommandText = $"INSERT INTO {DestinationTableName}({GetCsvColumnList(dt)}) VALUES {GetCsvValueList(dt)}";
                        cmd.ExecuteNonQuery();
                    }
                }
            });
        }
    

    GetCsvColumnListGetCsvValueList 我是作为辅助函数实现的。

    【讨论】:

    • 那么你在这里实际测试了什么?我相信它只测试所有列映射是否正确完成。如果是这种情况,您可以将该功能提取到一个单独的类中并为此编写单元测试,而无需单独使用批量复制操作类。这会更容易,IMO。
    • 是的,这很好。这不是测试驱动的开发,这里的想法只是为了编写一些单元测试而不修改它为了截止日期而测试的代码。理想情况下我会走你建议的路线,但我正在使用的代码与 sbc 紧密耦合,所以我必须模拟这个变通方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-26
    • 2016-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多