I'm in need of a fresh pair of eyes here. I'm trying to extract details from a user-uploaded file, add them to a DataTable, then write them to my SQL Server table.
It all appears to work nicely but for the actual fields being added to the DataTable (rather crucial!). The code runs to completion but with a blank DataTable, so obviously nothing is being written to my table. I attach my Controller - I'm sure it's something obvious that I'm missing, but I can't determine why.
public class FileUploaderController : Controller
{
public FileUploaderController()
{
db = new DatabaseContext();
}
public FileUploaderController(DatabaseContext context)
{
db = context;
}
private DatabaseContext db { get; }
public ActionResult UploadFilesPartial()
{
return View();
}
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
var dt = CreateDataTable();
if (file != null)
{
var fileName = file.FileName;
var filePath = Path.GetFullPath(fileName);
var fileType = Path.GetExtension(fileName);
DataRow fileDetails = dt.NewRow();
fileDetails["FileName"] = fileName;
fileDetails["FilePath"] = filePath;
fileDetails["FileType"] = fileType;
UploadToDataBase(db, dt);
}
return RedirectToAction("NettingBatch", "Views");
}
private DataTable CreateDataTable()
{
System.Data.DataTable table = new DataTable("FileUploads");
DataColumn id;
id = new DataColumn
{
DataType = System.Type.GetType("System.Int32"),
ColumnName = "Id",
ReadOnly = true,
Unique = true
};
table.Columns.Add(id);
DataColumn name;
name = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "FileName",
AutoIncrement = false,
Caption = "FileName",
ReadOnly = false,
Unique = false
};
table.Columns.Add(name);
DataColumn path;
path = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "FilePath",
AutoIncrement = false,
Caption = "FilePath",
ReadOnly = false,
Unique = false
};
table.Columns.Add(path);
DataColumn type;
type = new DataColumn
{
DataType = System.Type.GetType("System.String"),
ColumnName = "FileType",
AutoIncrement = false,
Caption = "FileType",
ReadOnly = false,
Unique = false
};
table.Columns.Add(type);
DataColumn[] PrimaryKeyColumns = new DataColumn[1];
PrimaryKeyColumns[0] = table.Columns["Id"];
table.PrimaryKey = PrimaryKeyColumns;
return table;
}
private void UploadToDataBase(DbContext context, DataTable data)
{
try
{
var columnMappings = new List<Tuple<string, string>>
{
new Tuple<string, string>("Id", "Id"),
new Tuple<string, string>("FileName", "FileName"),
new Tuple<string, string>("FilePath", "FilePath"),
new Tuple<string, string>("FileType", "FileType"),
};
PopulateHistoryTable.BulkCopyDataTableIntoDatabase(context, data, "[mdp].[FileUploads]",
columnMappings);
}
catch (Exception e)
{
Console.WriteLine(e.InnerException);
}
}
}
I expect the details of my test file that I upload on my view to be extracted and written back to FileUploads table. Actual results are blank.
NewRow returns a row that matches your DataTable scheme, but it doesn't actually "add" it to the DataTable. Use the "Add" function of the Rows collection to do that:
DataRow fileDetails = dt.NewRow();
fileDetails["FileName"] = fileName;
fileDetails["FilePath"] = filePath;
fileDetails["FileType"] = fileType;
dt.Rows.Add(fileDetails);
Related
I've got some code that I have used to pull Google Analytics data with a c# console application and it works great. Whenever I try to use that same code in an SSIS script task I get the error "Error deserializing JSON credential data.". I get the error when running locally and when deployed. I've got all the libraries added to the GAC and I'm using the same version libraries and .net Framework as the console app. Anyone have any ideas?
public void Main()
{
string SQL_Script = null;
string ErrorMessage = string.Empty;
string ExceptionMessage = "No error";
// Declare the variables that you'll be pulling from Google Analytics into the database
DateTime GA_Session_Date = new DateTime();
DateTime GA_End_Date = new DateTime();
GA_End_Date = DateTime.Today.AddDays(-1);
string GA_TransactionId = null;
string GA_ChannelGrouping = null;
string GA_Source = null;
string GA_Medium = null;
string GA_Keyword = null;
string GA_Campaign = null;
string GA_Device_Category = null;
string GA_Region = null;
int GA_Transactions = 0;
/*
* Get the last SessionDate loaded
*/
GA_Session_Date = Convert.ToDateTime(GetMaxSessionnDate());
GA_Session_Date = GA_Session_Date.AddDays(-1);
/*
* Delete the last SessionDate loaded from the table
*
* The free version of Google Analytics takes up to 24 hours to bake
* so reloading the last day will ensure that we get all of the data.
*/
SQL_Script = "DELETE FROM OmniChannelAnalytics.GoogleAnalytics.Transactions WHERE SessionDate >= '" + GA_Session_Date.ToString() + "';";
ErrorMessage = ExecuteSQL(SQL_Script);
/*
* Create the DataTable and DataSet to house the data from GA until
* it is bulk loaded into SQL
*/
DataSet dataSet = new DataSet();
DataTable sessionTable = new DataTable();
sessionTable.TableName = "Sessions";
// Add the columns to the Sessions table
sessionTable.Columns.Add("SessionDate", typeof(string));
sessionTable.Columns.Add("TransactionId", typeof(string));
sessionTable.Columns.Add("ChannelGrouping", typeof(string));
sessionTable.Columns.Add("Source", typeof(string));
sessionTable.Columns.Add("Medium", typeof(string));
sessionTable.Columns.Add("Keyword", typeof(string));
sessionTable.Columns.Add("Campaign", typeof(string));
sessionTable.Columns.Add("DeviceCategory", typeof(string));
sessionTable.Columns.Add("Region", typeof(string));
sessionTable.Columns.Add("Transactions", typeof(int));
sessionTable.Columns.Add("LoadDate", typeof(string));
dataSet.Tables.Add(sessionTable);
while (GA_Session_Date <= GA_End_Date)
{
try
{
var credential = Google.Apis.Auth.OAuth2.GoogleCredential.FromFile(GlobalVariables.GA_ClientSecretFileLocation)
.CreateScoped(new[] { Google.Apis.AnalyticsReporting.v4.AnalyticsReportingService.Scope.AnalyticsReadonly });
using (var analytics = new Google.Apis.AnalyticsReporting.v4.AnalyticsReportingService(new Google.Apis.Services.BaseClientService.Initializer
{
HttpClientInitializer = credential
}))
{
var request = analytics.Reports.BatchGet(new GetReportsRequest
{
ReportRequests = new[] {
new ReportRequest{
DateRanges = new[] { new DateRange{ StartDate = GA_Session_Date.ToString("yyyy-MM-dd"), EndDate = GA_Session_Date.ToString("yyyy-MM-dd") } },
Dimensions = new[] {
new Dimension{ Name = "ga:transactionId" }
, new Dimension { Name = "ga:channelGrouping" }
, new Dimension { Name = "ga:sourceMedium" }
, new Dimension { Name = "ga:keyword" }
, new Dimension { Name = "ga:campaign" }
, new Dimension { Name = "ga:deviceCategory" }
, new Dimension { Name = "ga:region" }
},
Metrics = new[] { new Metric{ Expression = "ga:transactions", Alias = "Transactions"}},
ViewId = GlobalVariables.GA_View_ID
}
}
});
var response = request.Execute();
foreach (var row in response.Reports[0].Data.Rows)
{
GA_TransactionId = row.Dimensions[0];
GA_ChannelGrouping = row.Dimensions[1];
GA_Source = row.Dimensions[2].Substring(0, row.Dimensions[2].IndexOf("/")).Trim().Replace("'", "''");
GA_Medium = row.Dimensions[2].Substring(row.Dimensions[2].IndexOf("/") + 1, row.Dimensions[2].Length - row.Dimensions[2].IndexOf("/") - 1).Trim().Replace("'", "''");
GA_Keyword = row.Dimensions[3];
GA_Campaign = row.Dimensions[4];
GA_Device_Category = row.Dimensions[5];
GA_Region = row.Dimensions[6];
foreach (var metric in row.Metrics)
{
GA_Transactions = Convert.ToInt32(metric.Values[0]);
}
// Populate the data table to hold until everything is bulk loaded into SQL
DataRow newRow = sessionTable.NewRow();
newRow["SessionDate"] = GA_Session_Date;
newRow["TransactionId"] = GA_TransactionId;
newRow["ChannelGrouping"] = GA_ChannelGrouping;
newRow["Source"] = GA_Source;
newRow["Medium"] = GA_Medium;
newRow["Keyword"] = GA_Keyword;
newRow["Campaign"] = GA_Campaign;
newRow["DeviceCategory"] = GA_Device_Category;
newRow["Region"] = GA_Region;
newRow["Transactions"] = GA_Transactions;
newRow["LoadDate"] = DateTime.Now;
sessionTable.Rows.Add(newRow);
} // foreach (var row in rows)
}
} // try
catch (Exception ex)
{
ExceptionMessage = ex.Message;
}
finally
{
// Import the current day's Session data
foreach (DataTable table in dataSet.Tables)
{
ImportTable(table);
}
sessionTable.Clear();
}
// Iterate the session date to import by 1
GA_Session_Date = GA_Session_Date.AddDays(1);
} // while (GA_Session_Date <= GA_End_Date)
Dts.TaskResult = (int)ScriptResults.Success;
}
I am trying to build some datagridviews. I am currently using the code shown below. Im wondering if there is a more efficient way to build the columns. I tried this method post, but cant seem to make it work. Does anyone have any suggestions?
DataGridView hire = form1.hireDataGridView;
hire.ColumnCount = 6;
hire.Columns[0].Name = "Name";
hire.Columns[1].Name = "Type";
hire.Columns[2].Name = "Date";
hire.Columns[3].Name = "Cost";
hire.Columns[4].Name = "Start Date";
hire.Columns[5].Name = "End Date";
DataGridView service = form1.serviceDataGridView;
service.ColumnCount = 5;
service.Columns[0].Name = "Name";
service.Columns[1].Name = "Type";
service.Columns[2].Name = "Date";
service.Columns[3].Name = "Cost";
service.Columns[4].Name = "Description";
DataGridView relocate = form1.relocationDataGridView;
relocate.ColumnCount = 9;
relocate.Columns[0].Name = "Name";
relocate.Columns[1].Name = "Type";
relocate.Columns[2].Name = "Date";
relocate.Columns[3].Name = "Cost";
relocate.Columns[4].Name = "dist";
relocate.Columns[5].Name = "latA";
relocate.Columns[6].Name = "longA";
relocate.Columns[7].Name = "latB";
relocate.Columns[8].Name = "longB";
You can create a dictionary having the names of the columns per grid:
using System.Collections.Generic;
private Dictionary<DataGridView, string[]> GridsSettings;
private void InitializeGridsSettings()
{
if ( GridsSettings != null) return;
GridsSettings = new Dictionary<DataGridView, string[]>
{
[hireDataGridView] = new string[]
{ "Name", "Type", "Date", "Cost", "Start Date", "End Date" },
[serviceDataGridView] = new string[]
{ "Name", "Type", "Date", "Cost", "Description" },
[relocationDataGridView] = new string[]
{ "Name", "Type", "Date", "Cost", "dist", "latA", "longA", "latB", "longB" },
};
}
Then you can dynamically generate:
InitializeGridsSettings(); // Called from constructor for example
foreach ( var grid in GridsSettings)
{
grid.Key.ColumnCount = grid.Value.Length;
int indexColumn = 0;
foreach ( string name in grid.Value )
grid.Key.Columns[indexColumn++].Name = name;
}
Also you can use a table with names and types per grid:
GridsSettings = new Dictionary<DataGridView, List<(string, Type)>>
{
[hireDataGridView] = new List<(string, Type)>
{ ("Name", typeof(string)) },
[serviceDataGridView] = new List<(string, Type)>
{ ("Name", typeof(string)) },
[relocationDataGridView] = new List<(string, Type)>
{ ("Name", typeof(string)) },
};
foreach ( var grid in GridsSettings)
{
grid.Key.ColumnCount = grid.Value.Count;
int indexColumn = 0;
foreach ( var column in grid.Value )
{
grid.Key.Columns[indexColumn].Name = column.Item1;
grid.Key.Columns[indexColumn].ValueType = column.Item2;
indexColumn++;
}
}
Data bindings can also be done the same manner to set database fields or object properties names, as well as anything suitable.
I am unable to make a hyperlink in the excel template. I'd like to display the value in the cell like this http://www.diamondschool/{personName}
I have read the official documentation on github. No luck at all
public FileInfo GenerateTemplate(long shoolId)
{
using (var excelPackage = new ExcelPackage(dataTemplate))
{
excelPackage.Workbook.Worksheets.Add("Sheet1");
var ws = excelPackage.Workbook.Worksheets[1];
var dataTable = InitializeDataTable();
var templateData = GetTemplateData(schoolId);
PopulateData(templateData, dataTable);
ws.Cells["A2"].LoadFromDataTable(dataTable, true);
}
// Since I am already loading the data from the data table I can't find a way to display the value as a hyperlink.
//http://www.diamondschool/{personName} so depending the value in the cell, display the personame as hyperlink to that address
//Person name as HyperLink
var namedStyle = ws.Workbook.Styles.CreateNamedStyle("HyperLink");
namedStyle.Style.Font.UnderLine = true;
namedStyle.Style.Font.Color.SetColor(Color.Blue);
ws.Cells["A2"].Value
ws.Cells["A2"].StyleName = "HyperLink";
excelPackage.Save();
}
return dataTemplate;
}
private static DataTable InitializeDataTable()
{
var dataTable = new DataTable();
dataTable.Columns.Add("Person Id", typeof(string));
dataTable.Columns.Add("Person Name", typeof(string));
return dataTable;
}
private static void PopulateData(IEnumerable<DataTemplateRow> data, DataTable dataTable)
{
foreach (var item in data)
{
var dataRow = dataTable.NewRow();
dataRow["Person Id"] = item.Id;
dataRow["Person Name"] = item.Name;
dataTable.Rows.Add(dataRow);
}
}
public IEnumerable<DataTemplateRow> GetTemplateData(long schoolId)
{
var personData = _schoolService.GetData(schoolId);
var result = personData.Data.Select(result => new DataTemplateRow
{
PersonId = result.Id,
PersonName = result.Name
});
return result;
}
public class DataTemplateRow
{
public long Id { get; set; }
public class Name { get; set; }
}
Since I am already loading the data from the data table I can't find a way to display the value as a hyperlink.
http://www.diamondschool/{personName} so depending the value in the cell, display the personame as hyperlink to that address
You need to format the Cell like this. Create a Hyperlink and set it with an Uri
ws.Cells[5, 5].Style.Font.UnderLine = true;
ws.Cells[5, 5].Style.Font.Bold = true;
ws.Cells[5, 5].Style.Font.Color.SetColor(Color.Blue);
ws.Cells[5, 5].Hyperlink = new Uri("https://www.google.nl");
private DataTable GetAttributeTable()
{
DataTable cltAttributeTable = new DataTable("CLT_ATTRIBUTE");
DataColumnCollection iRefColumns = cltAttributeTable.Columns;
//BETHiddenColumn is defined for hiding certain columns at the UI
//And can be used for manipulating entities internally
iRefColumns.AddRange(new[]
{
new BETHiddenColumn { ColumnName = CLDConstants.CLTGUID, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = CLDConstants.CLTNAME, DataType = typeof(string), ReadOnly = true },
new BETHiddenColumn { ColumnName = CLDConstants.SHEETID, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = CLDConstants.SHEETNAME, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "OBJECT_TYPE", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "OBJECT_NAME", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "ATTRIBUTE_NAME", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "ATTRIBUTE_VALUE", DataType = typeof(string), ReadOnly = false }
});
return cltAttributeTable;
}
public override async Task<DataTable> GetDataAsync(ControlNetworkStructure controlNetwork)
{
DataTable cltAttributeTable = GetAttributeTable();
try
{
using (var automationService = ConsumedServiceProvider.Provider.AutomationService)
{
foreach (string userDefinedLogicTemplate in selectedCLT)
{
var controlLogicClt =
automationService.AutomationClt.GetControlLogicTemplate(userDefinedLogicTemplate);
foreach (ISheet sheet in await controlLogicClt.GetSheets())
{
foreach (IFunctionCode functionCode in await sheet.GetFunctionCodes())
{
foreach (IHarmonyAttribute functionCodeAttribute in functionCode.Attributes)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = "FUNCTION CODE";
row["OBJECT_NAME"] = functionCode.Name;
row["ATTRIBUTE_NAME"] = functionCodeAttribute.Type;
row["ATTRIBUTE_VALUE"] = functionCodeAttribute.Value;
cltAttributeTable.Rows.Add(row);
}
}
foreach (IInputReference inputReference in await sheet.GetInputReferences())
{
foreach (IHarmonyAttribute functionCodeAttribute in inputReference.Attributes)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = "IREF";
row["OBJECT_NAME"] = inputReference.Name;
row["ATTRIBUTE_NAME"] = functionCodeAttribute.Type;
row["ATTRIBUTE_VALUE"] = functionCodeAttribute.Value;
cltAttributeTable.Rows.Add(row);
}
}
foreach (IOutputReference outputReference in await sheet.GetOutputReferences())
{
foreach (IHarmonyAttribute functionCodeAttribute in outputReference.Attributes)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = "OREF";
row["OBJECT_NAME"] = outputReference.Name;
row["ATTRIBUTE_NAME"] = functionCodeAttribute.Type;
row["ATTRIBUTE_VALUE"] = functionCodeAttribute.Value;
cltAttributeTable.Rows.Add(row);
}
}
foreach (IText text in await sheet.GetTexts())
{
foreach (IHarmonyAttribute functionCodeAttribute in text.Attributes)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = "TEXT";
row["OBJECT_NAME"] = text.Name;
row["ATTRIBUTE_NAME"] = functionCodeAttribute.Type;
row["ATTRIBUTE_VALUE"] = functionCodeAttribute.Value;
cltAttributeTable.Rows.Add(row);
}
}
}
}
}
}
catch (Exception exception)
{
LogService.LogException(this, ServiceResources.CONTEXT_CLD_EDITOR, "CLT Attribute",
exception);
}
finally
{
// Accepting all the modification to the table before leaving this method call
cltAttributeTable.AcceptChanges();
}
return cltAttributeTable;
}
Description
I have a method with multiple foreach loops inside and I have hard time understanding it as I'm beginner to C#. Also I read its not a good practice to write multiple foreach loops in program.
The method returns a data table and it is bound to a Datagrid.
Can anyone help me simplify it so that it becomes better readable and more intuitive?
if the Attribute property of the IFunctionCode, IInputReference, etc is based on a common interface, you could do something like this:
List<ICommomInterface> items = new List<ICommomInterface>();
items.AddRange(await sheet.GetFunctionCodes());
items.AddRange(await sheet.GetInputReferences());
items.AddRange(await sheet.GetOutputReferences());
items.AddRange(await sheet.GetTexts());
items.foreach(item =>
{
item.Attributes.ForEach(attrib =>
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
if(item is IFunctionCode){
row["OBJECT_TYPE"] = "FUNCTION CODE";
} else if(_other types_)
{
}
row["OBJECT_NAME"] = item.Name;
row["ATTRIBUTE_NAME"] = attrib.Type;
row["ATTRIBUTE_VALUE"] = attrib.Value;
cltAttributeTable.Rows.Add(row);
});
});
If that's your intention.
If they do not share a common base interface you have to use object class for the list and extend the if(item is IFunctionCode){... check.
Ok, this is pretty hastily done but I think is a good start. I didn't have time to test it so it probably has some bugs. Also, there's plenty of room for improvements.
private DataTable GetAttributeTable()
{
DataTable cltAttributeTable = new DataTable("CLT_ATTRIBUTE");
DataColumnCollection iRefColumns = cltAttributeTable.Columns;
//BETHiddenColumn is defined for hiding certain columns at the UI
//And can be used for manipulating entities internally
iRefColumns.AddRange(new[]
{
new BETHiddenColumn { ColumnName = CLDConstants.CLTGUID, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = CLDConstants.CLTNAME, DataType = typeof(string), ReadOnly = true },
new BETHiddenColumn { ColumnName = CLDConstants.SHEETID, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = CLDConstants.SHEETNAME, DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "OBJECT_TYPE", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "OBJECT_NAME", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "ATTRIBUTE_NAME", DataType = typeof(string), ReadOnly = true },
new DataColumn { ColumnName = "ATTRIBUTE_VALUE", DataType = typeof(string), ReadOnly = false }
});
return cltAttributeTable;
}
public override async Task<DataTable> GetDataAsync(ControlNetworkStructure controlNetwork)
{
DataTable cltAttributeTable = new DataTable();
try
{
using (var automationService = ConsumedServiceProvider.Provider.AutomationService)
{
foreach (string userDefinedLogicTemplate in selectedCLT)
{
var controlLogicClt =
automationService.AutomationClt.GetControlLogicTemplate(userDefinedLogicTemplate);
cltAttributeTable = await LoopDeLoop(controlLogicClt);
}
}
}
catch (Exception exception)
{
LogService.LogException(this, ServiceResources.CONTEXT_CLD_EDITOR, "CLT Attribute",
exception);
}
finally
{
// Accepting all the modification to the table before leaving this method call
cltAttributeTable.AcceptChanges();
}
return cltAttributeTable;
}
//Main loop with loops adding rows
private async Task<DataTable> LoopDeLoop(dynamic controlLogicClt)
{
DataTable cltAttributeTable = GetAttributeTable();
foreach (ISheet sheet in await controlLogicClt.GetSheets())
{
foreach (IFunctionCode functionCode in await sheet.GetFunctionCodes())
{
cltAttributeTable = GetNewRows(cltAttributeTable, functionCode.Attributes, functionCode.Name, "FUNCTION CODE", controlLogicClt, sheet);
}
foreach (IInputReference inputReference in await sheet.GetInputReferences())
{
cltAttributeTable = GetNewRows(cltAttributeTable, inputReference.Attributes, inputReference.Name, "IREF", controlLogicClt, sheet);
}
foreach (IOutputReference outputReference in await sheet.GetOutputReferences())
{
cltAttributeTable = GetNewRows(cltAttributeTable, outputReference.Attributes, outputReference.Name, "OREF", controlLogicClt, sheet);
}
foreach (IText text in await sheet.GetTexts())
{
cltAttributeTable = GetNewRows(cltAttributeTable, text.Attributes, text.Name, "TEXT", controlLogicClt, sheet);
}
}
}
//Adds the new created rows to the DataTable
private DataTable GetNewRows(DataTable cltAttributeTable, List<IHarmonyAttribute> attributes, string name, string objectType, dynamic controlLogicClt, ISheet sheet)
{
foreach (IHarmonyAttribute attribute in attributes)
{
cltAttributeTable.Rows.Add(GetNewRow(sourceDataRow, attribute, name, objectType, controlLogicClt, sheet));
}
}
//Creates and populates the new row
private DateRow GetNewRow(IHarmonyAttribute attribute, string name, string objectType, dynamic controlLogicClt, ISheet sheet)
{
DataRow row = GetRow(cltAttributeTable, controlLogicClt, sheet);
row["OBJECT_TYPE"] = objectType;
row["OBJECT_NAME"] = name;
row["ATTRIBUTE_NAME"] = attribute.Type;
row["ATTRIBUTE_VALUE"] = attribute.Value;
return row;
}
So I made three more methods:
LoopDeLoop --> This one's for taking out of the main method the majority of the loops and thus making it clearer. Also, it makes the sheets loops clearer.
´GetNewRows´ --> This one is for making a common IHarmonyAttribute loop and get rid of the need of using four loops.
GetNewRow --> This one just populates a new row, but now you don't have to do that four times.
Hope this helps
I have a problem.
I have an Excel File where sometimes the same customer is in 2 rows.
But not always.
Its like:
Click
Now, i want to create a DataGrid in C# which can list this in one row like:
Click
I know it would be easier to change the Excel file, but assume it wouldnt work that way(we get the file like this and we cant change it)
I know i could too just make another Excel File and make it with Excel(already did it this way, but we would need it more as C#)
Now i am at this point:
private void button2_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog() { Filter = "Excel Arbeitsmappe |*.xlsx", ValidateNames = true };
if (ofd.ShowDialog() == DialogResult.OK)
textBox1.Text = ofd.SafeFileName;
Excel.Application excelApp = new Excel.Application();
excelApp.Visible = false;
string filename = ofd.FileName;
Excel.Workbook workbook = excelApp.Workbooks.Open(filename);
Excel.Worksheet worksheet = workbook.Worksheets[1];
dataGridView1.ColumnCount = 2;
dataGridView1.Columns[0].Name = "Number";
dataGridView1.Columns[1].Name = "Street";
int rows = worksheet.UsedRange.Rows.Count;
for (int i = 2; i <= rows; i++)
{
string combinehr = worksheet.Cells[i, 150].Text + worksheet.Cells[i, 151].Text;
dataGridView1.Rows.Add(worksheet.Cells[i,29].Text, combinehr);
}
}
How do i expand it that it works like i want?
I would be so grateful
(sorry for the english)
With a reference to ExcelDataReader, ExcelDataReader.DataSet and DataSetExtensions (.Net) you can read the Excel file pretty easy into a DataSet, then you just have to work with the logic:
Input file:
using System;
using System.Data;
using System.IO;
using System.Linq;
using ExcelDataReader;
public DataTable GetTableFromExcel(string filePath)
{
DataSet ds = new DataSet();
using (var stream = System.IO.File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
ds = reader.AsDataSet();
}
}
DataTable table = new DataTable();
table.Columns.Add(new DataColumn("CustomerNr"));
table.Columns.Add(new DataColumn("Address"));
// Set column names
for (int i = 0; i < ds.Tables[0].Columns.Count; i++)
{
// DataColumn.ColumnName can't be empty when DataColumn is part
// of a DataTable (throws ArgumentException)
string columnName = ds.Tables[0].Rows[0][i].ToString();
if (string.IsNullOrWhiteSpace(columnName))
{
columnName = $"Column{i}";
}
ds.Tables[0].Columns[i].ColumnName = columnName;
}
// Remove the first row containing the headers
ds.Tables[0].Rows.Remove(ds.Tables[0].Rows[0]);
// I don't have the benchmarks with me right now, but I've tested
// DataTable.Select vs DataTable.AsEnumerable.Select many times
// and the AsEnumerable method its faster, that's why you need the
// reference to System.Data.DataSetExtensions
var enumerableTable = ds.Tables[0].AsEnumerable();
// list of unique products
var products = enumerableTable.Select(row => row.Field<string>("Product")).Distinct();
// Add a column for each product
foreach (string product in products)
{
table.Columns.Add(new DataColumn(product));
}
// list of unique customers
var customerNumbers = enumerableTable.Select(row => row.Field<double>("CustomerNr")).Distinct();
foreach (var customerNumber in customerNumbers)
{
DataRow record = table.NewRow();
record["CustomerNr"] = customerNumber;
record["Address"] = enumerableTable.First(row => row.Field<double>("CustomerNr").Equals(customerNumber))[1];
for (int i = 2; i < table.Columns.Count; i++)
{
DataRow product = enumerableTable.FirstOrDefault(row => row.Field<double>("CustomerNr").Equals(customerNumber)
&& row.Field<string>("Product").Equals(table.Columns[i].ColumnName));
// Quantity = 0 if product is null
record[i] = product?["Quantity"] ?? 0;
}
table.Rows.Add(record);
}
return table;
}
Result DataTable:
The same result as #IvanGarcíaTopete via Microsoft.Office.Interop.Excel.
Class ExcelModel for Excel data:
public class ExcelModel
{
public string CustomerNr { get; set; }
public string Address { get; set; }
public string Product { get; set; }
public string Quantity { get; set; }
}
Read Excel and fill out model:
private void OpenReadExcel()
{
var dlg = new OpenFileDialog();
if (dlg.ShowDialog() != DialogResult.OK) return;
var exApp = new Microsoft.Office.Interop.Excel.Application();
Workbook exWbk = exApp.Workbooks.Open(dlg.FileName);
Worksheet wSh = exWbk.Sheets[1];
int k = 2;
Customers.Clear();
while (wSh.Cells[k, 1].Text != "" && wSh.Cells[k, 1].Value != null)
{
var rowExcelModel = new ExcelModel()
{
CustomerNr = wSh.Cells[k, 1].Text,
Address = wSh.Cells[k, 2].Text,
Product = wSh.Cells[k, 3].Text,
Quantity = wSh.Cells[k, 4].Text
};
Customers.Add(rowExcelModel);
k++;
}
exApp.Quit();
}
Generate Data table:
private void GenerateDataTable()
{
// unique products and customers
var products = Customers.Select(x => x.Product).Distinct().ToList();
var customers = Customers.Select(x => x.CustomerNr).Distinct().ToList();
// columns CustomerNr and Address
var dataTable = new System.Data.DataTable();
dataTable.Columns.Add(new DataColumn("CustomerNr"));
dataTable.Columns.Add(new DataColumn("Address"));
// columns for each product
foreach (var product in products)
{
dataTable.Columns.Add(new DataColumn(product));
}
//fill rows for each customers
foreach (var customer in customers)
{
var row = dataTable.NewRow();
row["CustomerNr"] = customer;
row["Address"] = Customers.Where(x => x.CustomerNr == customer).Select(x => x.Address).FirstOrDefault();
foreach (var product in products)
{
var quantity = Customers.Where(x => x.CustomerNr == customer && x.Product == product)
.Select(x => x.Quantity).FirstOrDefault();
row[product] = quantity ?? "0";
}
dataTable.Rows.Add(row);
}
dataGridView1.DataSource = dataTable;
}