I try to develop an application in C# and have some concerns with MailMessage object:
it implements IDisposable interface, so I use it within using statement. So it implicitly calls Dispose method after. Now, using that object I need to add attachments, which I have converted to byte[] object and I add them as stream. Here's part of code to have better view:
using(MailMessage message = new MailMessage("john.smith#gmail.com"){
MemoryStream stream;
//here I pass byte array to the stream and create an attachemnt
message.Attachments.Add(new Attachment(stream, "name.xyz"));
using(SmtpClient client = new SmtpClient("server.com", port))
{
// send message
}
}
Now, I have one resource unmanaged: Stream object. I can't close it (so can't call Dispose method) right after setting attachments, because I'd get an error when sending message, because it uses the stream while sending.
So, I need to get rid of it later, which I do after sending. That's the code in second using:
try
{
client.Send(messgae);
}
finally
{
if(stream != null)
stream.Dispose();
}
Now the question: Dispose method of MailMesssage frees all resources used by that object. My Stream object is one of the resources, isn't it? So, when using(MailMessage... terminates it should manage also my Stream object, shouldn't it? So I wouldn't need to dispose of my Stream object manually.
EDIT:
Suggested approach:
using(MailMessage message = new MailMessage("john.smith#gmail.com"){
using(MemoryStream stream = ...)
{
//here I pass byte array to the stream and create an attachemnt
message.Attachments.Add(new Attachment(stream, "name.xyz"));
using(SmtpClient client = new SmtpClient("server.com", port))
{
// send message
}
}
}
But the questions stays: MailMessage uses this Stream - so, do we still need to manage Stream on our own?
Why not dispose the stream after the message is sent?
using(MailMessage message = new MailMessage("john.smith#gmail.com"))
{
using(var stream = new MemoryStream())
{
//here I pass byte array to the stream and create an attachemnt
message.Attachments.Add(new Attachment(stream, "name.xyz"));
using(SmtpClient client = new SmtpClient("server.com", port))
{
// send message
}
}
}
From the reference documentation you don't need to when you are using using.
Mail Message disposes Attachment Collection, which then disposes all its attachements.
Regarding, should we take or rely on this approach then I totally agree with Zohar on
Disposing of an IDisposable should be expressed in your code, either by calling Dispose explicitly or with the using statement.
Try this:
using(MailMessage message = new MailMessage("john.smith#gmail.com")
using(MemoryStream stream = new MemoryStream())
using(SmtpClient client = new SmtpClient("server.com", port))
{
message.Attachments.Add(new Attachment(stream, "name.xyz"))
client.Send(messgae);
}
If you put MemoryStream in using block, it will do the same thing as your try/finally block.
Related
I have a class MyData which is Json serializable by using Json.Net JsonSerializer.Serialize(TextWriter, object). I want to send this data (as json) to a web service via HttpClient.PostAsync.
Because converting the json to string and then sending it as StringContent is (probably) not performant, I want to do it with streams.
I found the class StreamContent, which takes a stream in its constructor. And serializing json into streams should be possible as well. So I tried this:
MyData data = ...; // already filled
string uri = ...; // already filled
HttpClient client = new HttpClient();
JsonSerializer serializer = new JsonSerializer();
using (MemoryStream ms = new MemoryStream())
{
using (StreamWriter sw = new StreamWriter(ms))
using (JsonWriter jw = new JsonTextWriter(sw))
{
serializer.Serialize(sw, data);
ms.Flush();
ms.Position = 0;
}
HttpResponseMessage response = client.PostAsync(uri, new StreamContent(ms)).Result;
}
But running this code gives me two exceptions in the line HttpResponseMessage response = ...:
HttpRequestException: Error when copying content into a stream.
ObjectDisposedException: Could not access closed stream.
What am I doing wrong?
If you serialize the object into a MemoryStream, the entire JSON data will be written in the buffer, so there is no significant performance benefit over just serializing into a string and using StringContent.
Your StremWriter disposes the memory stream before the request is sent, that is why you get the exceptions.
You can either move your using statements to be in the same scope as the MemoryStream, or use the StreamWriter's constructor that accepts a boolean parameter to leave the stream open after the writer is disposed.
StreamWriter constructor:
Unless you set the leaveOpen parameter to true, the StreamWriter object calls Dispose() on the provided Stream object when StreamWriter.Dispose is called.
I'm trying to construct an EmailMessage using multiple in-memory streams. However, when I send the e-mail, I get the following error:
"One of the streams has already been used and can't be reset to the origin"
From what I can gather, I think the problem may be that the message is losing context of the memory stream when I try to do the following:
foreach (var att in attachments)
{
doc = fetchDocumentByteArray();
using (MemoryStream ms = new MemoryStream(doc))
{
mailToSend.AddAttachment(new Attachment(ms, att.Filename));
}
}
mailToSend.Send();
I've also tried setting the ms.Position = 0 before the AddAttachment(), but that doesn't work.
After looking around a bit for a scenario like mine, I came across a suggestion to use a List<MemoryStream> - but I'm not sure how I'd implement this or if it's even the correct approach?
When you use "using" keywork, internally, Dispose method is invoked. It will make the MemoryStreams be deallocated.
Remove the using in your inner loop and create a try/finally clause to dispose the memory streams after the e-mail is sent.
The accepted answer did not work for me because you need to keep track of multiple MemoryStreams and dispose of each individually. I took the same approach but implemented it slightly differently adding each MemoryStream to a list and then calling Dispose() on each instance after the email is sent.
var msList = new List<MemoryStream>();
foreach (var attachment in message.Attachments)
{
var ms = new MemoryStream(attachment.Bytes);
msList.Add(ms);
var mailAttachment = new Attachment(ms, attachment.FileName);
mailMessage.Attachments.Add(mailAttachment);
}
smtp.Send(mailMessage);
foreach (var ms in msList)
{
ms.Dispose();
}
I use the following snippet of code, and I'm unsure whether I need to call the Flush methods (once on StreamWriter, once on MemoryStream):
//converts an xsd object to the corresponding xml string, using the UTF8 encoding
public string Serialize(T t)
{
using (var memoryStream = new MemoryStream())
{
var encoding = new UTF8Encoding(false);
using (var writer = new StreamWriter(memoryStream, encoding))
{
var serializer = new XmlSerializer(typeof (T));
serializer.Serialize(writer, t);
writer.Flush();
}
memoryStream.Flush();
return encoding.GetString(memoryStream.ToArray());
}
}
First of all, because the code is inside the using block, I think the automatically called dispose method might do this for me. Is this true, or is flushing an entirely different concept?
According to stackoverflow itself:
Flush meaning clears all buffers for a stream and causes any buffered data to be written to the underlying device.
What does that mean in the context of the code above?
Secondly, the flush method of the MemoryStream does nothing according to the api, so what's up with that? why do we call a method that does nothing?
You don't need to use Flush on the StreamWriter, as you are disposing it (by having it in a using block). When it's disposed, it's automatically flushed and closed.
You don't need to use Flush on the MemoryStream, as it's not buffering anything that is written to any other source. There is simply nothing to flush anywhere.
The Flush method is only present in the MemoryStream object because it inherits from the Stream class. You can see in the source code for the MemoryStream class that the flush method actually does nothing.
In general Streams will buffer data as it's written (periodically flushing the buffer to the associated device if there is one) because writing to a device, usually a file, is expensive. A MemoryStream writes to RAM so the whole concept of buffering and flushing is redundant. The data is always in RAM already.
And yes, disposing the stream will cause it to be flushed.
Commenting flush method returning empty byte[], Though I am Using Using block
byte[] filecontent = null;
using var ms = new MemoryStream();
using var sw = new StreamWriter(fs);
sw.WriteCSVLine(new[] { "A", "B" });//This is extension to write as CSV
//tx.Flush();
//fs.Flush();
fs.Position = 0;
filecontent = fs.ToArray();
are there any good examples of how to attach multiple files from a database to an e-mail in .NET? I've got a method that returns a Byte[] containing the Image column contents that I am calling in a loop to get each attachment, but I was wondering if there was a "correct"/best-practice way of doing this, especially with the possibility of introducing memory leaks by using MemoryStreams to contain the data? I'm fine creating en e-mail object and attaching the list of attachments to it, once I've got them and can do this fine with a single attachement but it seems to get slightly more complex with multiple files. Considering I wouldn't have thought this was an unusual requirement, there seems to be a dearth of articles/posts about it.
Thx - MH
Here's how to proceed. Let's suppose that you have an array of attachments that you have loaded from your database:
IEnumerable<byte[]> attachments = ... fetch from your database
We could also safely assume that along with those attachments you have loaded the filenames and probably their corresponding MIME type (information that you surely must have persisted along with those byte arrays representing your attachments). So you will probably have fetched IEnumerable<SomeAttachmentType> but that's not important for the purpose of this post.
So now you could send the mail:
using (var client = new SmtpClient("smtp.foo.com"))
using (var message = new MailMessage("from#foo.com", "to#bar.com"))
{
message.Subject = "test subject";
message.Body = "test body";
message.IsBodyHtml = false;
foreach (var attachment in attachments)
{
var attachmentStream = new MemoryStream(attachment);
// TODO: Choose a better name for your attachments and adapt the MIME type
var messageAttachment = new Attachment(attachmentStream, Guid.NewGuid().ToString(), "application/octet-stream");
message.Attachments.Add(messageAttachment);
}
client.Send(message);
}
Here's the deal:
A MailMessage (IDisposable) contains multiple Attachments (IDisposable). Each attachment references a MemoryStream (IDisposable). The MailMessage is wrapped in a using block which ensures that its Dispose method will be called which in turn calls the Dispose method of all attachments which in turn call the Dispose method of the memory streams.
Hi you can have buffered reads directly from the database, MemoryStream does NOT introduce any memory leak if you dispose it after usage. Example using SqlDataReader:
using(var stream = new MemoryStream())
{
byte[] buffer = new byte[4096];
long l, dataOffset = 0;
while ((l = reader.GetBytes(columnIndex, dataOffset, buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, buffer.Length);
dataOffset += l;
}
// here you have the whole stream and can attach it to the email...
}
similar question on how to read bytes from database has been asked already countless times, see here for example: What is the most efficient way to read many bytes from SQL Server using SqlDataReader (C#)
I implemented a Client and Server model that uses Socket with thread
When I want to pass only a string from Client to the Server, it works. But I want to pass an object and it throws this error:
"Attempting to deserialize an empty stream"
Here is the code:
Client:
ASCIIEncoding asen = new ASCIIEncoding();
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
Command command = new Command("meuLogin", "minhaSenha");
binaryFormatter.Serialize(memoryStream, command);
stream.Write(memoryStream.ToArray(), 0, memoryStream.ToArray().Length);
Server:
byte[] message = new byte[4096];
int bytesRead = 0;
bytesRead = clientStream.Read(message, 0, 4096);
MemoryStream memoryStream = new MemoryStream(bytesRead);
BinaryFormatter bf1 = new BinaryFormatter();
memoryStream.Position = 0;
Command command = (Command)bf1.Deserialize(memoryStream);
Another question: I copied the class Command from Client and pasted at Server to deserialize. Is this correct?
Thank you
I also recommend WCF. But if you continue using sockets, the key element that you're missing in your protocol is message framing.
You never use the message that you read from the stream. The memory stream you are reading from is thus empty.
On a side note, why do you use these intermediate MemoryStreams?
To answer your second question: for maximum maintainability, the class Command should be in a separate assembly that both Client and Server reference.
To answer your first question: you are attempting to deserialize from an empty stream on your server, just as the exception tells you. You need to copy the bytes you read from the clientStream into the memoryStream before you deserialize from the memoryStream. Alternatively, use the clientStream directly rather than using the memoryStream; this may require reconsidering your protocol.
Finally, I wholeheartedly agree with #Andrey: consider using WCF. It's way way way better than raw sockets.
If you change your server code to use a different MemoryStream constructor, the problem will go away.
MemoryStream memoryStream = new MemoryStream(message, 0, bytesRead);
However, I agree with Stephen and others. Either use WCF, or use Message framing.
Another question: I copied the class Command from Client and pasted at Server to deserialize. Is this correct?
No. The namespace for your Client and Server are likely different, so the Server will be trying to deserialize a stream that matches its namespace.
You should create your Command Class using its own namespace and reference that from both your Client and the Server.
After that...
Client:
static void StreamToServer(TcpClient client, Command obj) {
using (NetworkStream ns = client.GetStream()) {
using (MemoryStream ms = new MemoryStream()) {
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
byte[] buf = ms.ToArray();
ns.Write(buf, 0, buf.Length);
}
}
}
Server:
static Command ReadStream(TcpListener listener) {
Command obj = null;
using (TcpClient client = listener.AcceptTcpClient()) { // waits for data
using (NetworkStream ns = client.GetStream()) {
byte[] buf = new byte[client.ReceiveBufferSize];
int len = ns.Read(buf, 0, buf.Length);
using (MemoryStream ms = new MemoryStream(buf, 0, len)) {
BinaryFormatter formatter = new BinaryFormatter();
obj = formatter.Deserialize(ms) as Command;
}
}
}
return obj;
}
WCF or Message framing may be easier, but I don't often have the opportunity at work to sit around and read a book.
Instead of passing bytesRead to the MemoryStream which is actually the length of the byte stream, you should pass 'message', as it is the actual stream of bytes. Like,
MemoryStream memoryStream = new MemoryStream(message);
As you are passing an integer variable, the compiler is throwing exception that the stream is empty.
As for WCF, it is remarkable framework, but for applications that require low latency and high performance, WCF is a horrible answer because of its overheads, it is built upon sockets. So if you use sockets, that will be the lowest level implementation and thus, the fastest. That depends on your application which paradigm you should choose...