Writing unittest to create a file and then delete that file - c#

I have this method I created :
public static bool DeleteFile(FileInfo fileInfo)
{
try
{
fileInfo.Delete();
return true;
}
catch (Exception exception)
{
LogManager.LogError(exception);
return false;
}
}
Now I wrote the following unittest:
[TestMethod]
public void DeleteFileSuccessFul()
{
string fileName = "c:\\Temp\\UnitTest3.txt";
FileInfo fileInfo = new FileInfo(fileName);
File.Create(Path.Combine(fileName));
bool success = FileActions.DeleteFile(fileInfo);
Assert.IsTrue(success);
}
This test fails because the file is in use by a different proces.
The test fails on het bool success = FileActions.DeleteFile(fileInfo); because the file is in use by a different process.
How can I change my test so it works ?

You have to call Dispose method on the FileStream object returned by the File.Create method to release the handle to that file:
[TestMethod]
public void DeleteFileSuccessFul()
{
string fileName = "c:\\Temp\\UnitTest3.txt";
FileInfo fileInfo = new FileInfo(fileName);
using (File.Create(Path.Combine(fileName)))
{
}
bool success = FileActions.DeleteFile(fileInfo);
Assert.IsTrue(success);
}
UPDATE: using block provides a convenient syntax that ensures the Dispose method of an IDisposable object is get called after leaving the scope of the block even if an exception occurs. The equivalent to the above code could be re-written with try-finally block:
[TestMethod]
public void DeleteFileSuccessFul()
{
string fileName = "c:\\Temp\\UnitTest3.txt";
FileInfo fileInfo = new FileInfo(fileName);
FileStream fileStream = null;
try
{
fileStream = File.Create(Path.Combine(fileName));
}
finally
{
if (fileStream != null)
fileStream.Dispose();
}
bool success = FileActions.DeleteFile(fileInfo);
Assert.IsTrue(success);
}

Related

How to create directory on Android and IOS?

I create a Xamarin app and i want to serialize some data.
FileWorker class for Android:
public class FileWorker : IFileWorker
{
public Task<IEnumerable<string>> GetFilesAsync(string folder)
{
IEnumerable<string> filenames = from filepath in Directory.EnumerateFiles(GetDocsPath() + "/" + folder)
select Path.GetFileName(filepath);
return Task<IEnumerable<string>>.FromResult(filenames);
}
string GetFilePath(string filename)
{
return Path.Combine(GetDocsPath(), filename);
}
string GetDocsPath()
{
return System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
}
public Task SerializeAsync<T>(string filename, T obj)
{
string filepath = GetFilePath(filename);
DataContractJsonSerializer formatter = new DataContractJsonSerializer(typeof(T));
FileStream fs = new FileStream(filepath, FileMode.OpenOrCreate);
formatter.WriteObject(fs, obj);
return Task.FromResult(true);
}
public Task<T> DeserializeAsync<T>(string filename)
{
DataContractJsonSerializer formatter = new DataContractJsonSerializer(typeof(T));
T obj;
FileStream fs = new FileStream(filename, FileMode.OpenOrCreate);
obj = (T)formatter.ReadObject(fs);
return Task.FromResult(obj);
}
}
And Add method:
public async void AddObjectAsync(object obj)
{
var fv = DependencyService.Get<IFileWorker>();
string path = AppSettings.ObjectsFolder + "/";
try
{
await fv.SerializeAsync(path + "obj", obj);
}
catch (Exception e) { }
}
But i catch Exception in this function:
System.IO.DirectoryNotFoundException
How to create this directory on Android (and IOS, if same Exception is possible)?
you can use the normal C# System.IO methods
using System.IO;
DirectoryInfo di = Directory.CreateDirectory(path);

When to close file stream

