I'm allowing users to upload data from an Excel file to my database, and want to display friendly error messages if one of their cells is in the wrong format. In total, there will be 20 or so cells to upload, but right now I'm just trying two. The Excel upload I'm trying has some random text in the MedicalTotal field, which should instead be formatted as a decimal. I verified that my code is looking at the right field. The following controller code does nothing. Nothing is written to the database, and the same view is reloaded.
In the watch window, I get a message of "Convert.ToDecimal(table[0]) threw an exception of type System.FormatException" I have tried the code with both Exception and FormatException. I'm open to other methods of catching errors, keeping in mind that I'll be repeating the try/catch process about 20 times.
[Authorize]
[HttpPost]
public ActionResult CreateBenefitSummary(HttpPostedFileBase FileUpload, Guid ResponseId)
{
BenefitsUploadViewModel model = new BenefitsUploadViewModel();
if (FileUpload != null)
{
// tdata.ExecuteCommand("truncate table OtherCompanyAssets");
if (FileUpload.ContentType == "application/vnd.ms-excel" || FileUpload.ContentType == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
string filename = FileUpload.FileName;
string targetpath = Server.MapPath("~/Doc/");
FileUpload.SaveAs(targetpath + filename);
string pathToExcelFile = targetpath + filename;
var connectionString = "";
if (filename.EndsWith(".xls"))
{
connectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0; data source={0}; Extended Properties=Excel 8.0;", pathToExcelFile);
}
else if (filename.EndsWith(".xlsx"))
{
connectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0 Xml;HDR=YES;IMEX=1\";", pathToExcelFile);
}
var excelFile = new ExcelQueryFactory(pathToExcelFile);
var table = (from a in excelFile.WorksheetRangeNoHeader("B2", "B32", "Benefits Template") select a[0]).ToList();
TempData["ResponseId"] = ResponseId;
try
{
Benefits b = new Benefits();
b.ResponseId = ResponseId;
try
{
b.MedicalTotal = Convert.ToDecimal(table[0]);
}
catch (FormatException)
{
model.ErrorList.Add("Medical Total cell must use Number, Currency, or Accounting format.");
}
b.StdSicknessAccident = Convert.ToDecimal(table[1]);
if (model.ErrorList.Count > 0) {
return View(model);
}
db.benefits.Add(b);
db.SaveChanges();
}
catch (SqlException ex)
{
ViewBag.Message = ex.Message;
}
catch (Exception ex)
{
ViewBag.Message = ex.Message;
}
//deleting excel file from folder
if ((System.IO.File.Exists(pathToExcelFile)))
{
System.IO.File.Delete(pathToExcelFile);
}
TempData["ResponseId"] = ResponseId;
return RedirectToAction("CreateBenefitSummary", "Surveys");
}
}
return View();
}
Your convert.ToDecimal should probably use a Decimal.TryParse instead, then showing the error message after an if statement seeing if the results parsed. Using try/catch for flow control is generally considered bad practice.
something like:
Decimal decVal;
if (Decimal.TryParse(table[0], out decVal))
{
b.MedicalTotal = decVal;
}
else
{
model.ErrorList.Add("Medical Total cell must use Number, Currency, or Accounting format.");
}
Related
I want to upload the excel file of record 2500. This process takes time more than 5 minutes approximate. I want to optimize it to less than a minute maximum.
Find the best way of optimization of that code. I am using Dbgeography for location storing. File is uploaded and save the data properly. Everything is working find but I need optimization.
public ActionResult Upload(HttpPostedFileBase FileUpload)
{
if (FileUpload.ContentLength > 0)
{
string fileName = Path.GetFileName(FileUpload.FileName);
string ext = fileName.Substring(fileName.LastIndexOf('.'));
Random r = new Random();
int ran = r.Next(1, 13);
string path = Path.Combine(Server.MapPath("~/App_Data/uploads"), DateTime.Now.Ticks + "_" + ran + ext);
try
{
FileUpload.SaveAs(path);
ProcessCSV(path);
ViewData["Feedback"] = "Upload Complete";
}
catch (Exception ex)
{
ViewData["Feedback"] = ex.Message;
}
}
return View("Upload", ViewData["Feedback"]);
}
Here is other method from where the file is uploaded and save it to the database..
private void ProcessCSV(string filename)
{
Regex r = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
StreamReader sr = new StreamReader(filename);
string line = null;
string[] strArray;
int iteration = 0;
while ((line = sr.ReadLine()) != null)
{
if (iteration == 0)
{
iteration++;
continue; //Because Its Heading
}
strArray = r.Split(line);
StoreLocation store = new StoreLocation();
store.Name = strArray[0];
store.StoreName = strArray[1];
store.Street = strArray[2];
store.Street2 = strArray[3];
store.St_City = strArray[4];
store.St_St = strArray[5];
store.St_Zip = strArray[6];
store.St_Phone = strArray[7];
store.Office_Phone = strArray[8];
store.Fax = strArray[9];
store.Ownership = strArray[10];
store.website = strArray[11];
store.Retailer = check(strArray[12]);
store.WarehouseDistributor = check(strArray[13]);
store.OnlineRetailer = check(strArray[14]);
string temp_Add = store.Street + "," + store.St_City;
try
{
var point = new GoogleLocationService().GetLatLongFromAddress(temp_Add);
string points = string.Format("POINT({0} {1})", point.Latitude, point.Longitude);
store.Location = DbGeography.FromText(points);//lat_long
}
catch (Exception e)
{
continue;
}
db.StoreLocations.Add(store);
db.SaveChanges();
}
The obvious place to start would be your external call to the Geo Location service, which it looks like you are doing for each iteration of the loop. I don't know that service, but is there any way you can build up a list of addresses in memory, and then hit it once with multiple addresses, then go back and amend all the records you need to update?
The call to db.SaveChanges() which updates the database, this is happening for every line.
while (...)
{
...
db.StoreLocations.Add(store);
db.SaveChanges(); // Many database updates
}
Instead, just call it once at the end:
while (...)
{
...
db.StoreLocations.Add(store);
}
db.SaveChanges(); // One combined database update
This should speed up your code nicely.
private void Backup()
{
string x = txtb.Text;
string date = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
string file = x + date + "database backup.sql";
string conn = "server = localhost; user id = root; password =; database = sais_db;";
try
{
MySqlBackup mb = new MySqlBackup(conn);
mb.Export(file);
MessageBox.Show("Database Backup Success!");
}
catch (MySqlException ex)
{
MessageBox.Show(ex.Message);
}
}
Help please, when I run my program, I get a NotSupportedException error. How do I fix it?
P.S. txtb.Text contains directory path like this C:\Users\user\Desktop
Your DateTime format includes colons. Those are not allowed in a file or pathname except for delimiting the drive letter from the path.
You can easily fix that by changing the format string of your DateTime.Now call:
string x = txtb.Text;
string date = DateTime.Now.ToString("MM-dd-yyyy HH-mm-ss");
string file = x + date + "database backup.sql";
When you run this you'll get (assumimg txtb.Text contains foo) in file
foo09-24-2016 14-20-59database backup.sql
You can also consider to do that in one go:
string file = String.Format(
"{0}{1:MM-dd-yyyy HH-mm-ss}database backup.sql",
txtb.Text,
DateTime.Now);
If you allow your users to provide (part of) a filename consider checking for invalid characters. The Path class in the System.IO namespace has a nice helper for that GetInvalidFileNameChars.
if (file.IndexOfAny(Path.GetInvalidFileNameChars()) > -1)
{
// show an error
MessageBox.Show("invalid characters in file");
return;
}
There is a similar method for InvalidPathChars
Can anyone help me exporting in excel as number and not text? Also, I want to freeze the first row. This is the function I am using to export to excel. Also, I want the rows to be in alternate color:
public FilePathResult ExportToExcel(){
log.Info("Action ExportToExcel for user " + User.Identity.GetUserName());
List<string> dataTypes = new List<string>();
NameValueCollection parameters = Request.Params;
string query = QueryUtility.ConstructQueryForExcel(parameters);
DataSet ds = new DataSet();
DataTable dt = DaoUtilitySingleton.Instance.ExecuteQueryToGetDataTable(query);
ds.Tables.Add(dt);
log.Info("Query for export " + query);
log.Info("Column names : " + parameters["columnNames"]);
string guid = Guid.NewGuid().ToString();
string fileName = guid;
fileName += System.DateTime.Now.Ticks.ToString();
fileName += ".xlsx";
string destinationPath = Path.Combine(
Server.MapPath("~/App_Data/ExcelExports"));
if (!Directory.Exists(destinationPath))
{
log.Info("Directory does not exist. Creating Directory first");
Directory.CreateDirectory(destinationPath);
}
destinationPath = Path.Combine(destinationPath, fileName);
log.Info("Excel file will be saved as " + destinationPath);
//currently , this function return data which is in text and not number
try
{
if (!(new ExcelUtility()).CreateExcelDocumentFromDataset(ds,
destinationPath))
{
log.Error("Excel file could not be created");
return null;
}
}
catch (Exception ex)
{
GeneralUtility.LogException(log,
"Excel file could not be created",
ex);
throw;
}
log.Info("FIle created successfully");
FilePathResult file = File(destinationPath, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", Properties.Instance.FileForExcel);
return file;
//return the file
}
You can help yourself with an OpenXML wrapper. Rigth now I'm using SpreadLight. It handles most of the interaction with excel, You can digg into the examples to it's advanced features.
On my local machine everything works well but..
After publishing my MVC4 web project there is a problem with an uploaded Excel file.
I load an HttpPostedFileBase and send the path to my BL. There I load it to dataTable and on my second call I get it to a list.
Here is the code..
Controller:
[HttpPost]
public ActionResult UploadCards(HttpPostedFileBase file, string sheetName, int ProductID)
{
try
{
if (file == null || file.ContentLength == 0)
throw new Exception("The user not selected a file..");
var fileName = Path.GetFileName(file.FileName);
var path = Server.MapPath("/bin");
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
path = Path.Combine(path, fileName);
file.SaveAs(path);
DataTable cardsDataTable = logic.LoadXLS(path, sheetName);
cardsToUpdate = logic.getUpdateCards(cardsDataTable, ProductID);
foreach (var item in cardsToUpdate)
{
if (db.Cards.ToList().Exists(x => x.SerialNumber == item.SerialNumber))
cardsToUpdate.Remove(item);
}
Session["InfoMsg"] = "click update to finish";
}
catch (Exception ex)
{
Session["ErrorMsg"] = ex.Message;
}
return View("viewUploadCards", cardsToUpdate);
}
BL:
public DataTable LoadXLS(string strFile, String sheetName)
{
DataTable dtXLS = new DataTable(sheetName);
try
{
string strConnectionString = "";
if (strFile.Trim().EndsWith(".xlsx"))
strConnectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0 Xml;HDR=YES;IMEX=1\";", strFile);
else if (strFile.Trim().EndsWith(".xls"))
strConnectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=1\";", strFile);
OleDbConnection SQLConn = new OleDbConnection(strConnectionString);
SQLConn.Open();
OleDbDataAdapter SQLAdapter = new OleDbDataAdapter();
string sql = "SELECT * FROM [" + sheetName + "$]";
OleDbCommand selectCMD = new OleDbCommand(sql, SQLConn);
SQLAdapter.SelectCommand = selectCMD;
SQLAdapter.Fill(dtXLS);
SQLConn.Close();
}
catch (Exception ex)
{
string res = ex.Message;
return null;
}
return dtXLS;
}
and:
public List<Card> getUpdateCards(DataTable dt, int prodId)
{
List<Card> cards = new List<Card>();
try
{
Product product = db.Products.Single(p => p.ProductID == prodId);
foreach (DataRow row in dt.Rows)
{
cards.Add(new Card
{
SerialNumber = row[0].ToString(),
UserName = row[1].ToString(),
Password = row[2].ToString(),
Activated = false,
Month = product.Months,
Bandwidth = product.Bandwidth,
ProductID = product.ProductID,
// Product = product
});
}
}
catch (Exception ex)
{
db.Log.Add(new Log { LogDate = DateTime.Now, LogMsg = "Error : " + ex.Message });
}
return cards;
}
Now I think Windows Azure doesn't let me save this file because on the middle view when I supposed to see the data - I don't see it.
I thought of some ways...
one - not saving the file, but I don't see how to complete the ConnectionString...
second maybe there is a way to save the file there.
I'd love to get suggestions for solving this problem...
10x and sorry for my bad English =)
I'm embarrassed but I found a similar question here.. Not exactly but it gave me a good direction.
Hare the finally result:
[HttpPost]
public ActionResult UploadCards(HttpPostedFileBase file, string sheetName, int ProductID)
{
IExcelDataReader excelReader = null;
try
{
if (file == null || file.ContentLength == 0)
throw new Exception("The user not selected a file..");
if (file.FileName.Trim().EndsWith(".xlsx"))
excelReader = ExcelReaderFactory.CreateOpenXmlReader(file.InputStream);
else if (file.FileName.Trim().EndsWith(".xls"))
excelReader = ExcelReaderFactory.CreateBinaryReader(file.InputStream);
else
throw new Exception("Not a excel file");
cardsToUpdate = logic.getUpdateCards(excelReader.AsDataSet().Tables[sheetName], ProductID);
foreach (var item in cardsToUpdate)
{
if (db.Cards.ToList().Exists(x => x.SerialNumber == item.SerialNumber))
cardsToUpdate.Remove(item);
}
Session["InfoMsg"] = "Click Update to finish";
}
catch (Exception ex)
{
Session["ErrorMsg"] = ex.Message;
}
finally
{
excelReader.Close();
}
return View("viewUploadCards", cardsToUpdate);
}
10q all.
EDIT: download, reference and using
the dll is avalibale hare
i add the reference to the Excel.dll and i add the using Excel;
The problem might be caused by writing the file to the disk. Cloud providers usually do not allow the applications to write to the disk.
In your case it seems that the file is written to the disk only temporary and is loaded directly to the DB. You should be able to open the stream from the uploaded file directly and write it directly to the DB - without writing to the disk.
Check the exception which you are stocking in the Session - you should find more information there.
#hoonzis is right, writing files to disk in the cloud is not permited(you can't event get or set a path for the file). You should use blob storage, much more efficient for files and is cheaper than sql. I recommend to use the table storage service, it is noSQL but it's more cheaper than azure sql.
Use azure sql only if it is a must for your solution.
Blob storage see more details here: http://www.windowsazure.com/en-us/develop/net/how-to-guides/blob-storage/
Table storage: http://www.windowsazure.com/en-us/develop/net/how-to-guides/table-services/
Some more information about choosing the right storage you can find here: http://www.windowsazure.com/en-us/develop/net/fundamentals/cloud-storage-scenarios/
string Newfilename;
string Defaultfilename;
protected void btnup_Click(object sender, EventArgs e)
{
if (ASPxUploadControl1.HasFile)
{
string fileExt =
Path.GetExtension(ASPxUploadControl1.FileName);
if (fileExt == ".xls" || fileExt == ".xlsx")
try
{
string extension = Path.GetExtension(ASPxUploadControl1.FileName);
string id = Guid.NewGuid().ToString();
string fileLocation = string.Format("{0}/{1}{2}", Server.MapPath("upload/"), id, extension);
ASPxUploadControl1.SaveAs( fileLocation );
StatusLabel.Text = "Upload status: File uploaded!";
Newfilename = fileLocation;
Defaultfilename = Path.GetFileName(ASPxUploadControl1.FileName);
}
catch (Exception ex)
{
StatusLabel.Text = "Upload status: The file could not be uploaded. The following error occured: " + ex.Message;
}
else
{
StatusLabel.Text = "Please choose excel file";
}
}
}
I am trying to assign values to Newfilename and Defaultfilename (inside "try", after naming uploaded file), but they stay empty.
Where I'm wrong?
Refactor your code and think about the process that you want .. then Debug the Code.. Test it.. and if you have an Issue then edit your post.. that's what I suggest..
If statements should be wrapped with in a code block "{ }" same way that you have Try {} a good rule of thumb for even readability would be to wrap everthing around {} if you have If Else otherwise it makes if hard to read as well as lend assistance.
inside your code where you are declaring the following, make them variables within the method itself
string fileExt = string.Empty;
string extension = string.Empty;
string id = string.Empty;
string fileLocation = string.Empty;
so your method would look like this
protected void btnup_Click(object sender, EventArgs e)
{
string fileExt = string.Empty;
string extension = string.Empty;
string id = string.Empty;
string fileLocation = string.Empty;
if (ASPxUploadControl1.HasFile)
{
fileExt = Path.GetExtension(ASPxUploadControl1.FileName);
if (fileExt == ".xls" || fileExt == ".xlsx")
{
try
{
extension = Path.GetExtension(ASPxUploadControl1.FileName);
id = Guid.NewGuid().ToString();
fileLocation = string.Format("{0}/{1}{2}", Server.MapPath("upload/"), id, extension);
ASPxUploadControl1.SaveAs( fileLocation );
StatusLabel.Text = "Upload status: File uploaded!";
Newfilename = fileLocation;
Defaultfilename = Path.GetFileName(ASPxUploadControl1.FileName);
}
catch (Exception ex)
{
StatusLabel.Text = "Upload status: The file could not be uploaded. The following error occured: " + ex.Message;
}
}
else
{
StatusLabel.Text = "Please choose excel file";
}
}
}
Path.GetExtension returns null if the passed value is null and returns string.Empty if the passed value doesn't have an extension.
So please check if the value inside ASPxUploadControl1.FileName actually contains something usefull.
If this is not the case then you'll have to look up where the value is set and debug from there to find out why it's not set.
Can you step through the execution?
Does
NewFilename = fileLocation;
get executed?
If so, what are the values for NewFilename before and after?
This looks like ASP.Net code.
If it is. Is the problem that when you try to use NewFilename elsewhere in the code-behind is is blank.
If you are, then NewFilename may need to be saved to the session to allow you to use it.
hth,
Alan.