it-swarm.com.de

Excel zu DataTable mit EPPlus - Excel zur Bearbeitung gesperrt

Ich verwende den folgenden Code, um ein Excel mit EPPlus in eine Datentabelle zu konvertieren:

public DataTable ExcelToDataTable(string path)
{
    var pck = new OfficeOpenXml.ExcelPackage();
    pck.Load(File.OpenRead(path));
    var ws = pck.Workbook.Worksheets.First();
    DataTable tbl = new DataTable();
    bool hasHeader = true;
    foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
    {
        tbl.Columns.Add(hasHeader ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
    }
    var startRow = hasHeader ? 2 : 1;
    for (var rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
    {
        var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
        var row = tbl.NewRow();
        foreach (var cell in wsRow)
        {
            row[cell.Start.Column - 1] = cell.Text;
        }
        tbl.Rows.Add(row);
    }
    pck.Dispose();
    return tbl;
}

Es erstellt das Excel, aber wenn ich versuche, es zu öffnen, wird mir die Meldung angezeigt, dass es für die Bearbeitung durch einen anderen Benutzer gesperrt ist und ich es nur im schreibgeschützten Modus öffnen kann.

Ich dachte mit:

pck.Dispose();

würde das Problem lösen, aber ich bekomme immer noch den gleichen Fehler.

Wenn ich versuche, die Datei zu löschen, erhalte ich die Meldung: Die Aktion kann nicht abgeschlossen werden, da die Datei in WebDev.WebServer40.EXE geöffnet ist.

Irgendwelche Ideen, wie man dieses Problem lösen kann? Vielen Dank im Voraus. :)

28
Fahad

Ich sehe, das ist was ich habe vor kurzem hier gepostet (jetzt korrigiert). Sie kann verbessert werden, da die ExcelPackage und die FileStream (von File.OpenRead) nach der Verwendung nicht entsorgt werden. 

public static DataTable GetDataTableFromExcel(string path, bool hasHeader = true)
{
    using (var pck = new OfficeOpenXml.ExcelPackage())
    {
        using (var stream = File.OpenRead(path))
        {
            pck.Load(stream);
        }
        var ws = pck.Workbook.Worksheets.First();  
        DataTable tbl = new DataTable();
        foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
        {
            tbl.Columns.Add(hasHeader ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
        }
        var startRow = hasHeader ? 2 : 1;
        for (int rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
        {
            var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
            DataRow row = tbl.Rows.Add();
            foreach (var cell in wsRow)
            {
                row[cell.Start.Column - 1] = cell.Text;
            }
        }
        return tbl;
    }
}
81
Rango

Eine Erweiterungsversion der Antwort von Tim Schmelter.

public static DataTable ToDataTable(this ExcelWorksheet ws, bool hasHeaderRow = true)
{
    var tbl = new DataTable();
    foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
        tbl.Columns.Add(hasHeaderRow ?
            firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
    var startRow = hasHeaderRow ? 2 : 1;
    for (var rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
    {
        var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
        var row = tbl.NewRow();
        foreach (var cell in wsRow) row[cell.Start.Column - 1] = cell.Text;
        tbl.Rows.Add(row);
    }
    return tbl;
}
6
Jafin

Ich habe eine Methode erstellt, mit der eine Excel-Datei mit EPPlus in eine DataTable konvertiert werden kann, und es wurde versucht, die Typsicherheit aufrechtzuerhalten. Auch doppelte Spaltennamen werden behandelt, und mit einem booleschen Wert können Sie feststellen, ob das Blatt eine Zeile mit Kopfzeilen hat. Ich habe es für einen komplexen Importvorgang erstellt, der nach dem Hochladen mehrere Schritte umfasst, die vor dem Festschreiben in die Datenbank eine Benutzereingabe erfordern.

private DataTable ExcelToDataTable(byte[] excelDocumentAsBytes, bool hasHeaderRow)
{
    DataTable dt = new DataTable();
    string errorMessages = "";

    //create a new Excel package in a memorystream
    using (MemoryStream stream = new MemoryStream(excelDocumentAsBytes))
    using (ExcelPackage excelPackage = new ExcelPackage(stream))
    {
        ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];

        //check if the worksheet is completely empty
        if (worksheet.Dimension == null)
        {
            return dt;
        }

        //add the columns to the datatable
        for (int j = worksheet.Dimension.Start.Column; j <= worksheet.Dimension.End.Column; j++)
        {
            string columnName = "Column " + j;
            var excelCell = worksheet.Cells[1, j].Value;

            if (excelCell != null)
            {
                var excelCellDataType = excelCell;

                //if there is a headerrow, set the next cell for the datatype and set the column name
                if (hasHeaderRow == true)
                {
                    excelCellDataType = worksheet.Cells[2, j].Value;

                    columnName = excelCell.ToString();

                    //check if the column name already exists in the datatable, if so make a unique name
                    if (dt.Columns.Contains(columnName) == true)
                    {
                        columnName = columnName + "_" + j;
                    }
                }

                //try to determine the datatype for the column (by looking at the next column if there is a header row)
                if (excelCellDataType is DateTime)
                {
                    dt.Columns.Add(columnName, typeof(DateTime));
                }
                else if (excelCellDataType is Boolean)
                {
                    dt.Columns.Add(columnName, typeof(Boolean));
                }
                else if (excelCellDataType is Double)
                {
                    //determine if the value is a decimal or int by looking for a decimal separator
                    //not the cleanest of solutions but it works since Excel always gives a double
                    if (excelCellDataType.ToString().Contains(".") || excelCellDataType.ToString().Contains(","))
                    {
                        dt.Columns.Add(columnName, typeof(Decimal));
                    }
                    else
                    {
                        dt.Columns.Add(columnName, typeof(Int64));
                    }
                }
                else
                {
                    dt.Columns.Add(columnName, typeof(String));
                }
            }
            else
            {
                dt.Columns.Add(columnName, typeof(String));
            }
        }

        //start adding data the datatable here by looping all rows and columns
        for (int i = worksheet.Dimension.Start.Row + Convert.ToInt32(hasHeaderRow); i <= worksheet.Dimension.End.Row; i++)
        {
            //create a new datatable row
            DataRow row = dt.NewRow();

            //loop all columns
            for (int j = worksheet.Dimension.Start.Column; j <= worksheet.Dimension.End.Column; j++)
            {
                var excelCell = worksheet.Cells[i, j].Value;

                //add cell value to the datatable
                if (excelCell != null)
                {
                    try
                    {
                        row[j - 1] = excelCell;
                    }
                    catch
                    {
                        errorMessages += "Row " + (i - 1) + ", Column " + j + ". Invalid " + dt.Columns[j - 1].DataType.ToString().Replace("System.", "") + " value:  " + excelCell.ToString() + "<br>";
                    }
                }
            }

            //add the new row to the datatable
            dt.Rows.Add(row);
        }
    }

    //show error messages if needed
    Label1.Text = errorMessages;

    return dt;
}

Die Webforms-Schaltfläche klicken Sie zu Demonstrationszwecken.

protected void Button1_Click(object sender, EventArgs e)
{
    if (FileUpload1.HasFile)
    {
        DataTable dt = ExcelToDataTable(FileUpload1.FileBytes, CheckBox1.Checked);

        GridView1.DataSource = dt;
        GridView1.DataBind();
    }
}
4
VDWWD

Dies ist eine Verbesserung gegenüber dem oben genannten generischen. Verwenden Sie, wenn Sie eine Klasse mit den folgenden Eigenschaften haben: "Name", "Nachname", "Telefon", "Fax" und wenn Sie eine Excel-Tabelle mit der ersten Zeile mit denselben Namen haben, werden die Excel-Zeilen in eine Klassenobjekt und platzieren Sie es in eine Liste 

public static List<T> GetClassFromExcel<T>(string path, int fromRow, int fromColumn, int toRow = 0, int toColumn = 0)
{
if (toColumn != 0 && toColumn < fromColumn) throw new          Exception("toColumn can not be less than fromColumn");
if (toRow != 0 && toRow < fromRow) throw new Exception("toRow can not be less than fromRow");
List<T> retList = new List<T>();
using (var pck = new ExcelPackage())
{
            using (var stream = File.OpenRead(path))
            {
                pck.Load(stream);
            }
            //Retrieve first Worksheet
            var ws = pck.Workbook.Worksheets.First();
            //If the to column is empty or 0, then make the tocolumn to the count of the properties
            //Of the class object inserted
            toColumn = toColumn == 0 ? typeof(T).GetProperties().Count() : toColumn;

            //Read the first Row for the column names and place into a list so that
            //it can be used as reference to properties
            Dictionary<string, int> columnNames = new Dictionary<string, int>();
            // wsRow = ws.Row(0);
            var colPosition = 0;
            foreach (var cell in ws.Cells[1, 1, 1, toColumn == 0 ? ws.Dimension.Columns : toColumn])
            {
                columnNames.Add(cell.Value.ToString(), colPosition);
                colPosition++;
            }
            //create a instance of T
            T objT = Activator.CreateInstance<T>();
            //Retrieve the type of T
            Type myType = typeof(T);
            //Get all the properties associated with T
            PropertyInfo[] myProp = myType.GetProperties();


            //Loop through the rows of the Excel sheet
            for (var rowNum = fromRow; rowNum <= (toRow == 0? ws.Dimension.End.Row : toRow); rowNum++)
            {
                var wsRow = ws.Cells[rowNum, fromColumn, rowNum, ws.Cells.Count()];

                foreach (var propertyInfo in myProp)
                {
                    if (columnNames.ContainsKey(propertyInfo.Name))
                    {
                        int position = 0;
                        columnNames.TryGetValue(propertyInfo.Name, out position);
                        //int position = columnNames.IndexOf(propertyInfo.Name);
                        //To prevent an exception cast the value to the type of the property.
                        propertyInfo.SetValue(objT, Convert.ChangeType(wsRow[rowNum, position + 1].Value, propertyInfo.PropertyType));
                    }
                }

                retList.Add(objT);
            }

        }
        return retList;
    }

jetzt können Sie die Liste als Datenbindungsquelle verwenden, wenn Sie ....__ brauchen. Ein Geschenk von mir an Sie ... :) Daniel C. Vrey

Es wurde für toColumn aktualisiert, um zu funktionieren und toRow hinzugefügt und den Vorschlägen von Andreas gefolgt. Daumen hoch für Andreas

4
user3731074

Die obige Antwort von VDWWD funktioniert hervorragend, um die Typensicherheit aufrechtzuerhalten, und ich habe darauf einige Verbesserungen vorgenommen.

  • Methode liest direkt aus einer Datei.
  • Spaltentyperkennung durch Verwendung aller Zeilen und nicht nur eines Werts. Der Spaltentyp wird auf String gesetzt, wenn in der Spalte mehr als der Typ gefunden wird.
  • Fehlermeldungen, die in einer Liste von Zeichenfolgen zurückgegeben werden.

Hier ist die aktualisierte Version:

    public static DataTable ExcelToDataTable(string path, ref List<string> errorList, bool hasHeaderRow = true  )
    {
        DataTable dt = new DataTable();
        errorList = new List<string>();

        //create a new Excel package           
        using (ExcelPackage excelPackage = new ExcelPackage())
        {
            using (var stream = File.OpenRead(path))
            {
                excelPackage.Load(stream);
            }

            ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];

            //check if the worksheet is completely empty
            if (worksheet.Dimension == null)
            {
                return dt;
            }

            //add the columns to the datatable
            for (int j = worksheet.Dimension.Start.Column; j <= worksheet.Dimension.End.Column; j++)
            {
                string columnName = "Column " + j;


                //Build hashset with all types in the row
                var columnTypes = new HashSet<Type>();                   
                for (int i = worksheet.Dimension.Start.Row + Convert.ToInt32(hasHeaderRow); i <= worksheet.Dimension.End.Row; i++)
                {
                    //Only add type if cell value not empty
                    if (worksheet.Cells[i, j].Value != null)
                    {
                        columnTypes.Add(worksheet.Cells[i, j].Value.GetType());
                    }
                }

                var excelCell = worksheet.Cells[1, j].Value;


                if (excelCell != null)
                {
                    Type excelCellDataType = null;

                    //if there is a headerrow, set the next cell for the datatype and set the column name
                    if (hasHeaderRow == true)
                    {

                        columnName = excelCell.ToString();

                        //check if the column name already exists in the datatable, if so make a unique name
                        if (dt.Columns.Contains(columnName) == true)
                        {
                            columnName = columnName + "_" + j;
                        }
                    }

                    //Select  input type for the column
                    if (columnTypes.Count == 1)
                    {
                        excelCellDataType = columnTypes.First();
                    }
                    else
                    {
                        excelCellDataType = typeof(string);
                    }

                    //try to determine the datatype for the column (by looking at the next column if there is a header row)
                    if (excelCellDataType == typeof(DateTime))
                    {
                        dt.Columns.Add(columnName, typeof(DateTime));
                    }
                    else if (excelCellDataType == typeof(Boolean))
                    {
                        dt.Columns.Add(columnName, typeof(Boolean));
                    }
                    else if (excelCellDataType == typeof(Double))
                    {
                        //determine if the value is a decimal or int by looking for a decimal separator
                        //not the cleanest of solutions but it works since Excel always gives a double
                        if (excelCellDataType.ToString().Contains(".") || excelCellDataType.ToString().Contains(","))
                        {
                            dt.Columns.Add(columnName, typeof(Decimal));
                        }
                        else
                        {
                            dt.Columns.Add(columnName, typeof(Int64));
                        }
                    }
                    else
                    {
                        dt.Columns.Add(columnName, typeof(String));
                    }
                }
                else
                {
                    dt.Columns.Add(columnName, typeof(String));
                }
            }

            //start adding data the datatable here by looping all rows and columns
            for (int i = worksheet.Dimension.Start.Row + Convert.ToInt32(hasHeaderRow); i <= worksheet.Dimension.End.Row; i++)
            {
                //create a new datatable row
                DataRow row = dt.NewRow();

                //loop all columns
                for (int j = worksheet.Dimension.Start.Column; j <= worksheet.Dimension.End.Column; j++)
                {
                    var excelCell = worksheet.Cells[i, j].Value;

                    //add cell value to the datatable
                    if (excelCell != null)
                    {
                        try
                        {
                            row[j - 1] = excelCell;
                        }
                        catch
                        {
                            errorList.Add("Row " + (i - 1) + ", Column " + j + ". Invalid " + dt.Columns[j - 1].DataType.ToString().Replace("System.", "") + " value:  " + excelCell.ToString() );
                        }
                    }
                }

                //add the new row to the datatable
                dt.Rows.Add(row);
            }
        }

        return dt;
    }
1
David Ruiz
public static List<T> getClassFromExcel<T>(string path, int fromRow, int fromColumn, int toColumn = 0) where T : class
        {
            using (var pck = new OfficeOpenXml.ExcelPackage())
            {
                List<T> retList = new List<T>();

                using (var stream = File.OpenRead(path))
                {
                    pck.Load(stream);
                }
                var ws = pck.Workbook.Worksheets.First();
                toColumn = toColumn == 0 ? typeof(T).GetProperties().Count() : toColumn;

                for (var rowNum = fromRow; rowNum <= ws.Dimension.End.Row; rowNum++)
                {
                    T objT = Activator.CreateInstance<T>();
                    Type myType = typeof(T);
                    PropertyInfo[] myProp = myType.GetProperties();

                    var wsRow = ws.Cells[rowNum, fromColumn, rowNum, toColumn];

                    for (int i = 0; i < myProp.Count(); i++)
                    {
                        myProp[i].SetValue(objT, wsRow[rowNum, fromColumn + i].Text);
                    }
                    retList.Add(objT);
                }
                return retList;
            }
        }
0
dglee
public static List<T> GetClassFromExcel<T>(string path, int fromRow, int fromColumn, int toRow = 0, int toColumn = 0) where T: class, new()
{
        if (toColumn != 0 && toColumn < fromColumn) throw new Exception("toColumn can not be less than fromColumn");
        if (toRow != 0 && toRow < fromRow) throw new Exception("toRow can not be less than fromRow");
        List<T> retList = new List<T>();
        using (var pck = new ExcelPackage())
        {
            using (var stream = File.OpenRead(path))
            {
                pck.Load(stream);
            }
            //Retrieve first Worksheet
            var ws = pck.Workbook.Worksheets.First();

            toColumn = toColumn == 0 ? typeof(T).GetProperties().Count() : toColumn; //If the to column is empty or 0, then make the tocolumn to the count of the properties Of the class object inserted

            //Read the first Row for the column names and place into a list so that
            //it can be used as reference to properties
            Dictionary<string, int> columnNames = new Dictionary<string, int>();
            // wsRow = ws.Row(0);
            var colPosition = 0;
            foreach (var cell in ws.Cells[1, 1, 1, toColumn == 0 ? ws.Dimension.Columns : toColumn])
            {
                columnNames.Add(cell.Value.ToString(), colPosition);
                colPosition++;
            }

            //Retrieve the type of T
            Type myType = typeof(T);

            //Get all the properties associated with T
            PropertyInfo[] myProp = myType.GetProperties();

            //Loop through the rows of the Excel sheet
            for (var rowNum = fromRow + 1; rowNum <= (toRow == 0 ? ws.Dimension.End.Row : toRow); rowNum++) // fromRow + 1 to read from next row after columnheader
            {

                //create a instance of T
                //T objT = Activator.CreateInstance<T>();
                T objT = new T();

                // var wsRow = ws.Cells[rowNum, fromColumn, rowNum, ws.Cells.Count()]; //ws.Cells.Count() causing out of range error hence using ws.Dimension.Columns to get last column index 
                var wsRow = ws.Cells[rowNum, fromColumn, rowNum, ws.Dimension.Columns];
                foreach (var propertyInfo in myProp)
                {
                    var attribute = propertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), true).Cast<DisplayNameAttribute>().SingleOrDefault();
                    string displayName = attribute != null && !string.IsNullOrEmpty(attribute.DisplayName) ? attribute.DisplayName : propertyInfo.Name; // If DisplayName annotation not used then get property name itself                       
                    if (columnNames.ContainsKey(displayName))
                    {
                        int position = 0;                           
                        columnNames.TryGetValue(displayName, out position);
                        ////int position = columnNames.IndexOf(propertyInfo.Name);
                        ////To prevent an exception cast the value to the type of the property.
                        propertyInfo.SetValue(objT, Convert.ChangeType(wsRow[rowNum, position + 1].Value, propertyInfo.PropertyType));
                    }
                }                   
                retList.Add(objT);
            }

        }
        return retList;
    }
