Выбор C # xls & xlsx из листа независимо от имени листа - PullRequest
0 голосов
/ 04 февраля 2019

Это мой GetData метод:

    private DataTable GetData(string userFileName)
    {
        string dirName = Path.GetDirectoryName(userFileName);
        string fileName = Path.GetFileName(userFileName);
        string fileExtension = Path.GetExtension(userFileName);
        string connection = string.Empty;
        string query = string.Empty;

        switch (fileExtension)
        {
            case ".xls":
                connection = $@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={userFileName};" +
                               "Extended Properties=\"Excel 8.0; HDR=Yes; IMEX=1\"";
                query = "SELECT * FROM [Sheet1$]";
                break;

            case ".xlsx":
                connection = $@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={userFileName};" +
                               "Extended Properties=\"Excel 12.0; HDR=Yes; IMEX=1\"";
                query = "SELECT * FROM [Sheet1$]";
                break;

            case ".csv":
                connection = $@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dirName};" +
                               "Extended Properties=\"text; HDR=Yes; IMEX=1; FMT=Delimited\"";
                query = $"SELECT * FROM [{fileName}]";
                break;
        }

        return FillData(connection, query);
    }

Он работает для .csv файлов, так как использует имя файла, а не имя листа.

Он работает для .xls и .xlsx файлы с рабочим листом Sheet1.

Когда я пытаюсь использовать файл .xls/.xlsx с другим именем листа, я получаю следующую ошибку:

System.Data.OleDb.OleDbException: '' Sheet1 $ 'не является допустимым именем.Убедитесь, что он не содержит недопустимых символов или знаков препинания и что он не слишком длинный. '

Ответ на другой вопрос:

using (OleDbConnection conn = new OleDbConnection(connString))
{
    conn.Open();
    dtSchema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
    Sheet1= dtSchema.Rows[0].Field<string>("TABLE_NAME");
}

Не помогло, как и яЯ не знаю, куда это должно пойти в моем коде, и в ответе нет никаких указаний относительно того, куда это должно идти.

string Sheet1 = dataGridView1.Rows[0].Field<string>("TABLE_NAME");

Это дает мне ошибку:

Ошибка CS1929 'DataGridViewRow' не содержит определения для 'Field' и наилучшей перегрузки метода расширения 'DataRowExtensions.Field (DataRow, строка) 'требуется получатель типа' DataRow '

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

Я быстро добавил некоторые исправления в ваш код, но это решение далеко от того, чтобы быть чистым.Вы должны рассмотреть решение от @woldemar и немного глубже понять код.Некоторые превосходные источники можно найти здесь: https://github.com/EbookFoundation/free-programming-books

Вернуться к вашему коду.Чтобы узнать имя первого листа, сначала нужно открыть соединение с файлом xlsx.Затем запросите метаданные с помощью некоторого кода, как в примере:

using (OleDbConnection conn = new OleDbConnection(connString))
{
    conn.Open();
    dtSchema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
    Sheet1= dtSchema.Rows[0].Field<string>("TABLE_NAME");
}

После этого вы можете вставить полученное имя листа в свой запрос.

Быстро и грязно, ваш код должен выглядеть следующим образомчтобы заставить его работать с файлами xlsx:

private static DataTable GetData(string userFileName)
    {
        string dirName = Path.GetDirectoryName(userFileName);
        string fileName = Path.GetFileName(userFileName);
        string fileExtension = Path.GetExtension(userFileName);
        string connection = string.Empty;
        string query = string.Empty;

        switch (fileExtension)
        {
            case ".xls":
                connection = $@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={userFileName};" +
                             "Extended Properties=\"Excel 8.0; HDR=Yes; IMEX=1\"";
                query = "SELECT * FROM [Sheet1$]";
                break;

            case ".xlsx":
                connection = $@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={userFileName};" +
                             "Extended Properties=\"Excel 12.0; HDR=Yes; IMEX=1\"";
                string sheetName;
                using (OleDbConnection con = new OleDbConnection(connection))
                {
                    con.Open();
                    var dtSchema = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
                    sheetName = dtSchema.Rows[0].Field<string>("TABLE_NAME");
                }

                if (sheetName.Length <= 0) throw new InvalidDataException("No sheet found."); // abort if no sheet name was returned

                query = $"SELECT * FROM [{sheetName}]";
                break;

            case ".csv":
                connection = $@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dirName};" +
                             "Extended Properties=\"text; HDR=Yes; IMEX=1; FMT=Delimited\"";
                query = $"SELECT * FROM [{fileName}]";
                break;
        }

        return FillData(connection, query);
    }
0 голосов
/ 04 февраля 2019

Попробуйте вызвать этот метод вместо FillData метода в конце вашего GetData метода, передача query не требуется, как вы можете видеть, потому что этот метод получает само имя листа из схемы документа.

    private DataTable GetDataFromFirstSheet(string connection)
    {
        using (OleDbConnection conn = new OleDbConnection(connection))
        {
            using (DataTable dtSchema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" }))
            {
                string firstSheet = dtSchema.Rows[0].Field<string>("TABLE_NAME");

                //try to remove $ from sheetname if it will be not working
                using (OleDbCommand cmd = new OleDbCommand($"SELECT * FROM [{firstSheet}$]", conn))
                {
                    using (OleDbDataAdapter adp = new OleDbDataAdapter(cmd))
                    {
                        conn.Open();

                        DataTable dt = new DataTable();
                        adp.Fill(dt);

                        return dt;
                    }                            
                }
            }
        }
    }
...