Does XML deserialize lock the file from reading? - c#

I have an xml file which is used by multiple process for reading. Here is the code snippet used for deserializing the xml. I want to make sure the below code does not read lock the file.
public Address TestReadLock(string myXmlFile)
{
using (StreamReader sr = new StreamReader(myXmlFile))
{
XmlReaderSettings xrs = new XmlReaderSettings();
xrs.ValidationType = ValidationType.None;
xrs.XmlResolver = null;
using (XmlReader reader = XmlReader.Create(sr, xrs))
{
return (Address)xmlSerializer.Deserialize(reader);
}
}
}
I tried testing this by creating a dll of above function and loaded the file through powershell and VS in a loop at same time it worked fine.
public void Main()
{
for (int i = 0; i < 1000; i++)
{
Address myaddress = TestReadLock(#C:\MyDetails.xml")
}
}
Based on my understanding the above code should read lock the file nad while testing it is not the case
Is there a possibility like testing I did is wrong or my understanding is not correct?

new StreamReader(string) uses FileAccess.Read and FileShare.Read - it will not prevent other readers. If you want different control: use FileStream directly to control the access / sharing.

Related

Write different tab or files separated in .csv or Excel file when using StreamWriter in C#

I have a console application that outputs the result into a .csv file. The console application calls several web services and outputs several data. The output contain URLs which differentiate one site from another other. The code works fine, but the output is in one big CSV file and it gets divided when it reaches the header then starts writing the data from the new site.
Here you have how the output is now from the CSV file:
ProjectTitle,PublishStatus,Type,NumberOfUsers, URL
Project one,published,Open,1,http://localhost/test1
Project two,expired,Closed,14,http://localhost/test1
ProjectTitle,PublishStatus,Type,NumberOfUsers,URL
Project one V2,expired,Closed,2,http://localhost/test2
Project two V2,Published,Open,3,http://localhost/test2
What I am trying to do its to either output the first set of data (depending on each URL) and then the second set of data in a different tab in Excel, or just create a new file for the new set of data. My code:
public static XmlDocument xml = new XmlDocument();
static void Main(string[] args)
{
xml.Load("config.xml");
test();
}
private static void test()
{
List<string> url = new List<string>();
int count = xml.GetElementsByTagName("url").Count;
for (int i = 0; i < count; ++i)
{
url.Add(xml.GetElementsByTagName("url")[i].InnerText);
Url = url[i];
}
string listFile = "ListOfSites";
string outCsvFile = string.Format(#"C:\\testFile\\{0}.csv", testFile + DateTime.Now.ToString("_yyyyMMdd HHmms"));
using (FileStream fs = new FileStream(outCsvFile, FileMode.Append, FileAccess.Write))
using (StreamWriter file = new StreamWriter(fs))
file.WriteLine("ProjectTitle,PublishStatus,Type,NumberOfSUsers,URL");
foreach(WS.ProjectData proj in pr.Distinct(new ProjectEqualityComparer()))
{
file.WriteLine("{0},\"{1}\",{2},{3},{4}",
proj.ProjectTitle,
proj.PublishStatus,
proj.type,
proj.userIDs.Length.ToString(NumberFormatInfo.InvariantInfo),
url[i].ToString());
}
}
If you want to write an Excel file with different worksheets you can try and use the EpPlus library, it is open source and free.
Here is the nuget page
To generate more files, you have to decide how to name them and if the records for each file are random in the source list, you can generate different lists of output strings for different output files and use the File.AppendAllLines(filename, collection); to save the data in the different files. I don't recommend you to open and close the files for each row to write because it is a time and resource consuming operation.
This is a quick fix. There may be a more elegant way to accomplish this as well:
public static XmlDocument xml = new XmlDocument();
static void Main(string[] args)
{
xml.Load("config.xml");
// relocated from `test` method to `Main`
List<string> url = new List<string>();
int count = xml.GetElementsByTagName("url").Count;
for (int i = 0; i < count; ++i)
{
url.Add(xml.GetElementsByTagName("url")[i].InnerText);
Url = url[i];
test(Url);
}
}
private static void test(string url)
{
string listFile = "ListOfSites";
string outCsvFile = string.Format(#"C:\\testFile\\{0}.csv", testFile + DateTime.Now.ToString("_yyyyMMdd HHmms"));
using (FileStream fs = new FileStream(outCsvFile, FileMode.Append, FileAccess.Write))
{
using (StreamWriter file = new StreamWriter(fs))
{
file.WriteLine("ProjectTitle,PublishStatus,Type,NumberOfSUsers,URL");
foreach(WS.ProjectData proj in pr.Distinct(new ProjectEqualityComparer()))
{
file.WriteLine("{0},\"{1}\",{2},{3},{4}",
proj.ProjectTitle,
proj.PublishStatus,
proj.type,
proj.userIDs.Length.ToString(NumberFormatInfo.InvariantInfo),
url);
}
}
}
}
NOTE: Please use braces in C# even if you don't NEED them. It makes your code more readable.

Printing - Need to transfer selected printer settings

I am working on a printing feature where GUI applications create & store XPS files to be printed in a temp directory and spawn a new C# program which picks these files & sends them to printer.
I need to show the printdialog in the GUI application as the xps file generation process needs some input like page range from user specified settings in print dialog page.
Then the GUI invokes the C# "MyPrinter" process which actually prints the xps files. I am able to transfer PrintTicket settings by serializing them to a file from GUI (through SaveTo API) and reading it from "MyPrinter" process. Sample code at MyPrinter process -
var server = new PrintServer(#"\\servername"); // need to get this from GUI processs
var printQueue = server.GetPrintQueue("printername"); // We can use printQueue name here
printQueue.UserPrintTicket = GetPrintTicket();
int index = 0;
foreach (var printFileName in new DirectoryInfo(printingPath).EnumerateFiles("*.xps"))
{
printQueue.AddJob(string.Format("CustomPrint-{0}", ++index), printFileName.FullName, printQueue.IsXpsDevice);
}
private PrintTicket GetPrintTicket()
{
var reader = new FileStream(#"C:\temp\printTicket.xml", FileMode.Open);
var ticket = new PrintTicket(reader);
reader.Close();
return ticket;
}
Wanted to know -
If there is way to serialize the printQueue settings to a file (need print server and printer name at bare minimum) ? Already tried using Xamlreader/XamlWriter but it seems PrintQueue class does not have a default constructor.
OR
Any other way I could reliably transfer selected printer settings from one application to another ?
Thanks in advance
Amit
I suggest that you have a read of the Serialization (C# and Visual Basic) page from MSDN. In .NET, you can make practically any class serializable simply, using the SerializableAttribute Class like this:
[Serializable] // <--- This is all you need
public class YourClass
{
...
}
You can find several code examples describing how to save a serialized class in the Basic Serialization page on MSDN... from this linked page:
MyObject obj = new MyObject();
obj.n1 = 1;
obj.n2 = 24;
obj.str = "Some String";
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyFile.bin", FileMode.Create, FileAccess.Write,
FileShare.None);
formatter.Serialize(stream, obj);
stream.Close();
I used following way to transfer print settings & print using another process -
Serialized PrintTicket class, it already has "ToXml()" method.
Transferred the printername
For actual printing in the "MyPrinter" process, did the following-
var server = new PrintServer(printDialog.PrintQueue.HostingPrintServer.Name);
var printQueue = server.GetPrintQueue(printDialog.PrintQueue.Name);
printQueue.UserPrintTicket = GetPrintTicket();
PrintUsingXpsDocWriter(printQueue);
}
private void PrintUsingXpsDocWriter(PrintQueue printQueue)
{
var docWriter = PrintQueue.CreateXpsDocumentWriter(printQueue);
int index = 0;
foreach (var printFileName in new DirectoryInfo(printingPath).EnumerateFiles("*.xps"))
{
}
printQueue.Dispose();
}
private PrintTicket GetPrintTicket()
{
// This file had serialized print ticket settings
var reader = new FileStream(#"C:\temp\printTicket.xml", FileMode.Open);
var ticket = new PrintTicket(reader);
reader.Close();
return ticket;
}
Thanks

Clearing contents of memory mapped file in C#

I am using MemoryMappedFile for communication between 2 programs. Program "A" creates the mmf and reads it's contents on a timer. Program "B" writes xml data to the mmf on a timer. I have the memory map working but I run into an issue where the previous iteration of the XML data is longer than the current and old data gets carried over to the next round.
so for simplicity lets say program B writes
aaaa
Program A will read correctly,
Then the next write from program B is:
b
Program A reads
baaa
It seems like there should be some simple way to flush the contents of the memory mapped file but I can't seem to figure it out. It's very possible that I'm totally wrong in the way I'm going about this.
Here's what I'm currently doing.
Program A:
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap",MemoryMappedFileRights.ReadWrite))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
string outputtext;
using (MemoryMappedViewStream stream = mmf.CreateViewStream(0,0))
{
XmlSerializer deserializer = new XmlSerializer(typeof(MyObject));
TextReader textReader = new StreamReader(stream);
outputtext = textReader.ReadToEnd();
textReader.Close();
}
mutex.ReleaseMutex();
return outputtext; //ends up in a textbox for debugging
}
Program B
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap", MemoryMappedFileRights.ReadWrite))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream(0, 0))
{
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
TextWriter textWriter = new StreamWriter(stream);
serializer.Serialize(textWriter, myObjectToExport);
textWriter.Flush();
}
mutex.ReleaseMutex();
}
Assuming length is reasonably small, you could really clear it out
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
textWriter.BaseStream.Write(new byte[length], 0, length);
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
EDIT: I think I misunderstood the OP's question. The problem he was having was not with clearing the contents of the MMF, but with stream manipulation. This should fix the problem:
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
textWriter.Write("");
textWriter.Flush();
That being said, you might want to do both.
I haven't really worked with MemoryMappedStreams much but this question seemed interesting so I took a crack at it. I wrote a really basic windows example with two buttons (read/write) and a single text box. I didn't pass in "0, 0" to the CreateViewStream calls and I created the file with a fixed length using a call to "CreateOrOpen" and everything worked well! The following are the key pieces of code that I wrote:
WRITE The File
// create the file if it doesn't exist
if (sharedFile == null) sharedFile = MemoryMappedFile.CreateOrOpen("testmap", 1000, MemoryMappedFileAccess.ReadWrite);
// process safe handling
Mutex mutex = new Mutex(false, "testmapmutex");
if (mutex.WaitOne()) {
try {
using (MemoryMappedViewStream stream = sharedFile.CreateViewStream()) {
var writer = new StreamWriter(stream);
writer.WriteLine(txtResult.Text);
writer.Flush();
}
}
finally { mutex.ReleaseMutex(); }
}
READ The File
// create the file if it doesn't exist
if (sharedFile == null) sharedFile = MemoryMappedFile.CreateOrOpen("testmap", 1000, MemoryMappedFileAccess.ReadWrite);
// process safe handling
Mutex mutex = new Mutex(false, "testmapmutex");
if (mutex.WaitOne()) {
try {
using (MemoryMappedViewStream stream = sharedFile.CreateViewStream()) {
var textReader = new StreamReader(stream);
txtResult.Text = textReader.ReadToEnd();
textReader.Close();
}
}
finally { mutex.ReleaseMutex(); }
}
Dispose the file (after finished)
if (sharedFile != null) sharedFile.Dispose();
For the full example, see here: https://github.com/goopyjava/memory-map-test. Hope that helps!
EDIT/NOTE - If you look at the example provided you can write to the file as many times as you want and any time you read you will read exactly/only what was written last. I believe this was the original goal of the question.

Strange issue with IsolatedStorage in WP7 device

I have a strange problem in my Windows Phone 7 application. I need to read/write some xml file in my app and I'm using IsolatedStorage to collect data. My app sends/gets data from SkyDrive this is why I use it.
Ok, here is function which generate exception:
private void CreateFileIntoIsolatedStorage(List<Record> list)
{
isf = IsolatedStorageFile.GetUserStoreForApplication();
if(list.Count == 0)
list = new List<Record>() { new Record { Date = DateTime.Today, Value = 0 }};
if (isf.FileExists(fileName))
{
isf.DeleteFile(fileName);
}
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile(fileName, FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<Record>));
using (XmlWriter xmlWriter = XmlWriter.Create(stream, xmlWriterSettings))
{
serializer.Serialize(xmlWriter, list);
}
}
}
}
Problem:
My problem starts when I this function runs for the second time. Then isf.DeleteFile(fileName); throws IsolatedStorageException. And creating stream crashed application.
It's strange cause it happens every time I run it on my device, and rarely when I use the debugger.
So my question is how can I solve it or are there better ways to do this?
Any help would be appreciated.
Possibly it's because at the beginning of your method you have:
isf = IsolatedStorageFile.GetUserStoreForApplication();
And you never dispose of that. Then, later, you get it again in the using. But that one's disposed. And then the next time you call CreateFileIntoIsolatedStorage, you get it again, again without disposing.
Perhaps this is what you want:
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
if(list.Count == 0)
list = new List<Record>() { new Record { Date = DateTime.Today, Value = 0 }};
if (isf.FileExists(fileName))
{
isf.DeleteFile(fileName);
}
}
Although that class-scoped isf variable is troublesome. If you want to keep the store active, then just call it once and leave it open. Otherwise, ditch the class-scoped variable.
Or, it might be due to this, from the documentation for IsolatedStorageFile.DeleteFile?
File deletions are subject to intermittent failures because files can be in use simultaneously by operating system features such as virus scanners and file indexers. This is especially true for recently created files. Macintosh users should be aware of this issue because of its frequent indexing.
For these reasons, it is important to add code to the code block that handles the IsolatedStorageException to retry deleting the file or log a failure.
I would suggest something like:
int retryCount = 0;
while (retryCount < MaxRetryCount && isf.FileExists(fileName))
{
try
{
isf.DeleteFile(fileName);
}
catch (IsolatedStorageException)
{
++retryCount;
// maybe notify user and delay briefly
// or forget about the retry and log an error. Let user try it again.
}
}