//IMPLEMENTATION DONE BY PLACING Code IT IN SEPARATE Helpers.CS file  and 
//Consuming it in this manner
List<CustomerExcelModel> records = 
Helpers.GetClassFromExcel<CustomerExcelModel>(filelocation, 1, 1);

Vielen Dank an den Benutzer, der den vorgelegten Code und Andreas für den Vorschlag unterbreitet hat. Hier sind die folgenden Änderungen: Ich bin neu bei Generika. Verzeihen Sie und korrigieren Sie mich für etwaige Fehler 

  • Entity-Modell "Anzeige Annotation" hinzugefügt, um der Excel-Spalte zugeordnet zu werdenname, so dass auch der Spaltenname mit Leerzeichen behandelt werden kann.
  • hatte ein Problem "T objT", da es außerhalb der for-Schleife lag und daher verursacht wurde
    instantiieren der inneren Schleife, dh mit "new T ()"
  • Fehler in Spalte außerhalb des Bereichs behoben, indem "ws.Dimension.Columns" verwendet wurde, um die Anzahl der Spalten anstelle von ws.Cells.Count () zu ermitteln, da dies zu einem Fehler im Bereich 
  • für das Durchlaufen von Zeilendaten wurde +1 hinzugefügt, da RowNum = 1 den Namen des Headers las, und so geringfügige Änderung von "rowNum = fromRow + 1" vorgenommen.
0
user2568029

Hier erzähle ich Ihnen, wie Sie das Excel lesen können. Sie können es ändern, um jedes Datum in Datentypen zu speichern.

public void readXLS(string FilePath)
{
    FileInfo existingFile = new FileInfo(FilePath);
    using (ExcelPackage package = new ExcelPackage(existingFile))
    {
        //get the first worksheet in the workbook
        ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
        int colCount = worksheet.Dimension.End.Column;  //get Column Count
        int rowCount = worksheet.Dimension.End.Row;     //get row count
        for (int row = 1; row <= rowCount; row++)
        {
            for (int col = 1; col <= colCount; col++)
            {
                //You can update code here to add each cell value to DataTable.
                Console.WriteLine(" Row:" + row + " column:" + col + " Value:" + worksheet.Cells[row, col].Value.ToString().Trim());
            }
        }
    }
}

Reff: http://sforsuresh.in/read-data-Excel-sheet-insert-database-table-c/

0
Suresh Kamrushi