Avoiding nested using blocks when outer is used only to construct inner - c#

Often, the IDisposable object of a using block is itself constructed from another IDisposable object, e.g.
using (FileStream stream = File.Open(path, FileMode.Open))
using (MyObject obj = new MyObject(stream))
{
// do something with obj
}
Unfortunately the code above keeps the file stream open until the MyObject object is disposed.
To dispose of the file stream as soon as the MyObject constructor has completed, I could instead write:
MyObject CreateMyObject(string path)
{
using (FileStream stream = File.Open(path, FileMode.Open))
{
return new MyObject(stream);
}
}
using (MyObject obj = CreateMyObject(path))
{
// do something with obj
}
But I don't like the verbosity of this solution. I tried replacing CreateMyObject() with a lambda but I failed to find a legal syntax. Is there a way to do this without calling a custom creator function?
Edit: Bearing in mind some of the comments, I should point out that I'm trying to avoid try...finally - kind of the main reason for a using block in the first place.
Additional clarification: The MyObject object is constructed from information in the stream - i.e. its constructor reads the content of the stream in its entirity. No other method in MyObject references the stream. The content of the stream could come from anywhere - a file, a resource, an Internet socket, etc.

You could invoke some magic like so:
TResult CreateUsingDisposable<TDisposable, TResult>(TDisposable disposable, Func<TDisposable, TResult> getResult)
where TDisposable : IDisposable
{
using (disposable)
{
return getResult(disposable);
}
}
using (var obj = CreateUsingDisposable(new FileStream(path, FileMode.Open), stream => new MyObject(stream)))
{
}
But why? There's a super easy to read no-nonsense way of doing that:
MyObject obj;
using (var stream = new FileStream(path, FileMode.Open))
{
obj = new MyObject(stream);
}
using (obj)
{
}

While I don't see a way to avoid the creator function, you can make it generic enough to define once and use for any class:
static T WithStream<T>(string path, Func<FileStream, T> getter)
{
using (FileStream stream = File.Open(path, FileMode.Open))
{
return getter(stream);
}
}
class MyObject : IDisposable
{
public MyObject (Stream stream){ /* Work with stream */}
public void Dispose(){}
}
static void Main()
{
using (MyObject obj = WithStream("path", fs => new MyObject(fs)))
{
// do something with obj
}
}

Related

How to Deserialize the current class

I do not know if this is even possible but let's suppose I have a class:
[Serializable]
public class Test
{
//Properties and functions would go here.
public void SaveClass(string FilePath)
{
System.IO.Stream stream = System.IO.File.Create(FilePath);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
binaryFormatter.Serialize(stream, this);
stream.Close();
}
public void LoadClass(string FilePath)
{
System.IO.Stream stream = System.IO.File.OpenRead(FilePath);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
this = binaryFormatter.Deserialize(stream); //It returns an error saying that 'this' is readonly.
stream.Close();
}
}
Now I want to be able to Save and Load the class. The SaveClass is working fine, but the LoadClass returns an error, because I can not assign anything to this. Is there a way that I could load the class from a file, or is this impossible.
Make LoadClass static, something like:
public static Test LoadClass(string FilePath)
{
System.IO.Stream stream = System.IO.File.OpenRead(FilePath);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
var newTest = (Test)binaryFormatter.Deserialize(stream);
stream.Close();
return newTest;
}

Accessing the same files from multiple tasks

