Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to create an immutable (read-only) XDocument?

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.?

like image 399
Dmitry Dzygin Avatar asked Aug 27 '10 14:08

Dmitry Dzygin


2 Answers

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.

like image 58
Sly Avatar answered Sep 21 '22 11:09

Sly


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);
        }
    }
}
like image 23
ChaosPandion Avatar answered Sep 21 '22 11:09

ChaosPandion