通过 streamreader 读取大的分号分隔文件并插入 sql db in vb.net

reading large semicolon separated files via streamreader and inserting in sql db in vb.net

我需要读取大型 csv 文件并将它们插入 SQL,我的想法是使用流读取器并逐行读取文件,因为如果我将内容存储在变量中,程序就会崩溃。这就是我的想法:

using FileStream fs  
Dim list as String
 Try
 Dim MyFile as String = ("C:\Test.txt")
            Using fs as FileStream = File.Open(MyFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None) 'file is opened in a protected mode
               firstline= fs.ReadLine 'treat the firstline as columnname
                rest = fs.ReadLine 'the rest as rest
                Do While (Not rest Is Nothing) 'read the complete file
                list.Add(rest)

                Filestream.TextFieldType = FileIO.FieldType.Delimited
                Filestream.SetDelimiters(";")
                Loop
            End Using
        Catch
            ResultBlock.Text = "File not readable"
        End Try

我写了 list.Add(rest) 这实际上是个坏主意,因为当时内容存储在一个变量中,但我需要在 sql 数据库中读取并逐行插入不过似乎很复杂,有人知道我该如何处理吗?

如果因为文件太大而无法将文件读入内存,那么您需要的是某种缓冲区,用于将记录保存在内存中,并在列表达到一定大小时写入数据库。

如果您真的想让它易于管理,那么 reader、编写器和缓冲区都应该完全相互独立。这听起来像是更多的工作,因为它有更多的 classes,但它实际上更简单,因为每个 class 只做一件事。

我会创建一个 class 来表示您正在从文件中读取的项目,以及每条记录的属性。就像如果文件中的每一行代表一个有姓名和员工编号的人,创建一个 class like

public class Person
{
    public string FirstName {get;set;}
    public string LastName {get;set;}
    public string EmployeeNumber {get;set;}
}

你需要一个缓冲区。缓冲区的工作是将项目放入其中,并在达到最大大小时刷新到写入器。大概是这样的:

public interface IBuffer<T>
{
    void AddItem(T item);
}

public interface IWriter<T>
{
    void Write(IEnumerable<T> items);
}

public class WriterBuffer<T> : IBuffer<T>
{
    private readonly IWriter<T> _writer;
    private readonly int _maxSize;
    private readonly List<T> _buffer;

    public WriterBuffer(IWriter<T> writer, int maxSize)
    {
        _writer = writer;
        _maxSize - maxSize;
    }

    public void AddItem(T item)
    {
        _buffer.Add(item);
        if(_buffer.Count >= _maxSize)
        {
            _writer.Write(_buffer);
            _buffer.Clear();
        }
    }    
}

那么,你的reader class根本不了解作者。它只知道它写入缓冲区。

public class PersonFileReader
{
    private readonly string _filename;
    private readonly IBuffer<Person> _buffer;

    public PersonFileReader(string filename, IBuffer<Person> buffer)
    {
        _filename = filename;
        _buffer = buffer;
    }

    public void ReadFile()
    {
        //Reads from file.
        //Creates a new Person for each record
        //Calls _buffer.Add(person) for each Person.
    }       
}

public class PersonSqlWriter : IWriter<Person>
{
    private readonly string _connectionString;

    public PersonSqlWriter(string connectionString)
    {
        _connectionString = connectionString;
    }

    public void Write(IEnumerable<Person> items)
    {
        //Writes the list of items to the database 
        //using _connectionString;
    }
}

结果是每个 classes 只做一件事。您可以将它们与其他人分开使用,并与其他人分开测试。这适用于单一职责原则。没有人 class 太复杂,因为每个人都只有一项职责。它还应用了依赖倒置原则。 reader 不知道缓冲区的作用。这仅取决于界面。缓冲区不知道作者做了什么。而且作者不关心数据来自哪里。

现在复杂的是创建对象。您需要一个文件名、一个连接字符串和一个最大缓冲区大小。这意味着类似

var filename = "your file name";
var maxBufferSize = 50;
var connectionString = "your connection string"

var reader = new PersonFileReader(
    filename,
    new WriterBuffer<Person>(
        new PersonSqlWriter(connectionString),
        maxBufferSize));

您的 classes 更简单,但是将它们连接在一起变得有点复杂。这就是依赖注入的用武之地。它为您管理。我不会深入讨论,因为这可能是信息过载。但是如果你提到这是什么类型的应用程序——网络、WCF 服务等,那么我也许可以提供一个具体的例子来说明像 Windsor、Autofac 或 Unity 这样的依赖注入容器如何为你管理它。

几年前这对我来说是全新的。起初它看起来只是更多的代码。但它实际上使编写小而简单的 classes 变得更容易,这反过来又使构建复杂的应用程序变得更加容易。

查看以下链接:
批量复制 How can I insert 10 million records in the shortest time possible?
这包含代码示例:http://www.sqlteam.com/article/use-sqlbulkcopy-to-quickly-load-data-from-your-client-to-sql-server

您也可以使用导入向导 (https://msdn.microsoft.com/en-us/library/ms141209.aspx?f=255&MSPPError=-2147217396)。