I reading a file in a zip file using streams like this :
public static Stream ReadFile(Models.Document document, string path, string password, string fileName)
{
FileStream fsIn = System.IO.File.OpenRead(Path.Combine(path, $"{document.guid}.crypt"));
var zipFile = new ZipFile(fsIn)
{
//Password = password,
IsStreamOwner = true
};
var zipEntry = zipFile.GetEntry(fileName);
//zipEntry.AESKeySize = 256;
Stream zipStream = zipFile.GetInputStream(zipEntry);
return zipStream;
}
I'm having trouble closing the filestream fsIn as it is not available when I return from the ReadFile method and if I close it within the method the stream zipStream that I'm returning will be closed.
How can I close fsIn but still read the data-stream returned from my method?
You should change your return type to be an object that contains both the parent FileStream and the inner file-stream so that the consumer of your function can close both streams when they need to - alternatively you could subclass Stream with your own type that then assumes ownership of both streams. It's probably simpler to do the former as otherwise you'd have to proxy all Stream methods.
Like so:
sealed class ReadFileResult : IDisposable
// (use a `sealed` class with a non-virtual Dispose method for simpler compliance with IDisposable)
{
private readonly FileStream zipFileStream;
public Stream InnerFileStream { get; }
internal ReadFileResult( FileStream zipFileStream, Stream innerFileStream )
{
this.zipeFileStream = zipFileStream;
this.InnerFileStream = innerFileStream;
}
public void Dispose()
{
this.InnerFileStream.Dispose();
this.zipFileStream.Dispose();
}
}
public static ReadFileResult ReadFile(Models.Document document, string path, string password, string fileName)
{
// ...
return new ReadFileResult( zipFileStream: fsIn, innerFileStream: zipStream );
}
Consumed like so:
void Foo()
{
using( ReadFileResult rfr = ReadFile( ... ) )
{
Stream s = rdr.InnerFileStream;
// ..
}
}

Writing to a blob stream - how to fail and clean up?

Using Azure storage, I'm writing to a blob using a stream. I have a method something like this:
public async Task<BlobSteamContainer> GetBlobStreamAsync(string filename, string contentType = "text/csv")
{
var blob = container.GetBlockBlobReference($"{filename}--{Guid.NewGuid().ToString()}.csv");
blob.Properties.ContentType = contentType;
return new BlobSteamContainer(blob.Uri.ToString(), await blob.OpenWriteAsync());
}
Where BlobStreamContainer is just a simple object so I can keep track of the filename and the stream together:
public class BlobSteamContainer : IDisposable
{
public CloudBlobStream Stream { get; private set; }
public string Filename { get; private set; }
public BlobSteamContainer(string filename, CloudBlobStream stream)
{
Stream = stream;
Filename = filename;
}
public void Dispose()
{
Stream.Close();
Stream?.Dispose();
}
}
And then I use it something like this:
using (var blobStream = await GetBlobStreamAsync(filename))
using (var outputStream = new StreamWriter(blobStream.Stream))
using (var someInputStream = ...)
{
try
{
outputStream.WriteLine("write some stuff...");
//....processing
if (someCondition) {
throw new MyException("can't write the file");
}
//....more processing
outputStream.Flush();
}
catch(MyException e)
{
// what to do here? I want to stop writing
// and remove any trace of the file in azure
throw; // let the higher ups handle this
}
}
Where somecondition is something that I know before hand (obviously there's more going on involving processing an input stream and writing out as I go). If everything is fine, then this works great. My problem is on figuring out the best way to handle the case where an exception is thrown during writing.
I tried just deleting the file in the catch like this:
DeleteBlob(blobStream.Filename);
where:
public void DeleteBlob(string filename)
{
var blob = container.GetBlobReference(filename);
blob.Delete();
}
But the problem is that the file might not have been created yet and so this will throw a Microsoft.WindowsAzure.Storage.StorageException telling me the file wasn't found (and then the file will end up getting created anyway!).
So what would be the cleanest way to handle this?

"activator.createinstance()" gives error when try to run

I was unable to solve the following error. I'll appreciate your help.
I have the following class:
public class GB
{
private StreamReader sr;
public GB()
{
sr = new StreamReader(new FileStream("C:\\temp.txt", FileMode.Open, FileAccess.ReadWrite));
}
public int MultiCall()
{
if (sr.ReadLine() == "test1")
return 1;
else
return 0;
}
}
in my form, there is button with the following function;
void buttonClick()
{
myAssembly = Assembly.LoadFrom(dllpath); // dllpath is the dll file for GB class
myType = myAssembly .GetType("GB");
myObject = Activator.CreateInstance(myType);
myMethodInfo = myType .GetMethod("MultiCall");
returnValue = myMethodInfo.Invoke(myObject, null);
myObject = null;
}
Here is my question; When i click the button for the first time, Everything is OK. But when i click it again i get the following error;
The process cannot access the file 'C:\temp.txt' because it is being used by another process.
The object returned from activator.createinstance doesnot seem to be nulled after first call. Although i assign it to a null value by myObject = null i still get the same error. Any ideas?
thx.
The constructor of GB opens a ReadWrite stream to the file, but then never closes it. The subsequent invocation of GB attempts to open the same file, but this obviously fails.
What you need to do is implement IDisposable on GB, which disposes of the StreamReader, e.g.:
public class GB : IDisposable
{
private StreamReader sr;
private bool _isDisposed;
public GB()
{
sr = new StreamReader(new FileStream("C:\\temp.txt", FileMode.Open, FileAccess.ReadWrite));
}
public int MultiCall()
{
if (sr.ReadLine() == "test1")
return 1;
else
return 0;
}
~GB()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool isManaged)
{
if(_isDisposed)
return;
if(isManaged)
{
// Ensure we close the file stream
sr.Dispose();
}
_isDisposed = true;
}
}
Then dispose of your GB instance once you are done with it.
// Dispose of the GB instance (which closes the file stream)
var asDisposable = (IDisposable)myObject;
asDisposable.Dispose();
IDisposable exists for exactly this reason, to ensure unmanaged resources are successfully released.
Store the value returned by the file then check it value in the MultiCall method. This way you file gets closed and disposed for you.
public class GB
{
private string str;
public GB()
{
using (var sr = new StreamReader(new FileStream("C:\\temp.txt", FileMode.Open, FileAccess.ReadWrite)))
{
str = sr.ReadToEnd();
}
}
public int MultiCall()
{
if (str == "test1")
return 1;
else
return 0;
}
}

