Deserializing an array using SOAP - c#

I'm having a problem getting a SOAP service to deserialize an array of objects; its only reading in the first member of the array and nothing else. Here is my code:
public void StoreCredentials(Credentials credentials)
{
Credentials[] credsArray;
var soap = new SoapFormatter();
var stream = new FileStream(_path, FileMode.OpenOrCreate);
try
{
credsArray = (Credentials[])soap.Deserialize(stream);
var credsList = credsArray.ToList();
credsList.Add(credentials);
credsArray = credsList.ToArray();
}
catch (SerializationException)
{
credsArray = new[] {credentials};
}
soap.Serialize(stream, credsArray);
stream.Close();
}
I wrote a simple unit test which adds two Credentials objects to the file, the output looks correct, both sets of credentials are present, but when I run the test to add a third set to the file, the soap.Deserialize(stream) line returns an array with only one entry, even though the file its reading from contains two entries. Am I doing something wrong here? Is there a better / easier way to do this? Please help!

I figured it out. The problem is with the FileMode.OpenOrCreate attribute of the FileStream. This will open the file but append it with a new collection instead of altering the original collection. I needed to overwrite the file by using FileMode.Create instead, so here is the working code (I also changed the collection to a Hashtable, which works better):
public void StoreCredentials(Credentials credentials)
{
credentials.Encrypt(_myUser.Encryption);
Hashtable credsTable;
var soap = new SoapFormatter();
FileStream stream;
try
{
stream = new FileStream(_path, FileMode.Open, FileAccess.ReadWrite);
credsTable = (Hashtable) soap.Deserialize(stream);
stream.Close();
stream = new FileStream(_path, FileMode.Create, FileAccess.ReadWrite);
if (credsTable.ContainsKey(credentials.Id))
credsTable[credentials.Id] = credentials;
else
credsTable.Add(credentials.Id, credentials);
}
catch (FileNotFoundException e)
{
stream = new FileStream(_path, FileMode.Create, FileAccess.ReadWrite);
credsTable = new Hashtable {{credentials.Id, credentials}};
}
soap.Serialize(stream, credsTable);
stream.Close();
}

Related

Convert List<string> to a stream

The problem I have is that I have a CSV file full of records, that currently is being mapped to a strongly typed collection via the open source CsvHelper.CsvReader.GetRecords<T> method. It gets passed a GZIP stream which is built on a FileStream so is reading the stream from disk.
My suspicion is that the CsvHelper class when used with a FileStream is not very efficient as this load takes a long time. I want to try and load the raw file efficiently first just into memory, and then do the strong type mapping afterwards.
Unfortunately, the mapping class CsvHelper.CsvReader.GetRecords<T> accepts only a stream. I have managed to load the raw data into a List<string> very fast, however I now cannot figure out how to "streamify" this to pass to the mapper. Is this something I can do or is there another solution?
My code so far is
var fileStream = ...
var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress);
var entries = new List<string>();
using (var unzip = new StreamReader(gzipStream))
while(!unzip.EndOfStream)
entries.Add(unzip.ReadLine());
Parse(??);
public IReadOnlyCollection<TRow> Parse(Stream stream)
{
Func<Stream> streamFactory = () => stream;
var results = ParseCsvWithConfig <TRow>(streamFactory, _configuration).AsReadOnly();
}
public static IEnumerable<T> ParseCsvWithConfig<T>(Func<Stream> streamFactory, CsvConfiguration configuration)
{
using (var stream = streamFactory())
{
var streamReader = new StreamReader(stream);
using (var csvReader = new CsvReader(streamReader, configuration ?? new CsvConfiguration()))
{
return csvReader.GetRecords<T>().ToList();
}
}
}
Skip the list altogether:
var fileStream = ...
var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress);
var memoryStream = new MemoryStream();
gzipStream.CopyTo(memoryStream);
// call Parse on memorystream
Feel free to add using blocks where appropriate in your code.

Trying to create a bin-file using stream

