I cannot figure out why I keep getting a null ref on filename when I'm clearly calling this singleton and it should be calling the Logger() to set the filename variable:
public class Logger
{
private static Logger defaultLogger = null;
readonly string filename;
public static Logger DefaultLogger
{
get
{
// Check for valid instance
if (defaultLogger == null)
defaultLogger = new Logger();
// Return instance
return defaultLogger;
}
}
private Logger()
{
filename = ConfigurationManager.AppSettings["MyLogPath"];
}
public string Filename
{
get { return this.filename; }
}
public void Write(EntryType type, string data)
{
lock (this)
{
using (StreamWriter writer = new StreamWriter(filename, true))
{
//do something
}
}
}
}
Here's how I'm calling this class:
Logger.DefaultLogger.Write(EntryType.Error, e.ToString());
So I get this error during runtime saying that filename is null:
Value cannot be null.
Parameter name: path
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: path
Source Error:
Line 49:
Line 50:
Line 51: public string Filename
Line 52: {
Line 53: get { return this.filename; }
This was our original code (I did not write this), same problem:
public class Logger
{
public static Logger DefaultLogger = new Logger();
string filename;
public Logger()
{
filename = ConfigurationManager.AppSettings["LogPath"];
}
public Logger(string filename)
{
this.filename = filename;
}
public string Filename
{
get { return this.filename; }
}
public void Write(LogEntryType type, string data)
{
lock ()
{
using (StreamWriter writer = new StreamWriter(filename, true))
{
...
}
}
}
Is it because
ConfigurationManager.AppSettings["MyLogPath"];
is null?
Check that your App.Config file is there ( it takes the form of yourexe.exe.config in your bin folder). You App.Config should have the following lines:
<configuration>
<appSettings>
<add key="MyLogPath" value="C:\Simple.txt" />
<appSettings>
</configuration>
Maybe in order to test, you can temporary set the filename to a well-know path ( filename=#"D:\C#\mytext.txt";) and see whether you get the error or not.
If I set the filename explicitly, then I won't have such an error, OTOH,if I set
filename=ConfigurationManager.AppSettings["MyLogPath"];
then I will get a
System.ArgumentNullException : Value
cannot be null. Parameter name: path
at
System.IO.StreamWriter..ctor(String
path, Boolean append, Encoding
encoding, Int32 bufferSize) at
System.IO.StreamWriter..ctor(String
path, Boolean append)
If I stepped using a debugger, I can see that it failed at:
(StreamWriter writer = new StreamWriter(filename, true))
I don't know why your debugger won't hit the constructor. My debugger hit the constructor in both cases.
Without looking into it too much..
private Logger()
{
filename = ConfigurationManager.AppSettings["MyLogPath"];
throw new Exception("filename = "+filename);
}
Does the exception get thrown?
I copy pasted the code and replaced
filename = ConfigurationManager.AppSettings["MyLogPath"];
with
filename = #"test.log";
And it works fine. So your error is in the spelling of "MyLogPath" or in the app.config .
Static initilaizers are executed when the class definition is first accessed, which in your case means they are executed before your private constructor.
You're using a lock, so you expect this to be called in a multithreaded scenario, but you don't synchronize DefaultLogger. Also, are you sure you're setting fileName to something non-null in the first place?
Try assigning your Logger to instance variable in your client code and accessing it's FileName. Perhaps it's not getting assigned correctly.
Also, do you need to have filename as readonly if you're only providing a get property?
Related
Visual Studio has the option to apply a Build Action for the App.config file as "Embedded Resource", which means including in the same final exe the content of the App.config.
Fine.
The problem is: how to read the data inside the embedded App.config? For example an appSetting value from a given key?
The code I used before to read from the App.config (the one phisically written on the disk which usually is nameoftheprogram.exe.config), seems to be not working anymore.
string s = System.Configuration.ConfigurationManager.AppSettings["mykey"];
Probably it must be re-adapted with other C# classes designed for this job.
Any ideas?
You can have interface IConfigUtility with method :
IConfigUtility.cs:
public interface IConfigUtility
{
string LogFilePath
{
get;
}
string GetAppSetting(string key);
}
ConfigUtility.cs
using System;
using System.Configuration;
public class ConfigUtility : IConfigUtility
{
Configuration config = null;
public string LogFilePath
{
get
{
return GetAppSetting(#"Code to read the log file path");
}
}
public ConfigUtility()
{
var exeConfigPath = this.GetType().Assembly.Location;
try
{
config = ConfigurationManager.OpenExeConfiguration(exeConfigPath);
}
catch (Exception)
{
//handle error here.. means DLL has no satellite configuration file.
}
}
public virtual string GetAppSetting(string key)
{
if (config != null)
{
KeyValueConfigurationElement element = config.AppSettings.Settings[key];
if (element != null)
{
string value = element.Value;
if (!string.IsNullOrEmpty(value))
return value;
}
}
return string.Empty;
}
}
Now you can use the above ConfigUtility.cs and read your appsettings key from the App.config file
I am writing a method that take a string containing a message and write this string into a log file.
I have done in this way:
internal static void WriteLogFile(string messageLog)
{
if (messageLog == "")
{
messageLog = "L'import delle regole di inoltro è andato a buon fine. Tutte le regole di inoltro sono state inserite";
}
try
{
var filePath = new Uri(Assembly.GetEntryAssembly().GetName().CodeBase);
Thread.CurrentThread.CurrentCulture = new CultureInfo("it-IT");
CultureInfo ci = new CultureInfo("it-IT");
File.WriteAllText(filePath + "log.txt", messageLog);
Thread.CurrentThread.CurrentCulture = new CultureInfo("it-IT");
}
catch (Exception ex)
{
throw ex;
}
}
The problem is that when perform this line:
File.WriteAllText(filePath + "log.txt", messageLog);
I am obtaining the following exception:
"URI formats are not supported."
What is wrong? What am I missing? How can I try to fix it?
Because WriteAllText does not support a URI format, and you're using a URI.
Per https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writealltext?view=netframework-4.8, you need to pass it a string path.
As others have suggested, you should use GetPath if you want to create the file locally, or some other method depending where you want the file to go.
Try this class:
using System;
using System.IO;
namespace YourNameSpace.Models
{
public class Logger
{
private Object Locker { get; set; }
public string Path { get; set; }
public Logger(string path)
{
Locker = new Object();
Path = path;
}
public void Log(string message, params object[] args)
{
lock (Locker)
{
string messageToLog = string.Format("{0} - {1}", DateTime.Now, string.Format(message, args));
string path = System.IO.Path.Combine(Path, string.Format("{0}.txt", DateTime.Today.ToString("yyyyMMdd")));
Directory.CreateDirectory(Path);
File.AppendAllLines(path, new string[] { messageToLog });
}
}
}
}
I'm assuming your problem is to write a log file in the same folder as your executable. Try to use the Location property:
var filePath = Assembly.GetEntryAssembly().Location;
That will return a valid path that you can concatenate with the file name, or use Path.Combine method.
This is my logging.cs, It usually may create the "Log-Folder" and the Datetime.csv into the users Desktop
public static class Logging
{
public static string _Path = $"C:\\Users\\{Environment.UserName}\\Desktop\\Logs\\{DateTime.Now.ToString("dd.MM.yyyy")}.csv";
static StreamWriter _File = new StreamWriter(_Path);
public static void getPath(string path)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
}
public static void logging(string message)
{
_File.Write(message);
}
}
In my main class, i use the method logging just to enter "Test" into the csv file
class Program
{
static void Main(string[] args)
{
Logging.getPath(Logging._Path);
Logging.logging("Test");
}
}
but when there is no "Logs-Folder", i get the exception that part of the path doesn´t exist. If i create the path manually, i get the exception, that the path already exists, so something's wrong with the If-Statement up in the Logging-class. But i don't know what the heck works wrong
Your path is a file and not a directory. You need to create the directory from your path
String Path = $"C:\\Users\\{Environment.UserName}\\Desktop\\Logs\\{DateTime.Now.ToString("dd.MM.yyyy")}.csv";
String Directory = System.IO.Path.GetDirectoryName(Path);
if (System.IO.Directory.Exists(Directory)==false) {
System.IO.Directory.CreateDirectory(Directory);
}
if (System.IO.File.Exists(Path)==false) {
System.IO.File.Create(Path);
}
Your _Path variable isn't actually a directory, but rather a filename.
You get the Directory with System.IO.Path.GetDirectoryName(_Path)
Your testing if an Directory exists but your giving the path to a File. Here's some code you could use to fix it:
public static string _Path = $"C:\\Users\\{Environment.UserName}\\Desktop\\Logs";
public static string _Filename = $"{DateTime.Now.ToString("dd.MM.yyyy")}.csv";
static StreamWriter _File = new StreamWriter(_File);
Try to take DirectoryPath and FilePath differently.
Move your StreamWriter to method scope so we can close this stream after Write content inside file.
public static class Logging
{
public static string _DirectoryPath = $"C:\\Users\\{Environment.UserName}\\Desktop\\Logs";
public static string _FileName = $"{DateTime.Now.ToString("dd.MM.yyyy")}.csv";
public static void getPath(string path)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
}
public static void logging(string message)
{
StreamWriter _sw = new StreamWriter(_DirectoryPath + "\\" + _FileName);
_sw.Write(message);
_sw.Flush();
_sw.Close();
}
}
And from Program.cs.
Logging.getPath(Logging._DirectoryPath);
Logging.logging("Test");
Output:
I'm new in VisualStudio, so I haven't got much experience with the unit testing in it.
I try to write a basic file reader class, wich has two variables: path and lines string list.
The path can be given to the constructor, or also can be set with a setter method.
There is a readIn function, which load up the list with the lines of the text file can be found on the path.
The function should chek if the path is not null or is it valid (is the path pointing to an existing file). If not, it throws a System.ArgumentNullException or a throw new System.ArgumentException.
When I try to test that the exceptions are thrown in case of the path is null or not valid, the VisualStudio always stuck and says that I have an unhadnled exception, however, I defined the expected exception before the test method.
Have you got any idea how should I do this in correct way?
Or if it is the correct way (I read about it also here and on the msdn page too, so it should work), what should I do to get it work?
The reader class:
using System;
using System.Collections.Generic;
using System.IO;
namespace LogParserConsole
{
public class FileReader
{
private string path;
private List<string> lines;
public FileReader()
{
path = null;
lines = null;
}
public FileReader(string path)
{
this.path = path;
if (isFileExists())
{
lines = new List<string>();
}
else
{
lines = null;
}
}
public void readIn()
{
if(path == null)
{
throw new System.ArgumentNullException("The path is not set. Please set a path first!");
}
else if(!isFileExists())
{
throw new System.ArgumentException("The given path is invalid, please chek it!");
}
string line;
System.IO.StreamReader file = new System.IO.StreamReader(path);
while ((line = file.ReadLine()) != null)
{
lines.Add(line);
Console.WriteLine(line);
}
file.Close();
}
public bool isFileExists()
{
return File.Exists(path);
}
public string getPath()
{
return this.path;
}
public List<string> getLines()
{
return this.lines;
}
public void setPath(string path)
{
this.path = path;
}
}
}
The unit test method for the exception:
[TestMethod]
[ExpectedException(typeof(System.ArgumentNullException))]
public void ReadInTest()
{
FileReader fr = new FileReader();
fr.readIn();
}
I also tried an example from the msdn blog page about the expected exceptions (https://blogs.msdn.microsoft.com/csell/2006/01/14/expectedexception-might-not-be-what-youve-expected/), but it also stopped at the exception and after the test failed.
It happened with this simple example code:
[TestMethod, ExpectedException( typeof ( ApplicationException ) ) ]
public void ut1()
{
throw new ApplicationException();
}
I think I should check my preferencies, but have no idea what should I search for.
Thanks for any advice!
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);
}