I have many files on disk that I need to read and deserialize, If i try to access these files using a streamreader from multiple tasks I get the following exception:
System.ObjectDisposedException: Cannot access a disposed object
System.Runtime.InteropServices.SafeHandle.DangerousReleaseInternal(bool dispose)SafeHandle.cs:193
System.Runtime.InteropServices.SafeHandle.DangerousRelease()SafeHandle.cs:142
System.IO.FileStream.Dispose(bool disposing)FileStream.cs:913
System.IO.IsolatedStorage.IsolatedStorageFileStream.Dispose(bool disposing)IsolatedStorageFileStream.cs:250
System.IO.Stream.Close()stream.cs:248
System.IO.StreamReader.Dispose(bool disposing)streamreader.cs:296
System.IO.TextReader.Dispose()textreader.cs:78
System.IO.TextReader.Dispose()(wrapper remoting-invoke-with-check)
.FileServices.StreamReaderWrapper.Dispose()
.FileServices.IsolatedSerializer.<DeserializeAsync>d__9<T>.MoveNext()
Here is the code:
public async Task<T> DeserializeAsync<T>(string path)
{
T result = default(T);
if (!_fileRepository.FileManager.FileExists(path)) { return result; }
using (var streamReader = _streamReader.GetNew(_fileRepository.FileManager.OpenFile(
path, FileMode.Open, FileAccess.Read, FileShare.Read)))
{
var contents = await streamReader.ReadToEndAsync();
result = _jsonSerializer.DeserializeFromString<T>(contents, Encoding.UTF8);
streamReader.Dispose();
}
return result;
}
What am I doing wrong here?
/// <summary>
/// Wrapper to allow testing with StreamReader
/// </summary>
public class StreamReaderWrapper : IStreamReader
{
private StreamReader _streamReader;
public void Dispose()
{
if (_streamReader != null)
{
_streamReader.Dispose();
_streamReader = null;
}
}
public IStreamReader GetNew(Stream stream)
{
Dispose();
_streamReader = new StreamReader(stream);
return this;
}
public string ReadToEnd()
{
return _streamReader.ReadToEnd();
}
public Task<string> ReadToEndAsync()
{
return _streamReader.ReadToEndAsync();
}
}
You are calling Dispose twice. The using statement will dispose your streamReader variable automatically after exiting the code block. But you are also explicitly calling streamReader.Dispose();
Also, your StreamReaderWrapper.GetNew() should just return a new instance of the wrapper, otherwise threads will step on each other's toes
I think you can't access the "getNew" function, as you already disposed the object..
from your code:
using (var streamReader = _streamReader.GetNew(_fileRepository.FileManager.OpenFile(
who's "_streamReader"? an instance of the wrapper or the wrapped reader?

How to stop StreamWriter to not to create file if nothing to Write?

Stream Writer creates a file even if there is nothing to write if Stream Writer was used in using statement, the Reason is using block calls dispose method at the end of block.
Code is
using (StreamWriter sw = new StreamWriter(#"d:\abc.txt"))
{
}
I wan't Not to create the blank file if there is nothing to write. The reason I am looking for is We are writing logs conditionally.
You could write something like:
Lazy<StreamWriter> sw = new Lazy<StreamWriter>(() => new StreamWriter(#"d:\abc.txt"));
try
{
if (something)
{
sw.Value.WriteLine("Foo");
}
}
finally
{
if (sw.IsValueCreated)
{
sw.Value.Dispose();
}
}
using Lazy<T> to encapsulate the StreamWriter.
You could even encapsulate Lazy<T> to "handle" IDisposable:
public sealed class DisposableLazy<T> : Lazy<T>, IDisposable where T : IDisposable
{
public DisposableLazy(Func<T> valueFactory) : base(valueFactory)
{
}
// No unmanaged resources in this class, and it is sealed.
// No finalizer needed. See http://stackoverflow.com/a/3882819/613130
public void Dispose()
{
if (IsValueCreated)
{
Value.Dispose();
}
}
}
and then
using (DisposableLazy<StreamWriter> sw = new DisposableLazy<StreamWriter>(() => new StreamWriter(#"d:\abc.txt")))
{
if (something)
{
sw.Value.WriteLine("Foo");
}
}

File being used by another process using StreamWriter

My program was practice for me, however, when I try to write all the directories it found, it crashes.
I tried the following:
Having it write to a file stream instead of the file itself
using File.Writealllines using a list<> (this worked, only it did the first five and no more)
FileStream.Write(subdir.ToCharArray())
I cannot see why this wouldn't work, what have I done wrong?
static void Main(string[] args)
{
Method(#"C:\");
}
static void Method(string dir)
{
//crash happens here v
StreamWriter sw = new StreamWriter(#"C:\users\"+Environment.UserName+"\desktop\log.txt",true);
foreach (string subdir in Directory.GetDirectories(dir))
{
try
{
Console.WriteLine(subdir);
sw.Write(subdir);
Method(subdir);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Error");
}
}
sw.Close();
}
Its recursive.
Because you're calling Method again here:
Console.WriteLine(subdir);
sw.Write(subdir);
Method(subdir); // BOOM
Your file is already open. You can't open it for writing again.
Open the file in Main once..
static void Main(string[] args) {
using (StreamWriter sw = new StreamWriter(#"C:\users\"+Environment.UserName+"\desktop\log.txt",true)) {
Method(#"C:\", sw);
}
}
Then accept it in your method:
public static void Method(string dir, StreamWriter sw) {
Then when you call it again:
sw.Write(subdir);
Method(subdir, sw); // pass in the streamwriter.
Note though, that you will quickly start chewing up memory. You're recursing through your entire C:\ drive. Maybe test it on a smaller folder?
I am agree with above but in my case solution different a little.
private static object locker = new object();
private static void WriteMessageToFile(string message)
{
string dateStr = DateTime.Now.Date.Day.ToString()+"_"+ DateTime.Now.Date.Month.ToString()+"_"+ DateTime.Now.Date.Year.ToString();
if (!Directory.Exists("Logs"))
{
DirectoryInfo di = Directory.CreateDirectory("Logs");
}
//Guid guidGenerator = Guid.NewGuid();
string filePath = _env.ContentRootPath + "\\Logs\\ProcessLog_" + dateStr + ".txt";
FileInfo fi = new FileInfo(filePath);
lock (locker)
{
using (FileStream file = new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.Read))
using (StreamWriter streamWriter = new StreamWriter(file))
{
streamWriter.WriteLine(message);
streamWriter.Close();
}
}
}
Because the following function is called asynchronous and asynchronous in many places in my asp.net core application. In this case, one thread was trying to write a file, another thread wanted to write the same file, and there was an error. As a solution, I tried the above, but it didn't work either because I tried to open a new stream before closing the previous stream. So I decided to write a secure block of code as a solution. In this case, since the other threads could not reach the locked area, they made the write operation by waiting for the previous operation and I was able to write the file without error.
I think; there is another reason code behind, cause i have used Singleton registration on startup. This function's caller classes are isolated from each other. with this reason they didn't know which thread is called the function before. Their lifetime has been finished while. Also FileStream wraps the StreamWriter then it also may work without lock, anyway it is guaranty.
Even Microsoft.Extensions.Logging does not support FileLoger by default, but we can write custom. I share the entire implementation below
public class FileLoger : ILogger
{
public static IHostingEnvironment _env;
private static object locker = new object();
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var message = string.Format("{0}: {1} - {2}", logLevel.ToString(), eventId.Id, formatter(state, exception));
WriteMessageToFile(message);
}
private static void WriteMessageToFile(string message)
{
string dateStr = DateTime.Now.Date.Day.ToString()+"_"+ DateTime.Now.Date.Month.ToString()+"_"+ DateTime.Now.Date.Year.ToString();
if (!Directory.Exists("Logs"))
{
DirectoryInfo di = Directory.CreateDirectory("Logs");
}
//Guid guidGenerator = Guid.NewGuid();
string filePath = _env.ContentRootPath + "\\Logs\\ProcessLog_" + dateStr + ".txt";
FileInfo fi = new FileInfo(filePath);
lock (locker)
{
using (FileStream file = new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.Read))
using (StreamWriter streamWriter = new StreamWriter(file))
{
streamWriter.WriteLine(message);
streamWriter.Close();
}
}
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
}
public class FileLogProvider : ILoggerProvider
{
public FileLogProvider(IHostingEnvironment env)
{
FileLoger._env = env;
}
public ILogger CreateLogger(string category)
{
return new FileLoger();
}
public void Dispose()
{
}
}
Seems you didn't close your streamwriter before you use it again
public static void Method(string dir)
{
//crash happens here v
StreamWriter sw = new StreamWriter(#"C:\users\"+Environment.UserName+"\desktop\log.txt",true);
foreach (string subdir in Directory.GetDirectories(dir))
{
try
{
Console.WriteLine(subdir);
sw.Write(subdir);
//This line you'll call "Method" again
Method(subdir);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Error");
}
}
sw.Close();
}
Also, another suggestion, why don't you use "System.IO.File.AppendAllText(Path,Text)" method? it's easier to use

Why does my non closing stream still close?

I have a non-closing stream class that's wrapped in a using block with a binary reader, but for some reason when the block ends, my non-closing stream still closes.
The stream is defined as:
internal class NonClosingStream : Stream, IDisposable
{
private Stream baseStream;
public NonClosingStream(Stream baseStream)
{
this.baseStream = baseStream;
}
public override bool CanRead{ get { return baseStream.CanRead; } }
public override bool CanSeek{ get { return baseStream.CanSeek; } }
public override bool CanWrite { get { return baseStream.CanWrite; } }
public override void Flush()
{
baseStream.Flush();
}
public override long Length { get { return baseStream.Length; } }
public override long Position
{
get { return baseStream.Position; }
set { baseStream.Position = value; }
}
public override int Read(byte[] buffer, int offset, int count)
{
return baseStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return baseStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
baseStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
baseStream.Write(buffer, offset, count);
}
public override void Close()
{
// Disconnects from base stream, but does not close it
this.baseStream = null;
}
void IDisposable.Dispose()
{
// Disconnects from base stream, but does not close it
this.baseStream = null;
}
}
and the read block looks like this:
public T Deserialize<T>(Stream stream)
{
using (NonClosingStream nonClosingStream = new NonClosingStream(stream))
using (BinaryReader reader = new BinaryReader(nonClosingStream, Encoding.ASCII, true))
{
// Read the type name, then convert it to an actual type
String typeName = reader.ReadString();
Type graphType = AvailableTypes.GetType(typeName);
// If a deserializer for this type already exists, use it.
if (deserializerFunctions.ContainsKey(graphType))
{
return (T)deserializerFunctions[graphType](reader);
}
// Otherwise, create one and use it
T graph = (T)FormatterServices.GetUninitializedObject(graphType);
typeof(ServiceSerializer).GetMethod("DeserializeObject",
BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(graphType)
.Invoke(this, new Object[] { reader, graph });
return graph;
}
}
what am I doing wrong?
Update
So I wrote this little diddy:
static void Main()
{
MemoryStream stream = new MemoryStream();
using (NonClosingStream nonCloser = new NonClosingStream(stream))
using (BinaryWriter writer = new BinaryWriter(nonCloser))
using (BinaryReader reader= new BinaryReader(nonCloser))
{
writer.Write("Lorem ipsum");
stream.Seek(0, SeekOrigin.Begin);
String data = reader.ReadString();
Console.WriteLine(data);
}
stream.Seek(0, SeekOrigin.Begin);
using (NonClosingStream nonCloser = new NonClosingStream(stream))
using (BinaryWriter writer = new BinaryWriter(nonCloser))
using (BinaryReader reader = new BinaryReader(nonCloser))
{
writer.Write("Lorem ipsum");
stream.Seek(0, SeekOrigin.Begin);
String data = reader.ReadString();
Console.WriteLine(data);
}
Console.ReadLine();
}
and it seems to work fine, the stream stays open like it's supposed to. So I guess the consensus is right. Somehow I'm closing the stream elsewhere. When I figure out what I'll post the results. Thanks all.
Update
Gaaaahhh, I figured out the problem. So the way the code works is that while it serializes/deserializes an object, it then builds a customized serializer out of expression trees and then compiles it so that future serializations are more fluid. That means my code is littered with stuff like this:
Action<BinaryReader, Object> assignmentAction = delegate(BinaryReader bReader, Object oGraph)
{
bReader.ReadByte(); // Read the next action
bReader.ReadString(); // Read the field name
bReader.ReadByte(); // Read the field type
// Call the assignment lambda
assignmentLambda(reader, deserializerFunctions[primitiveType], (T)oGraph);
};
did you catch that? No? Neither did I apparently. Let's add some context:
private static void DeserializeObject<T>(BinaryReader reader, T graph)
{
...
Action<BinaryReader, Object> assignmentAction = delegate(BinaryReader bReader, Object oGraph)
{
bReader.ReadByte(); // Read the next action
bReader.ReadString(); // Read the field name
bReader.ReadByte(); // Read the field type
// Call the assignment lambda
assignmentLambda(reader, deserializerFunctions[primitiveType], (T)oGraph);
};
...
}
The lambda is closing over reader from the outside block, instead of using the bReader provided when the cached deserializer runs. Hence, when the deserializer runs, it's using an already discarded Binary reader object rather than the fresh one being provided to it. I guess the issue wasn't that I was closing the stream, but that I was using a disposed reader. At least that explains why it would work once, and then fail the second time, since the second time it relies on the cached deserializer. Oops!
Thanks all.
Since your stream does not create inner stream most likely outer code closes your inner stream. Chances are your code look like:
NonClosingStream nonClosing;
using(var stream = new FileStream(...))
{
nonClosing = new NonClosingStream(stream );
....
}
// inner stream now closed and nonClosing will fail all operations.
void IDisposable.Dispose()
Your class has two Dispose() methods. The one you explicitly implemented. And the one that you inherited from the Stream class. Problem is, BinaryStream doesn't know beans about yours. It only knows about the one that Stream implements. Furthermore, when you use the BinaryStream(Stream) constructor, the BinaryStream object assumes ownership of the passed Stream object. Which means it will dispose that stream when it gets disposed itself. Perhaps you see the problem now, the inherited Dispose() method will be called, not yours. And it closes the base stream.
This is why Stream implements the Dispose pattern. You'll need to make it look like this instead:
internal class NonClosingStream : Stream {
protected override Dispose(bool disposing) {}
}
It depends on whether the stream which is being wrapped by your NonClosingStream class is referenced elsewhere. If not, then the underlying stream will have no references, so at some point afterwards its finalizer will close the stream.

Categories

Resources