Read an Excel spreadsheet in memory

How can I read an Excel spreadsheet that was just posted to my server?
I searched for something but I only found how to read an Excel spreadsheet with the file name path which is not my case.
I need something like that:
public ActionResult Import(HttpPostedFileBase file)
{
var excel = new ExcelQueryFactory(file); //using linq to excel
}
I was running into your same issue but I didn't want to switch to a paid service so this is what I did.
public class DataImportHelper : IDisposable
{
private readonly string _fileName;
private readonly string _tempFilePath;
public DataImportHelper(HttpPostedFileBase file, string tempFilePath)
{
_fileName = file.FileName;
_tempFilePath = Path.Combine(tempFilePath, _fileName);
(new FileInfo(_tempFilePath)).Directory.Create();
file.SaveAs(_tempFilePath);
}
public IQueryable<T> All<T>(string sheetName = "")
{
if (string.IsNullOrEmpty(sheetName))
{
sheetName = (typeof (T)).Name;
}
var excelSheet = new ExcelQueryFactory(_tempFilePath);
return from t in excelSheet.Worksheet<T>(sheetName)
select t;
}
public void Dispose()
{
File.Delete(_tempFilePath);
}
}
Here is a Test
[Fact]
public void AcceptsAMemoryStream()
{
MemoryFile file;
using (var f = File.OpenRead("SampleData.xlsx"))
{
file = new MemoryFile(f, "multipart/form-data", "SampleData.xlsx");
using (var importer = new DataImportHelper(file, "Temp/"))
{
var products = importer.All<Product>();
Assert.NotEmpty(products);
}
}
}
Here is MemoryFile.cs. This file is only used for testing. It is just an implementation of HttpPostedFileBase so you can test your controllers and my little helper. This was borrowed from another post.
public class MemoryFile : HttpPostedFileBase
{
Stream stream;
string contentType;
string fileName;
public MemoryFile(Stream stream, string contentType, string fileName)
{
this.stream = stream;
this.contentType = contentType;
this.fileName = fileName;
}
public override int ContentLength
{
get { return (int)stream.Length; }
}
public override string ContentType
{
get { return contentType; }
}
public override string FileName
{
get { return fileName; }
}
public override Stream InputStream
{
get { return stream; }
}
public override void SaveAs(string filename)
{
using (var file = File.Open(filename, FileMode.Create))
stream.CopyTo(file);
}
}
Unfortunately it's not possible to read a spreadsheet from a stream with LinqToExcel.
That's because it uses OLEDB to read from the spreadsheets and it can't read from a stream.
You can use the InputStream property of HttpPostedFileBase to read the excel spreadsheet in memory.
I use ClosedXML nuget package to read excel content from stream which is available in your case. It has a simple overload which takes stream pointing to stream for the excel file (aka workbook).
imported namespaces at the top of the code file:
using ClosedXML.Excel;
Source code:
public ActionResult Import(HttpPostedFileBase file)
{
//HttpPostedFileBase directly is of no use so commented your code
//var excel = new ExcelQueryFactory(file); //using linq to excel
var stream = file.InputStream;
if (stream.Length != 0)
{
//handle the stream here
using (XLWorkbook excelWorkbook = new XLWorkbook(stream))
{
var name = excelWorkbook.Worksheet(1).Name;
//do more things whatever you like as you now have a handle to the entire workbook.
var firstRow = excelWorkbook.Worksheet(1).Row(1);
}
}
}
You need Office Interops assemblies. Check the Excel Object Model for reference.

Categories