I onced managed to create the bin-file in my project. I changed the primary key from int to Guid and moved code from Main to my class Quote. At the moment I can only add new entries in said file. If I remove it a new file(0 bytes) is created and the stream gets ArgumentException when I try to feed the file dummy-data. I am trying to use an if-loop to handle stream.Lenght == 0.
public static List<Quote> readBinaryToList() //Crashes if binfile is 0 bytes long
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(#"C:\Users\xxxxxx\Desktop\quotes.bin", FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read);
if (stream.Length == 0)
{
Quote q = new Quote(Guid.NewGuid(), "Quote dummy", false);
List<Quote> quoteList = new List<Quote>();
quoteList.Add(q);
var bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bformatter.Serialize(stream, quoteList);
bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
List<Quote> quoteListTmp = (List<Quote>)bformatter.Deserialize(stream);
return quoteList;
}
else
{
List<Quote> quoteList = (List<Quote>)formatter.Deserialize(stream);
stream.Close();
return quoteList;
}
}
As pointed out in previous answers, you must give your file stream write permissions which can be done in its constructor, then you should also set the position of the stream back to 0, you can achieve this by using the stream's Position property.
You are creating a lot of unnecessary objects that don't actually contribute to purpose of the method I have omitted these below. In doing so, setting the streams Position property to 0 is redundant but I've left it in a comment to show how its done.
Some other things to consider: Declare the file stream inside a using statement so that it is disposed when the method comes to an end this means you can omit the manual close in the else statement. Some of your code can be written more tersely, this is just a personal preference but I think it would be best to inline some of your code to remove as much noise as possible. It is also convention in C# to use PascalCase for Methods.
public static List<Quote> ReadBinaryToList(){
using(Stream stream = new FileStream(#"quotes.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite)) {
IFormatter formatter = new BinaryFormatter();
if (stream.Length == 0) {
List<Quote> quoteList = new List<Quote> {new Quote(Guid.NewGuid(), "Quote dummy", false)};
formatter.Serialize(stream, quoteList);
//stream.Position = 0;
return quoteList;
}
else return (List<Quote>)formatter.Deserialize(stream);
}
}
The file is being opened as readonly, serializing to the file will require write permissions.
Stream stream = new FileStream(#"C:\temp\quotes.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
The stream should also be returned to the beginning before making any attempts to deserialize from it.
stream.Seek(0, SeekOrigin.Begin);
FileStreams have a single "head" where all read and write operations take places. As a new stream is being written, the head is always at the end and any attempt to read from the end will fail. Some streams (e.g. NetworkStream) behave differently and do not allow seeking at all.
Also, the initial position of the FileStream depends on how the file is opened (based on the specified FileMode). The FileMode specified in the question will result in the stream position starting at the beginning of the file, so this is not required in the else block.
And make sure that the Quote class is marked [Serializable]

Could XmlSerializer and MemoryStream return an array of 0 value bytes?

I have a method that saves an instance of my custom class to a file. One time I noticed that my application fails to start, because this file is filled with 0-value bytes (null characters). This has never happened before, it seemed to work just fine. Does anyone see something odd with this code? Something that can cause the serializer or the memory stream to return an array of zero values? Or should I suspect it's the work of another application?
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(MySettings));
public void Save(MySettings config)
{
using (var stream = new MemoryStream())
{
_serializer.Serialize(stream, config);
byte[] binaryConfig = stream.ToArray();
File.WriteAllBytes(_configFilePath, binaryConfig);
}
}
Wouldn't it be simpler to use something like this?
XmlSerializer x = new XmlSerializer(typeof(MySettings));
using (FileStream stream = new FileStream(_configFilePath, FileMode.Create, FileAccess.Write))
{
x.Serialize(stream, config);
stream.Close();
}
The XML file should not contain any 0-bytes or nul characters, as your object is translated to XML text during serialization. You can simply open the XML file using a text editor to have a look at the file contents.

Remove temporary file after returning in raw format in WCF

I have a WCF service which works in raw format, using streams:
[ServiceContract]
public interface IEncryptingService
{
[WebInvoke(UriTemplate = "/")]
[OperationContract]
Stream SignDocument(Stream requestStream);
}
public class EncryptingService : IEncryptingService
{
public Stream SignDocument(Stream requestStream)
{
string originalFileName = Path.GetTempFileName();
string signedFileName = Path.GetTempFileName();
using (var originalFileStream = File.Open(originalFileName, FileMode.Create, FileAccess.Write))
{
requestStream.CopyTo(originalFileStream);
}
XmlDocumentSigner.SignFile(originalFileName, signedFileName);
return File.Open(signedFileName, FileMode.Open, FileAccess.Read);
}
}
Now, how can I remove this file after WCF ends returning the file?
I have tried to use finally block, but it gets called right after return, and throws exception, since the file is still used by a process.
Of course, these is a workaround like a background worker waiting for a file to be available for deletion, but, in my opinion, it is not like how web-services should be implemented.
I haven't tried it but you could open the file and write that stream to another stream. Something like:
MemoryStream ms = new MemoryStream();
using (FileStream fs = File.OpenRead(signedFileName))
{
//Read from fs and write to ms
}
Then all you will need to do is call delete on the file and return ms:
File.Delete(signedFileName);
return ms;
The solution came to mind inexcusably quickly, and it is absolutely logical: I can simply read file contents to memory and remove the file.
public Stream SignDocument(Stream requestStream)
{
string originalFileName = Path.GetTempFileName();
string signedFileName = Path.GetTempFileName();
using (var originalFileStream = File.Open(originalFileName, FileMode.Create, FileAccess.Write))
{
requestStream.CopyTo(originalFileStream);
}
XmlDocumentSigner.SignFile(originalFileName, signedFileName);
byte[] signedFileBytes = File.ReadAllBytes(signedFileName);
File.Delete(signedFileName);
return new MemoryStream(signedFileBytes);
}
Note that using statement makes this code fail as well:
using (var ms = new MemoryStream(signedFileBytes))
{
return ms;
}

ServiceStack Stream Compression

I am returning a stream of data from a ServiceStack service as follows. Note that I need to do it this way instead of the ways outlined here because I need to perform some cleanup after the data has been written to the output stream.
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
fs.WriteTo(Response.OutputStream);
}
Response.EndRequest();
...cleanup code...
Compression is handled in the other services that return simple DTOs by using a ServiceRunner similar to this answer. However the stream response above never hits that code as the response object in OnAfterExecute is always null. I am able to manually compress the result inside of the service method as follows, but it requires a lot of setup to determine if and what compression is needed and manually setting up the correct HTTP headers (omitted below).
var outStream = new MemoryStream();
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (var tinyStream = new GZipStream(outStream, CompressionMode.Compress))
{
fs.CopyTo(tinyStream);
outStream.WriteTo(Response.OutputStream);
}
Response.EndRequest();
...cleanup code...
Is there a way in ServiceStack to handle this compression for me similar to the way it works with the ServiceRunner?
I'm not exactly sure what you like about the way ServiceStack handles compression within ServiceRunner. Is it because it is global across ServiceStack APIs?
For your example, I think something like below works and meets your need to perform some cleanup after the data has been written to the output stream ...
public class FStreamService : Service
{
public object Get(FStream request)
{
var filePath = #"c:\test.xml";
var compressFileResult = new CompressedFileResult(filePath); //CompressedResult in ServiceStack.Common.Web.CompressedFileResult
compressFileResult.WriteTo(Response.OutputStream);
Response.EndRequest();
// ...cleanup code...
}
}
Based on comments update of above to add compression using some ServiceStack extensions
public object Get(FStream request)
{
var filePath = #"c:\test.xml";
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
var compressedBtyes = fs.ToUtf8String().Compress(this.RequestContext.CompressionType);
new CompressedResult(compressedBtyes).WriteTo(Response.OutputStream);
}
Response.EndRequest();
// ...cleanup code...
}
If you want it within a ServiceRunner something like this should work...
public override object OnAfterExecute(IRequestContext requestContext, object response)
{
var resp = requestContext.Get<IHttpResponse>();
response = requestContext.ToOptimizedResult(requestContext.Get<IHttpResponse>().OutputStream);
return base.OnAfterExecute(requestContext, response);
}

Categories

Resources