I am uploading an Excel file and extracting data from that and saving it into a database. I am using MVC4 .NET Framework. This is my code from class:
public static void Upload(HttpPostedFileBase File)
{
NIKEntities1 obj = new NIKEntities1();
MyApp = new Excel.Application();
MyApp.Visible = false;
string extension = System.IO.Path.GetExtension(File.FileName);
string pic = "Excel" + extension;
string path = System.IO.Path.Combine(System.Web.HttpContext.Current.Server.MapPath("~/Excel"), pic);
File.SaveAs(path);
MyBook = MyApp.Workbooks.Open(path);
MySheet = (Excel.Worksheet)MyBook.Sheets[1]; // Explicit cast is not required here
int lastRow = MySheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell).Row;
List<Employee> EmpList = new List<Employee>();
for (int index = 2; index <= lastRow; index++)
{
System.Array MyValues = (System.Array)MySheet.get_Range("A" +
index.ToString(), "B" + index.ToString()).Cells.Value;
EmpList.Add(new Employee
{
BatchID = MyValues.GetValue(1, 1).ToString(),
BatchName = MyValues.GetValue(1, 2).ToString()
});
}
for (int i = 0; i < EmpList.Count; i++)
{
int x=obj.USP_InsertBatches(EmpList[i].BatchID, EmpList[i].BatchName);
}
}
}
class Employee
{
public string BatchID;
public string BatchName;
}
This code is working perfectly the first time but next time it says that file is currently in use. So I thought of deleting the file at the end of code using the following line:
File.Delete(path);
But this line threw error:
HttpPostedFileBase does not contain definition for Delete
Also, if I don't write this line and try to execute code again it says that it can't save because a file exists with same name and could not be replaced because it is currently in use.
What should I do to get rid of this:
(File.Delete()) Error
Any other way of accessing the Excel file which I am receiving without saving will also be very helpful because I have to just access the data one time.
The File you use there is your variable that is the input parameter of your method. That parameter is of type HttpPostedFileBase and that type has no instance methods (nor static ones for that matter) that allow you to delete that File instance.
You are probably looking for the static Delete method on the File type that is in the System.IO namespace.
A quickfix would be to be explicit about which File you mean:
System.IO.File.Delete(path);
You might want to consider a different naming guideline for your variables though. In c# we tend to write variables starting with a lower case letter. Almost all types in the framework start with an Uppercase letter. Which makes it easier to distinguish the thing file and the type File.
Do notice that a file can only be deleted if it is closed by all processes and all file handles are cleared by the filesystem. In your case you have to make sure Excel closed the file and released it's handles. If you have the search indexer running or a rough virus scanner you might have to try a few times before giving up.
I normally use this code:
// make sure here all Ole Automation servers (like Excel or Word)
// have closed the file (so close the workbook, document etc)
// we iterate a couple of times (10 in this case)
for(int i=0; i< 10; i++)
{
try
{
System.IO.File.Delete(path);
break;
} catch (Exception exc)
{
Trace.WriteLine("failed delete {0}", exc.Message);
// let other threads do some work first
// http://blogs.msmvps.com/peterritchie/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program/
Thread.Sleep(0);
}
}
From what I can tell, you are opening Excel, reading the file but never closing the Excel.
Add:
MyApp.Workbooks.Close();
MyApp.Quit();
at the end of the Upload function. Even better, wrap whole code you got in
try{
//here goes your current code
}
catch(Exception e)
{
//manage exception
}
finally
{
MyApp.Workbooks.Close();
MyApp.Quit();
}
You initialize MyApp outside try catch block, then whatever happens close the file.
Related
I am attempting to get the metadata from a few music files and failing miserably. Online, there seems to be absolutely NO HOPE in finding an answer; no matter what I google. I thought it would be a great time to come and ask here because of this.
The specific error I got was: Error HRESULT E_FAIL has been returned from a call to a COM component. I really wish I could elaborate on this issue, but I'm simply getting nothing back from the COMException object. The error code was -2147467259, and it in hex is -0x7FFFBFFB, and Microsoft have not documented this specific error.
I 70% sure that its not the file's fault. My code will run through a directory full of music and convert the file into a song, hence the ConvertFileToSong name. The function would not be running if the file were to not exist is what I'm trying to say.
The only thing I can really say is that I'm using Dotnet 6, and have a massive headache.
Well, I guess I could also share another problem I had before this error showed up. Dotnet6 has top level code or whatever its called, this means that I can't add the [STAThread] attribute. To solve this, I simply added the code bellow to the top. Not sure why I have to set it to unknown, but that's what I (someone else on Stack Overflow) have to do. That solved that previous problem that the Shell32 could not start, but could that be causing my current problem? Who knows... definitely not me.
Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown);
Thread.CurrentThread.SetApartmentState(ApartmentState.STA);
Here is the code:
// Help from: https://stackoverflow.com/questions/37869388/how-to-read-extended-file-properties-file-metadata
public static Song ConvertFileToSong(FileInfo file)
{
Song song = new Song();
List<string> headers = new List<string>();
// initialise the windows shell to parse attributes from
Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder objFolder = null;
try
{
objFolder = shell.NameSpace(file.FullName);
}
catch (COMException e)
{
int code = e.ErrorCode;
string hex = code.ToString();
Console.WriteLine("MESSAGE: " + e.Message + ", CODE: " + hex);
return null;
}
Shell32.FolderItem folderItem = objFolder.ParseName(file.Name);
// the rest of the code is not important, but I'll leave it there anyway
// pretty much loop infinetly with a counter better than
// while loop because we don't have to declare an int on a new
// line
for (int i = 0; i < short.MaxValue; i++)
{
string header = objFolder.GetDetailsOf(null, i);
// the header does not exist, so we must exit
if (String.IsNullOrEmpty(header)) break;
headers.Add(header);
}
// Once the code works, I'll try and get this to work
song.Title = objFolder.GetDetailsOf(folderItem, 0);
return song;
}
Good night,
Diseased Finger
Ok, so the solution isn't that hard. I used file.FullName which includes the file's name, but Shell32.NameSpace ONLY requires the directory name (discluding the file name).
This is the code that fixed it:
public static Song ConvertFileToSong(FileInfo file)
{
// .....
Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder objFolder = file.DirectoryName;
Shell32.FolderItem folderItem = objFolder.ParseName(file.Name);
// .....
return something;
}
I have an app that reads from text files to determine which reports should be generated. It works as it should most of the time, but once in awhile, the program deletes one of the text files it reads from/writes to. Then an exception is thrown ("Could not find file") and progress ceases.
Here is some pertinent code.
First, reading from the file:
List<String> delPerfRecords = ReadFileContents(DelPerfFile);
. . .
private static List<String> ReadFileContents(string fileName)
{
List<String> fileContents = new List<string>();
try
{
fileContents = File.ReadAllLines(fileName).ToList();
}
catch (Exception ex)
{
RoboReporterConstsAndUtils.HandleException(ex);
}
return fileContents;
}
Then, writing to the file -- it marks the record/line in that file as having been processed, so that the same report is not re-generated the next time the file is examined:
MarkAsProcessed(DelPerfFile, qrRecord);
. . .
private static void MarkAsProcessed(string fileToUpdate, string
qrRecord)
{
try
{
var fileContents = File.ReadAllLines(fileToUpdate).ToList();
for (int i = 0; i < fileContents.Count; i++)
{
if (fileContents[i] == qrRecord)
{
fileContents[i] = string.Format("{0}{1} {2}"
qrRecord, RoboReporterConstsAndUtils.COMPLETED_FLAG, DateTime.Now);
}
}
// Will this automatically overwrite the existing?
File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);
}
catch (Exception ex)
{
RoboReporterConstsAndUtils.HandleException(ex);
}
}
So I do delete the file, but immediately replace it:
File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);
The files being read have contents such as this:
Opas,20170110,20161127,20161231-COMPLETED 1/10/2017 12:33:27 AM
Opas,20170209,20170101,20170128-COMPLETED 2/9/2017 11:26:04 AM
Opas,20170309,20170129,20170225-COMPLETED
Opas,20170409,20170226,20170401
If "-COMPLETED" appears at the end of the record/row/line, it is ignored - will not be processed.
Also, if the second element (at index 1) is a date in the future, it will not be processed (yet).
So, for these examples shown above, the first three have already been done, and will be subsequently ignored. The fourth one will not be acted on until on or after April 9th, 2017 (at which time the data within the data range of the last two dates will be retrieved).
Why is the file sometimes deleted? What can I do to prevent it from ever happening?
If helpful, in more context, the logic is like so:
internal static string GenerateAndSaveDelPerfReports()
{
string allUnitsProcessed = String.Empty;
bool success = false;
try
{
List<String> delPerfRecords = ReadFileContents(DelPerfFile);
List<QueuedReports> qrList = new List<QueuedReports>();
foreach (string qrRecord in delPerfRecords)
{
var qr = ConvertCRVRecordToQueuedReport(qrRecord);
// Rows that have already been processed return null
if (null == qr) continue;
// If the report has not yet been run, and it is due, add i
to the list
if (qr.DateToGenerate <= DateTime.Today)
{
var unit = qr.Unit;
qrList.Add(qr);
MarkAsProcessed(DelPerfFile, qrRecord);
if (String.IsNullOrWhiteSpace(allUnitsProcessed))
{
allUnitsProcessed = unit;
}
else if (!allUnitsProcessed.Contains(unit))
{
allUnitsProcessed = allUnitsProcessed + " and "
unit;
}
}
}
foreach (QueuedReports qrs in qrList)
{
GenerateAndSaveDelPerfReport(qrs);
success = true;
}
}
catch
{
success = false;
}
if (success)
{
return String.Format("Delivery Performance report[s] generate
for {0} by RoboReporter2017", allUnitsProcessed);
}
return String.Empty;
}
How can I ironclad this code to prevent the files from being periodically trashed?
UPDATE
I can't really test this, because the problem occurs so infrequently, but I wonder if adding a "pause" between the File.Delete() and the File.WriteAllLines() would solve the problem?
UPDATE 2
I'm not absolutely sure what the answer to my question is, so I won't add this as an answer, but my guess is that the File.Delete() and File.WriteAllLines() were occurring too close together and so the delete was sometimes occurring on both the old and the new copy of the file.
If so, a pause between the two calls may have solved the problem 99.42% of the time, but from what I found here, it seems the File.Delete() is redundant/superfluous anyway, and so I tested with the File.Delete() commented out, and it worked fine; so, I'm just doing without that occasionally problematic call now. I expect that to solve the issue.
// Will this automatically overwrite the existing?
File.Delete(fileToUpdate);
File.WriteAllLines(fileToUpdate, fileContents);
I would simply add an extra parameter to WriteAllLines() (which could default to false) to tell the function to open the file in overwrite mode, and not call File.Delete() at all then.
Do you currently check the return value of the file open?
Update: ok, it looks like WriteAllLines() is a .Net Framework function and therefore cannot be changed, so I deleted this answer. However now this shows up in the comments, as a proposed solution on another forum:
"just use something like File.WriteAllText where if the file exists,
the data is just overwritten, if the file does not exist it will be
created."
And this was exactly what I meant (while thinking WriteAllLines() was a user defined function), because I've had similar problems in the past.
So, a solution like that could solve some tricky problems (instead of deleting/fast reopening, just overwriting the file) - also less work for the OS, and possibly less file/disk fragmentation.
I am trying to work through a school assignment that has us use a C# program to parse data from a CSV file and add it to a table in a local database. When I try to run the program though, the method I am using fails to parse any of the data into the object.
Here is the method I am using:
//Parse CSV line
public bool ParseCSVline(string aLine)
{
try
{
string[] fields = aLine.Split(',');
this.Item_ID = int.Parse(fields[0]);
this.Invent_id = int.Parse(fields[1]);
this.Itemsize = fields[2];
this.Color = fields[3];
this.Curr_price = decimal.Parse(fields[4]);
this.Qoh = int.Parse(fields[5]);
return true; //if everything parsed, return true
}
catch (Exception ex)
{
Console.Write("Failed to Parse");
return false; //if a parse failed, return false
}
When running the program the method keeps throwing the Exception instead of actually parsing the data. For clarity, here is the section in the Main program that is calling everything:
/Step 2 - Open input file
//Set where the file comes from
string filepath = #"C:\Users\Karlore\Documents\School\SAI-430\";
string filename = #"NewInventory.csv";
//Open reader
StreamReader theFile = new StreamReader(filepath + filename);
//Step 3 - Create an object to use
Item theItem = new Item();
//Step 4 - Loop through file and add to database
while (theFile.Peek() >= 0)
{
//Get one line and parse it inside the object
theItem.ParseCSVline(filename);
//Check to see if item is already there
if (theItem.IsInDatabase(connection))
{
continue;
}
else
{
//Add the new item to the database if it wasn’t already there
theItem.AddRow(connection);
}
} //end of while loop
If anyone can point out where I may have made an error, or point me in the right direction I would appreciate it.
Replace the line:
theItem.ParseCSVline(filename);
by:
theItem.ParseCSVline(theFile.ReadLine());
So the title might be bit misleading, but what I wanted to accomplish is reading an array of files and then combine them into one, which is where I am now.
The problem is that I have a catch that looks for the exception "FileNotFoundException", when this is called I want to continue my try statement (Using "continue") but let the user know that the file is missing.
My setup is a class that is called from a form (It's in the form where the error should show up)
I thought about creating an event that can be registered from my form, but is that the right way?
public void MergeClientFiles(string directory)
{
// Find all clients
Array clients = Enum.GetValues(typeof(Clients));
// Create a new array of files
string[] files = new string[clients.Length];
// Combine the clients with the .txt extension
for (int i = 0; i < clients.Length; i++)
files[i] = clients.GetValue(i) + ".txt";
// Merge the files into directory
using (var output = File.Create(directory))
{
foreach (var file in files)
{
try
{
using (var input = File.OpenRead(file))
{
input.CopyTo(output);
}
}
catch (FileNotFoundException)
{
// Its here I want to send the error to the form
continue;
}
}
}
}
You want the method to do its job and report user about problems, right?
Then Oded has suggested right thing. With small modification, the code could look like this:
public List<string> MergeClientFiles( string path )
{
// Find all clients
Array clients = Enum.GetValues( typeof( Clients ) );
// Create a new array of files
string[] files = new string[clients.Length];
// Combine the clients with the .txt extension
for( int i = 0; i < clients.Length; i++ )
files[i] = clients.GetValue( i ) + ".txt";
List<string> errors = new List<string>();
// Merge the files into AllClientData
using( var output = File.Create( path ) ) {
foreach( var file in files ) {
try {
using( var input = File.OpenRead( file ) ) {
input.CopyTo( output );
}
}
catch( FileNotFoundException ) {
errors.Add( file );
}
}
}
return errors;
}
Then, in caller you just check if MergeClientFiles returns non-empty collection.
You can collect the exceptions into a List<FileNotFoundException> and at the end of iteration, if the list is not empty, throw a custom exception assigning this list to a corresponding member.
This will allow any code calling the above to catch your custom exception, iterate over the FileNotFoundExceptions and notify the user.
you could define a delegate that you pass as an argument of your method.
public delegate void FileNotFoundCallback(string file);
public void MergeClientFiles(string directory, FileNotFoundCallback callback)
{
// Find all clients
Array clients = Enum.GetValues(typeof(Clients));
// Create a new array of files
string[] files = new string[clients.Length];
// Combine the clients with the .txt extension
for (int i = 0; i < clients.Length; i++)
files[i] = clients.GetValue(i) + ".txt";
// Merge the files into directory
using (var output = File.Create(directory))
{
foreach (var file in files)
{
try
{
using (var input = File.OpenRead(file))
{
input.CopyTo(output);
}
}
catch (FileNotFoundException)
{
// Its here I want to send the error to the form
callback( file );
continue;
}
}
}
}
Rather than catching FileNotFoundExceptions you should actively check if the file exists and then don't try to open it if it doesn't.
You can change the method to return a list of merged files, a list of missing files, or a list of all files along with an indicator if they were merged or missing. Returning a single list gives the caller the option to process the missing files all at once and know how many were missing, instead of one-by-one as would be the case with an event or callback.
For some inspiration, have a look at the documentation for the new parallel constructs in c#, such as Parallel.For and the Reactive Framework (rx).
In the first, exceptions are collected in an AggregateException, in Rx exceptions are communicated via a callback interface.
I think I prefer the approach used in Parallel.For, but choose what fits your scenario best.
I am trying to create a file with a FileInfo object and I am getting strange behavior.
Here is the gist of what I am doing -
public void CreateLog()
{
FileInfo LogFile = new FileInfo("");
if (!LogFile.Directory.Exists) { LogFile.Directory.Create(); }
if (!LogFile.Exists) { LogFile.Create(); }
if (LogFile.Length == 0)
{
using (StreamWriter Writer = LogFile.AppendText())
{
Writer.WriteLine("Quotes for " + Instrument.InstrumentID);
Writer.WriteLine("Time,Bid Size,Bid Price,Ask Price,Ask Size");
}
}
}
However, when it checks to see the length of the logfile, it says that the file does not exist (I checked - it does exist).
When I substitute LogFile.Length with the following:
File.ReadAllLines(LogFile.FullName).Length;
Then I get an exception that says that it cannot access the file because something else is already accessing it.
BUT, if I do a Thread.Sleep(500) before I do ReadAllLines, then it seems to work fine.
What am I missing?
LogFile.Create() if you user this function ,you may lock the file, so you can use using ,like this
using(LogFile.Create()){}
after that you can use the file again