TextWriter not writing all files

I've got written a service that has a separate thread running that reads roughly 400 records from a database and serializes them into xml files. It runs fine, there are no errors and it reports all files have been exported correctly, yet only a handful of xml files appear afterwards, and its always a different number each time. I've checked to see if it's a certain record causing problems, but they all read out fine, and seem to write fin, but don't...
After playing around and putting a delay in of 250ms between each write they are all exported properly, so I assume it must have something to do with writing so many files in such a quick succession, but I have no idea why, I would have thought it would report some kind of error if they didn't write properly, yet there's nothing.
Here is the code for anyone who wants to try it:
static void Main(string[] args)
{
ExportTestData();
}
public static void ExportTestData()
{
List<TestObject> testObjs = GetData();
foreach (TestObject obj in testObjs)
{
ExportObj(obj);
//Thread.Sleep(10);
}
}
public static List<TestObject> GetData()
{
List<TestObject> result = new List<TestObject>();
for (int i = 0; i < 500; i++)
{
result.Add(new TestObject()
{
Date = DateTime.Now.AddDays(-1),
AnotherDate = DateTime.Now.AddDays(-2),
AnotherAnotherDate = DateTime.Now,
DoubleOne = 1.0,
DoubleTwo = 2.0,
DoubleThree = 3.0,
Number = 345,
SomeCode = "blah",
SomeId = "wobble wobble"
});
}
return result;
}
public static void ExportObj(TestObject obj)
{
try
{
string path = Path.Combine(#"C:\temp\exports", String.Format("{0}-{1}{2}", DateTime.Now.ToString("yyyyMMdd"), String.Format("{0:HHmmssfff}", DateTime.Now), ".xml"));
SerializeTo(obj, path);
}
catch (Exception ex)
{
}
}
public static bool SerializeTo<T>(T obj, string path)
{
XmlSerializer xs = new XmlSerializer(obj.GetType());
using (TextWriter writer = new StreamWriter(path, false))
{
xs.Serialize(writer, obj);
}
return true;
}
Try commenting\uncommenting the Thread.Sleep(10) to see the problem
Does anybody have any idea why it does this? And can suggest how I can avoid this problem?
Thanks
EDIT: Solved. The time based filename wasn't unique enough and was overwriting previously written files. Should've spotted it earlier, thanks for your help
Perhaps try putting the writer in a using block for immediate disposal? Something like
XmlSerializer xs = new XmlSerializer(obj.GetType());
using(TextWriter writer = new StreamWriter(path, false))
{
xs.Serialize(writer, obj);
}
Ok I've found the problem, I was using a time based filename that I thought would be unique enough for each file, turns out in a loop that tight they're coming out with the same filenames and are over-writing each other.
If I change it to use actually unique filenames it works! Thanks for your help
Dispose the writer
public static bool SerializeTo<T>(T obj, string path)
{
XmlSerializer xs = new XmlSerializer(obj.GetType());
using(TextWriter writer = new StreamWriter(path, false)) {
xs.Serialize(writer, obj);
writer.Close();
}
return true;
}
If you're not getting any exceptions, then the using statements proposed by other answers won't help - although you should change to use them anyway. At that point, you don't need the close call any more:
XmlSerializer xs = new XmlSerializer(obj.GetType());
using(TextWriter writer = new StreamWriter(path, false))
{
xs.Serialize(writer, obj);
}
I don't think the problem lies in this code, however. I suspect it's something like the "capturing a loop variable in a lambda expression" problem which crops up so often. If you can come up with a short but complete program which demonstrates the problem, it will be a lot easier to diagnose.
I suggest you create a simple console application which tries to create (say) 5000 files serializing some simple object. See if you can get that to fail in the same way.
Multi-threading may cause that problem. The 250ms delay is an evidence of that.
Do you have multiple threads doing that?

Categories