10

I have an API that returns XElement's, and I want the document behind those XElement's to be immutable (read-only). I need it for:

  • Not to give devs an ability to change it accidentally :)
  • Improving performance - creating a copy of an XDocument might be a performance "heavy" operation in some cases.

It doesn't seem to possible to inherit & override the necessary behavior in XDocument/XElement/XContainer, because all virtual methods there are marked as internal:

internal virtual void XContainer.AddAttribute(XAttribute a)
{
}

So my question is - is there a way make it happen, or it is better to have a different API that will either return something like XPathNavigator's, or it is better to have own classes like IReadOnlyXElement, etc.?

mak
  • 13,267
  • 5
  • 41
  • 47
Dmitry Dzygin
  • 1,258
  • 13
  • 26

3 Answers3

13

I doubt that the autor is still waiting for answers, but perhaps someone else will find it useful.

You can kind-of make the XDocument immutable by using its Changing event:

    class Program
    {
        static void Main(string[] args)
        {
            var xdoc = XDocument.Parse("<foo id=\"bar\"></foo>");
            xdoc.Changing += (s, ev) =>
            {
                throw new NotSupportedException("This XDocument is read-only");
            };

            try
            {
                xdoc.Root.Attribute("id").Value = "boo";
            }
            catch (Exception e)
            {
                Console.WriteLine("EXCEPTION: " + e.Message);
            }

            Console.WriteLine("ID on exit: " + xdoc.Root.Attribute("id").Value);

            Console.ReadKey();
        }
    }

// Console output:
// EXCEPTION: This XDocument is read-only
// ID on exit: bar

Not the nicest solution, but it does provide a basic mechanism preventing accidental changes.

Sly
  • 408
  • 4
  • 8
5

You could create an XElement wrapper that is similar to ReadOnlyCollection<T>.

public sealed class ReadOnlyXElement
{
    private readonly XElement _element;


    public string Value
    {
        get { return _element.Value; }
    }


    public ReadOnlyXElement(XElement element)
    {
        _element = element;
    }


    public IEnumerable<ReadOnlyXElement> Elements()
    {
        foreach (var child in _element.Elements())
        {
            yield return new ReadOnlyXElement(child);
        }
    }

    public IEnumerable<ReadOnlyXElement> Elements(XName xname)
    {
        foreach (var child in _element.Elements(xname))
        {
            yield return new ReadOnlyXElement(child);
        }
    }
}
ChaosPandion
  • 77,506
  • 18
  • 119
  • 157
3

IMHO, it's probably better to make your own wrapper class for interacting with XDocuments/XElements. You can then limit the ability of a dev to write over the file in code.

I say limit because with enough information (location, schema (if needed)) a developer could use the stock XMLClasses to do whatever they wanted. The end all be all would be to make the file read-only on disk and make sure they (devs, users) do not have permission to change the read-only access on the file.

Tony Abrams
  • 4,505
  • 3
  • 25
  • 32