I have an XML file that I need to have concurrent access to. I do not need concurrent write, but I do need concurrent read. The perscribed method for reading/modifying this XML file is by using a PowerShell script, which uses Linq to XML (XElement) to work with the XML file.
The ideal scenario is:
- All users on the team will run this PowerShell script at the beginning of their workday, and it will remain running until the end of the workday.
- Throughout the workday, any user on the team will enter pre-defined commands that will query the XML file to collect data. Each time the user runs one of these queries, it will open the XML file, perform the query, close the XML file, then display the results.
- Occasionally, throughout the day, any of those users will need to modify the XML file. The user will enter a pre-defined command, specifying which node they want to modify, then they will enter all of the data they would like to add/modify. The script will then open the file (locking it, so no other user can open it read/write), read the XML file into an
XElement
object, perform the desired operations, then save theXElement
back to the file, and then release the lock. - At any point, while a user is modifying a file (some operations may be lengthy), any other user who tries to make a modification must not be able to open the file (I will catch the exception, and present a "Please wait a few moments" type message). But, they must be able to open the file read-only to execute queries.
I have seen this related post, 'Concurrent file usage in C#', and attempted to implement this, using the below code. In one instance of PowerShell ISE, I execute the Read-Write function, and then while in the "Enter an item name" loop, I execute the Read-Only function in another instance of PowerShell ISE. When attempting to open the document read-only, I receive an exception stating The process cannot access the file 'C:\Path\To\file.xml' because it is being used by another process.
using namespace System.Linq.Xml
using namespace System.IO
Add-Type -AssemblyName 'System.Xml.Linq (rest of Assembly's Full Name)'
$filename = "C:\Path\To\file.xml"
function Read-Only()
{
$filestream = [FileStream]::new($filename, [FileMode]::Open, [FileAccess]::Read)
$database = [XElement]::Load($filestream)
foreach($item in $database.Element("Items").Elements("Item"))
{
Write-Host "Item name $($item.Attribute("Name").Value)"
}
$filestream.Close()
$filestream.Dispose()
}
function Read-Write()
{
$filestream = [FileStream]::new($filename, [FileMode]::Open, [FileAccess]::ReadWrite, [FileShare]::Read)
$database = [XElement]::Load($filestream)
$itemname = Read-Host "Enter an item name ('quit' to quit)"
while($itemname -ne 'quit')
{
$database.Element("Items").Add([XElement]::new([XName]"Item", [XAttribute]::new([XName]"Name", $itemname)))
$itemname = Read-Host "Enter an item name ('quit' to quit)"
}
$filestream.Seek(0, [SeekOrigin]::Begin)
$database.Save($filestream)
$filestream.Close()
$filestream.Dispose()
}
How can I lock a file for exclusive editing, while still allowing read-only access to any other client?
Edit: I have solved this problem by using the solution suggested by mklement0 - using a seperate file as a lock file. Code is posted here: https://pastebin.com/ytzGE7se