简体   繁体   中英

Insert Columns and Rows into spreadsheet from IQueryable object

How do I insert the columns and rows of data from an Queryable object? What I have so far is listed below. It seems I can get the column names into the spread sheet but Im not sure how to insert the values using the method I have written.


private IQueryable<ShippingRequest> GetRecordsFromDatabase()
{
   var CurrentUserId = (int)Session["UserId"];

   var results = db.ShippingRequests
                .Where(r => r.UserId == CurrentUserId);

   return results;
}
//Create the WorkSheet
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.Add("FedEx Rates");

//get columns of table
var columnNames = typeof(ShippingRequest).GetProperties()
.Select(x => x.Name)
.ToArray();

int i = 0;
//Adding column name to worksheet
foreach (var col in columnNames)
{
    i++;
    worksheet.Cells[1, i].Value = col;
}

//Adding records to worksheet
int j;
for (i = 0; i<columnNames.Length; i++)
{
    foreach (var item in db)
    {
        worksheet.Cells[i + 2, j + 1].Value = ???; //Not sure how to get this value
    }
}

So you fetched some data as a sequence, and you want every element of this sequence to be added as one row to your table. The columns are all readable public properties of ShippingRequests.

Let's create a generic solution that will add any sequence of columns and show any sequence of objects of some class.

Quite often, the names of the columns don't have to fit one-on-one to the names of all your properties. Sometimes you want to show only some properties. Sometimes you want to create different column names or show different values. Maybe you don't want to show your data to an excel sheet, but to a different kind of table?

A reusable class to define columns from some table class could be something like:

class Column<TSource>
{
    public int Index {get; set;}
    public string Name {get; set;}

    public Func<TSource, object> PropertyValueSelector {get; set;}

    public object GetValue(TSource source)
    {
        return this.PropertyValueSelector(source);
    }

    ... // possible other properties, like: IsVisible, IsSortable, DisplayFormat?
}

Apparently, you want to create a sequence of columns for your ShippingRequests containing every public property of ShippingRequest. The name of the column is the identifier of the property. The index is not important.

The following function will create your sequence of Columns:

public static IEnumerable<Column<TSource>> CreateColumns<TSource>() 
       where TSource : class
{
    return typeof(TSource).GetProperties()
        .Where(property => property.CanRead) // they must be at least readable
        .Select( (propertyInfo, index) => new Column<TSource>
        {
            Index = index,
            Name = propertyInfo.Name,

            PropertyValueSelector = source => propertyInfo.GetValue(source);
        });
}

Once we got our data and our columns, we can fill your worksheet:

void Fill<TSource>(this ExcelWorkSheet workSheet,
     IEnumerable<Column<TSource>> columns,
     IEnumerable<TSource> sourceData)
{
    // TODO: clear worksheet?
    //Add column names to worksheet
    foreach (var column in columns)
    {
        worksheet.Cells[1, column.Index].Value = column.Name;
    }

    // add the source data
    int nextRowIndex = 2;
    foreach (var rowData in sourceData)
    {
        AddRow(workSheet, nextRowIndex, columns, rowData);
        ++nextRowIndex;
    }
}

void AddRow<TSource> AddRow<TSource>(this ExcelWorkSheet workSheet,
     int rowIndex,
     IEnumerable<Column<TSource>> columns,
     TSource rowData)
{
    foreach (var column in columns)
    {
        var value = column.GetValue(rowData);
        worksheet.Cells[rowIndex, column.Index].Value = value;
    }
}

Now that you've got this, your code will be easy:

var workSheet = ...
var columns = ...
var data = ...
worksheet.Fill(columns, data);

In your case:

var worksheet = excelPackage.Workbook.Worksheets.Add("FedEx Rates");
var columns = CreateColumns<ShippingRequest>().ToList();
var shippingRequests = GetShippingRequests();
worksheet.Fill(columns, shippingRequests);
// Bam! Done!

The nice thing is that you can use the code to fill worksheets with data from any class.

For example, I have a class Student and I want to show some columns of the 100 youngest students.

// I only want to show the following columns of students:
var studentColumns = new Column<Student>
{ 
    new Column {Index = 1, Name = "Id", PropertyValueSelector = student => student.Id },
    new Column {Index = 3, Name = "Birthday", PropertyValueSelector = student => student.Id }
    new Column {Index = 2, Name = "Student Name", PropertyValueSelector = student =>
        String.Format("{0} {1} {2}", student.FirstName, 
                                     student.MiddleName,
                                     student.FamilyName} },
};

// I only want 100 youngest students:
var studentsToDisplay = GetStudents()
    .OrderByDescending(student => student.BirthDay)
    .Take(100)
    .ToList();

// filling the worksheet is only two lines:
var worksheet = excelPackage.Workbook.Worksheets.Add("Young Students");
worksheet.Fill(studentColumns, studentsToDisplay);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM