1

I am working on adding Export() functionality to my MVC5 Code-First app. To do this, I have created the following ViewModel - ExportAssetsViewModel:

public class ExportAssetsViewModel
{
    public Dictionary<int, string> ListOfExportFields { get; set; }
    public int[] SelectedFields { get; set; }

    public ExportAssetsViewModel() {
        ListOfExportFields = new Dictionary<int, string>() {
            {1, "Model"},
            {2, "Manufacturer"},
            {3, "Type"},
            {4, "Location"},
            {5, "Vendor"},
            {6, "Status"},
            {7, "ip_address"},
            {8, "mac_address"},
            {9, "note"},
            {10, "owner"},
            {11, "cost"},
            {12, "po_number"},
            {13, "description"},
            {14, "invoice_number"},
            {15, "serial_number"},
            {16, "asset_tag_number"},
            {17, "acquired_date"},
            {18, "disposed_date"},
            {19, "verified_date"},
            {20, "created_date"},
            {21, "created_by"},
            {22, "modified_date"},
            {23, "modified_by"},
        };
    }
}

This is used to display my MultiSelectList on my ExportController - Index() View:

ExportController - Index() Action:

    public ActionResult Index()
    {
        ExportAssetsViewModel expViewMod = new ExportAssetsViewModel();
        return View(expViewMod);
    }

Index View:

@model InventoryTracker.Models.ExportAssetsViewModel

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Export</h2>

<p>Please select which Asset fields to Export to Excel:</p>

@using (Html.BeginForm("ExportUsingEPPlus", "Export", FormMethod.Post))
{
    @Html.ListBoxFor(m => m.SelectedFields, new MultiSelectList(Model.ListOfExportFields, "Key", "Value"), new { @class = "form-control", style = "height: 250px;" })
    <br />
    <input type="submit" value="ExportUsingEPPlus" />
}

Index View - Rendered HTML:

<h2>Export</h2>

<p>Please select which Asset fields to Export to Excel:</p>

<form action="/Export/ExportUsingEPPlus" method="post"><select class="form-control" id="SelectedFields" multiple="multiple" name="SelectedFields" style="height: 250px;"><option value="1">Model</option>
<option value="2">Manufacturer</option>
<option value="3">Type</option>
<option value="4">Location</option>
<option value="5">Vendor</option>
<option value="6">Status</option>
<option value="7">ip_address</option>
<option value="8">mac_address</option>
<option value="9">note</option>
<option value="10">owner</option>
<option value="11">cost</option>
<option value="12">po_number</option>
<option value="13">description</option>
<option value="14">invoice_number</option>
<option value="15">serial_number</option>
<option value="16">asset_tag_number</option>
<option value="17">acquired_date</option>
<option value="18">disposed_date</option>
<option value="19">verified_date</option>
<option value="20">created_date</option>
<option value="21">created_by</option>
<option value="22">modified_date</option>
<option value="23">modified_by</option>
</select>    <br />
    <input type="submit" value="ExportUsingEPPlus" />
</form>

Now what I'm attempting to do is use the EPPlus library to Export all values in my INV_Assets table for the selected fields in my MultiSelectList to Excel.

ExportController - ExportUsingEPPlus():

    [HttpPost]
    public ActionResult ExportUsingEPPlus(ExportAssetsViewModel model)
    {
        ExcelPackage package = new ExcelPackage();
        var ws = package.Workbook.Worksheets.Add("TestExport");  

        var exportFields = new List<string>();
        foreach(var selectedField in model.SelectedFields)
        {
            // Adds selected fields to [exportFields] List<string>
            exportFields.Add(model.ListOfExportFields.First(s => s.Key == selectedField).Value);
        }

        // Loops to insert column headings into Row 1 of Excel
        for (int i = 0; i < exportFields.Count(); i++ )
        {
            ws.Cells[1, i + 1].Value = exportFields[i].ToString();
        }

        // INVALID - Need to query table INV_Assets for all values of selected fields and insert into appropriate columns.
        if (exportFields.Count() > 0)
        {
            ws.Cells["A2"].LoadFromCollection(exportFields);
        }

        var memoryStream = new MemoryStream();
        package.SaveAs(memoryStream);

        string fileName = "Exported-InventoryAssets-" + DateTime.Now + ".xlsx";
        string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

        memoryStream.Position = 0;
        return File(memoryStream, contentType, fileName);

For my current code, if I select [ip_address], [mac_address], [note], [owner], and [cost] in my MultiSelectList I get the following output verbatim in my Excel Sheet:

[ip_address]
[mac_address]
[note]
[owner]
[cost]

How do I query my INV_Assets table to insert all the data values for the selected fields into the appropriate columns?


EDIT:

This is my INV_Assets Model I'm attempting to query using LINQ. The suggestion for THIS seems promising, but I'm not quite sure how to set it up using my own fields?

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using GridMvc.DataAnnotations;
using System.Web.Mvc;
using InventoryTracker.Models;

namespace InventoryTracker.Models
{
    [GridTable(PagingEnabled = true, PageSize = 30)]
    public class INV_Assets 
    {
        // Setting GridColumn Annotations allows you to use AutoGenerateColumns on view to auto create the Grid based on the model.


        public int Id { get; set; }

        public int Model_Id { get; set; }
        [ForeignKey("Model_Id")]
        public virtual INV_Models Model { get; set; }

        [Required]
        public int Manufacturer_Id { get; set; }
        [ForeignKey("Manufacturer_Id")]
        public virtual INV_Manufacturers Manufacturer { get; set; }

        [Required]
        public int Type_Id { get; set; }
        [ForeignKey("Type_Id")]
        public virtual INV_Types Type { get; set; }

        [Required]
        public int Location_Id { get; set; }
        [ForeignKey("Location_Id")]
        public virtual INV_Locations Location { get; set; }

        public int Vendor_Id { get; set; }
        [ForeignKey("Vendor_Id")]
        public virtual INV_Vendors Vendor { get; set; }

        [Required]
        public int Status_Id { get; set; }
        [ForeignKey("Status_Id")]
        public virtual INV_Statuses Status { get; set; }

        public string ip_address { get; set; }

        public string mac_address { get; set; }

        [DataType(DataType.MultilineText)]
        public string note { get; set; }
        public string owner { get; set; }

        //[DataType(DataType.Currency)]
        //[DisplayFormat(DataFormatString="{0:C}", ApplyFormatInEditMode=true)]
        [DisplayFormat(DataFormatString = "{0:#,###0.00}", ApplyFormatInEditMode=true)]
        public decimal cost { get; set; }
        public string po_number { get; set; }

        [DataType(DataType.MultilineText)]
        public string description { get; set; }

        public int invoice_number{ get; set; }

        [Required]
        public string serial_number { get; set; }

        [Required]
        public string asset_tag_number { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime? acquired_date { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime? disposed_date { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime? verified_date { get; set; }

        [Required]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime created_date { get; set; }

        [Required]
        public string created_by { get; set; }

        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
        public DateTime? modified_date { get; set; }

        public string modified_by { get; set; }

        // Flag to specify if item is available? (Not signed out, not auctioned, recycled, etc.)
        //public bool available { get; set; }
    }
}

EDIT2:

I believe I got the right file as mentioned in I need to select particular column based on check box list. That solution says to get DynamicLibrary.cs, but in the download all I found was Dynamic.cs? In VS2013 I clicked on my Project and added a new class called DynamicLibrary.cs. I then copy/pasted the contents from the downloaded file to this new class file.

This allowed me to reference using System.Linq.Dynamic.

Now however I think I've added the example setup in the wrong place? This is how my ExportController currently stands with the DynamicColumns code towards the bottom. The last section public IQueryable DynamicSelectionColumns() currently has not been modified -- IQueryable, TrackerDataContext() and DynamicColumns() are all flagged as errors currently.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using InventoryTracker.DAL;
using OfficeOpenXml;
using InventoryTracker.Models;
using System.Linq.Dynamic;


namespace InventoryTracker.Controllers
{
    public class ExportController : Controller
    {
        InventoryTrackerContext _db = new InventoryTrackerContext();

        // GET: Export
        public ActionResult Index()
        {
            ExportAssetsViewModel expViewMod = new ExportAssetsViewModel();
            return View(expViewMod);
        }

        public ActionResult Export()
        {
            GridView gv = new GridView();
            gv.DataSource = _db.INV_Assets.ToList();
            gv.DataBind();
            Response.ClearContent();
            Response.Buffer = true;
            Response.AddHeader("content-disposition", "attachment; filename=InventoryAssets-" + DateTime.Now + ".xls");
            Response.ContentType = "application/ms-excel";
            Response.Charset = "";
            StringWriter sw = new StringWriter();
            HtmlTextWriter htw = new HtmlTextWriter(sw);
            gv.RenderControl(htw);
            Response.Output.Write(sw.ToString());
            Response.Flush();
            Response.End();

            return RedirectToAction("StudentDetails");
        }

        [HttpPost]
        public ActionResult ExportUsingEPPlus(ExportAssetsViewModel model)
        {
            //FileInfo newExcelFile = new FileInfo(output);
            ExcelPackage package = new ExcelPackage();
            var ws = package.Workbook.Worksheets.Add("TestExport");  

            var exportFields = new List<string>();
            foreach(var selectedField in model.SelectedFields)
            {
                // Adds selected fields to [exportFields] List<string>
                exportFields.Add(model.ListOfExportFields.First(s => s.Key == selectedField).Value);
            }

            // Loops to insert column headings into Row 1 of Excel
            for (int i = 0; i < exportFields.Count(); i++ )
            {
                ws.Cells[1, i + 1].Value = exportFields[i].ToString();
            }

            // INVALID - Need to query table INV_Assets for all values of selected fields and insert into appropriate columns.
            if (exportFields.Count() > 0)
            {
                var exportAssets = from ia in _db.INV_Assets
                                   select new {
                                       ia.ip_address,

                                   }
                ws.Cells["A2"].LoadFromCollection(exportFields);
            }

            var memoryStream = new MemoryStream();
            package.SaveAs(memoryStream);

            string fileName = "Exported-InventoryAssets-" + DateTime.Now + ".xlsx";
            string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

            memoryStream.Position = 0;
            return File(memoryStream, contentType, fileName);
        }

    }

    public class DynamicColumns : INV_Assets
    {
        //public int Id { get; set; }
        //public int Model_Id { get; set; }
        public virtual INV_Models Model { get; set; }
        //public int Manufacturer_Id { get; set; }
        public virtual INV_Manufacturers Manufacturer { get; set; }
        //public int Type_Id { get; set; }
        public virtual INV_Types Type { get; set; }
        //public int Location_Id { get; set; }
        public virtual INV_Locations Location { get; set; }
        //public int Vendor_Id { get; set; }
        public virtual INV_Vendors Vendor { get; set; }
        //public int Status_Id { get; set; }
        public virtual INV_Statuses Status { get; set; }
        public string ip_address { get; set; }
        public string mac_address { get; set; }
        public string note { get; set; }
        public string owner { get; set; }
        public decimal cost { get; set; }
        public string po_number { get; set; }
        public string description { get; set; }
        public int invoice_number { get; set; }
        public string serial_number { get; set; }
        public string asset_tag_number { get; set; }
        public DateTime? acquired_date { get; set; }
        public DateTime? disposed_date { get; set; }
        public DateTime? verified_date { get; set; }
        public DateTime created_date { get; set; }
        public string created_by { get; set; }
        public DateTime? modified_date { get; set; }
        public string modified_by { get; set; }
    }

    public enum EnumTasks
    {
        Model = 1,
        Manufacturer = 2,
        Type = 3,
        Location = 4,
        Vendor = 5,
        Status = 6,
        ip_address = 7,
        mac_address = 8,
        note = 9,
        owner = 10,
        cost = 11,
        po_number = 12,
        description = 13,
        invoice_number = 14,
        serial_number = 15,
        asset_tag_number = 16,
        acquired_date = 17,
        disposed_date = 18,
        verified_date = 19,
        created_date = 20,
        created_by = 21,
        modified_date = 22,
        modified_by = 23
    }

     public IQueryable DynamicSelectionColumns()
    {
        using (var db = new TrackerDataContext())
        {
            string fieldIds = "," + "4,5,3,2,6,17,11,12" + ",";

            var taskColum = Enum.GetValues(typeof(EnumTasks)).Cast<EnumTasks>().Where(e => fieldIds.Contains("," + ((int)e).ToString() + ",")).Select(e => e.ToString().Replace("_", ""));

            string select = "new (  TaskId, " + (taskColum.Count() > 0 ? string.Join(", ", taskColum) + ", " : "") + "Id )";

            return db.Task.ToList().Select(t => new DynamicColumns() { Id = t.Id, TaskId = Project != null ? Project.Alias + "-" + t.Id : t.Id.ToString(), ActualTime = t.ActualTime, AssignedBy = t.AssignedBy.ToString(), AssignedDate = t.AssignedDate, AssignedTo = t.AssignedTo.ToString(), CreatedDate = t.CreatedDate, Details = t.Details, EstimatedTime = t.EstimatedTime, FileName = t.FileName, LogWork = t.LogWork, Module = t.Module != null ? t.Module.Name : "", Priority = t.Priority != null ? t.Priority.Name : "", Project = t.Project != null ? t.Project.Name : "", ResolveDate = t.ResolveDate, Status = t.Status != null ? t.Status.Name : "", Subject = t.Subject, TaskType = t.TaskType != null ? t.TaskType.Type : "", Version = t.Version != null ? t.Version.Name : "" }).ToList().AsQueryable().Select(select);
        }
    }
}

Where/how do I set up this code to use Linq DynamicColumns()?

Analytic Lunatic
  • 3,853
  • 22
  • 78
  • 120
  • 1
    Code First is an approach for Entity Framework. Query a database is Entity Framework, too. So what your question is about, MVC or EF? If EF then why did you post the markup. Post your data access layer code instead! – abatishchev Mar 05 '15 at 20:23
  • @abatishchev, Post my Data access layer code? – Analytic Lunatic Mar 05 '15 at 20:25
  • If you're already wrote how to handle user's input to select fields, that's good and enough for now. Please post how do you query your database (using EF?). – abatishchev Mar 05 '15 at 20:28
  • Yes, typically I do my querying through using EF/LINQ in my Controllers. (Ex.): `INV_Assets asset = db.INV_Assets.Find(asset_ID);` – Analytic Lunatic Mar 05 '15 at 20:29
  • I think I got what you're saying. You want an UI to select fields to export. Then see [Scott Gu's blog](http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library). – abatishchev Mar 05 '15 at 20:30
  • Or better [this](http://fransiscuss.com/2013/04/02/run-a-selective-column-query-in-entity-framework/). So you need to build an expression for `Select()` dynamically basing on fields names selected by a user. – abatishchev Mar 05 '15 at 20:32
  • Here's the [PredicateBuilder](http://www.albahari.com/nutshell/predicatebuilder.aspx), to build an expression for `Where()`, just for better understanding of the problem. – abatishchev Mar 05 '15 at 20:33
  • @abatishchev, can you post some form of example? I'm getting a bit overloaded with all the links your commenting about. – Analytic Lunatic Mar 05 '15 at 20:34
  • First I'd recommend to implement a basic export will all fields included, so you would have a code to improve. Rather than try to write a more complex one right away from scratch. Meanwhile I'll try to write an example. – abatishchev Mar 05 '15 at 20:57
  • Here's exactly what you're looking for http://stackoverflow.com/questions/606104/how-to-create-linq-expression-tree-with-anonymous-type-in-it – abatishchev Mar 05 '15 at 20:58
  • Also http://stackoverflow.com/questions/11096029/i-need-to-select-particular-column-based-on-check-box-list-in-linq-c-net – abatishchev Mar 05 '15 at 21:02
  • The last suggestion for http://stackoverflow.com/questions/11096029/i-need-to-select-particular-column-based-on-check-box-list-in-linq-c-net seems promising, but how do I set it up based on my own fields? I've included my `INV_Assets` Model in the EDIT above. – Analytic Lunatic Mar 05 '15 at 21:14
  • @abatishchev, I made some leeway (I think) but have run into some issues. Please see EDIT2 above. – Analytic Lunatic Mar 05 '15 at 21:51
  • So you can't compile it. What errors do you get? – abatishchev Mar 05 '15 at 21:52
  • I don't understand what you mean "Try source code form here"? – Analytic Lunatic Mar 05 '15 at 21:55
  • Never mind. Replace TrackerDataContext with your DataContext you use in EF. – abatishchev Mar 05 '15 at 22:03
  • @abatishchev, that would be my `InventoryTrackerContext`, but it's not getting referenced where I've got the code set up above. Is the code for `Linq.Dynamic` in the wrong place? I stuck it in my `ExportController`. – Analytic Lunatic Mar 05 '15 at 22:11
  • Then implement this logic not in Controller but in DAL where you do have your DbContext. – abatishchev Mar 05 '15 at 22:13
  • Ok, in my `DAL.InventoryTrackerContext` I added the `Linq.Dynamic` code exampled in your link (`public class DynamicColumns : INV_Assets`, `public enum EnumTasks`, and `public IQueryable DynamicSelectionColumns()`, with all of the same errors as before still flagging. – Analytic Lunatic Mar 05 '15 at 22:16

0 Answers0