Archive

Archive for May, 2009

XAttribute and null values

May 9th, 2009 Comments off

It turns out that for whatever the reason to the constructor for XAttribute does not accept null values for the value parameter. I think this makes sense even though the XElement constructor will accept null values for the value parameter – mostly because it can still function; a null value for an attribute would be nothing not ‘attributeName=”"‘.

The quick and dirty: there’s an easy solution, use an extension method.

    public static class AddXAttribute
    {
        public static XElement NewXAttribute(this XElement xa, XName name, Object value)
        {
            if (xa != null && value != null)
            {
                xa.Add(new XAttribute(name, value));
            }
            return xa;
        }
    }

Now to explain. Say we have a pretty generic BO for which we want to be able to generate some XML.

    public class BusinessObject
    {
        public int Id { get; set; }
        public String Name { get; set; }
        public String Description { get; set; }
    }
        private static String BuildXML(List<BusinessObject> data)
        {
            XDocument xDoc = null;
            try
            {
                xDoc = new XDocument(
                    new XElement("Root",
                        from d in data
                        select new XElement("BO",
                            new XAttribute("Id", d.Id),
                            new XAttribute("Name", d.Name),
                            new XAttribute("Desc", d.Description)
                        )
                    )
                );
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: " + ex.Message);
                return String.Empty;
            }

            return xDoc.ToString();
        }

If we were try and run the BuildXML method with a set of data that included a null in the Name or Description properties then an exception would occur, “Value cannot be null.” This seems like it would be a pretty standard why of using LINQ expressions to build an XML document as simple as possible. We don’t really care about the nulls – we just want them handled. The answer is to use the NewXAttribute method from above as an extension method. The new BuildXML (call it BuildXML2 for now) would be very similar.

        private static String BuildXML2(List<BusinessObject> data)
        {
            XDocument xDoc = null;
            try
            {
                xDoc = new XDocument(
                    new XElement("Root",
                        from d in data
                        select (new XElement("BO"))
                            .NewXAttribute("Id", d.Id)
                            .NewXAttribute("Name", d.Name)
                            .NewXAttribute("Desc", d.Description)
                    )
                );
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: " + ex.Message);
                return String.Empty;
            }

            return xDoc.ToString();
        }

You can still chain together expressions, so the code result is not terribly different than what you would expect. The one difference being that any child data would have to be declared before you would add the attributes. At least the way I think of the data when I’m building it that’s backwards.

I’ve included the entire sample below for completeness.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var data = new List<BusinessObject>();

            // get some data
            data.Add(new BusinessObject()
            {
                Id = 1,
                Name = "One",
                Description = "First One"
            });

            data.Add(new BusinessObject()
            {
                Id = 2,
                Name = "Two",
                Description = "Second One"
            });

            data.Add(new BusinessObject()
            {
                Id = 3,
                Name = "Three",
                Description = "And the third."
            });

            String xml;

            Console.WriteLine("==== Standard =============");
            xml = BuildXML(data);
            Console.WriteLine(xml);
            Console.WriteLine("===========================");

            // we need some null data
            data[2].Description = null;

            Console.WriteLine("==== Standard w/ null data=");
            xml = BuildXML(data);
            Console.WriteLine(xml);
            Console.WriteLine("===========================");

            Console.WriteLine("==== New Extention ========");
            xml = BuildXML2(data);
            Console.WriteLine(xml);
            Console.WriteLine("===========================");

            Console.ReadLine();

        }

        private static String BuildXML(List<BusinessObject> data)
        {
            XDocument xDoc = null;
            try
            {
                xDoc = new XDocument(
                    new XElement("Root",
                        from d in data
                        select new XElement("BO",
                            new XAttribute("Id", d.Id),
                            new XAttribute("Name", d.Name),
                            new XAttribute("Desc", d.Description)
                        )
                    )
                );
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: " + ex.Message);
                return String.Empty;
            }

            return xDoc.ToString();
        }

        private static String BuildXML2(List<BusinessObject> data)
        {
            XDocument xDoc = null;
            try
            {
                xDoc = new XDocument(
                    new XElement("Root",
                        from d in data
                        select (new XElement("BO"))
                            .NewXAttribute("Id", d.Id)
                            .NewXAttribute("Name", d.Name)
                            .NewXAttribute("Desc", d.Description)
                    )
                );
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: " + ex.Message);
                return String.Empty;
            }

            return xDoc.ToString();
        }
    }

    public class BusinessObject
    {
        public int Id { get; set; }
        public String Name { get; set; }
        public String Description { get; set; }
    }

    public static class AddXAttribute
    {
        public static XElement NewXAttribute(this XElement xa, XName name, Object value)
        {
            if (xa != null && value != null)
            {
                xa.Add(new XAttribute(name, value));
            }
            return xa;
        }
    }
}
Categories: .NET Tags: ,

VirtualPathProvider for Assemblies

May 2nd, 2009 Comments off

The VirtualPathProvider class provides some options when building websites. One of the most common is to be able to server ASPX/ASCX requests from embedded resources in an assembly.

A good place to start is from a CodeProject article that address this issue.

First, instead of using hardcode assemblies/paths/etc, a few properties were attached.

        #region Properties
        public String VirtualPath { get; set; }
        public String VirtualFileSystemAssembly { get; set; }
        public String VirtualFileSystemBaseNamespace { get; set; }
        #endregion

Also, instead of just assuming that a file might exist in the assembly if the virtual paths match – because we might want to serve file from multiple assemblies on the same virtual path – the code will check if the resource exists in the assembly.

        private bool DoesExistInVirtualPath(string virtualPath)
        {
            if (!IsInVirtualPath(virtualPath))
                return false;

            Assembly assembly = GetAssemblyReference();

            if (assembly != null)
            {
                String[] resources = assembly.GetManifestResourceNames();
                String resourceName = ConstructAssemblyResourceName(virtualPath);

                if (resources != null && resources.Contains(resourceName, StringComparer.InvariantCultureIgnoreCase))
                    return true;
            }
            return false;
        }

First we check to see if the path even matches, failing that it’s obvious that there isn’t a match. They we attempt to get a reference to the desired assembly. Of course if that fails then there’s no way to be able to serve files from it. Lastly we check the manifest for resources and if there are any does it contain the file we’re looking for. A file name mangling needs to occur (in ConstructAssemblyResourceName) because of the way the files are named as resources.

There are a couple of helper methods used in the above block, they are just there to keep things neat.

        private String ConstructAssemblyResourceName(string resourcePath)
        {
            // nix the base path
            String resourceName = resourcePath.Remove(0, VirtualPath.Length);
            // tack on the base namespace
            if (VirtualFileSystemBaseNamespace.Length > 0)
            {
                resourceName = VirtualFileSystemBaseNamespace + '/' + resourceName;
            }
            // replace directory seperators with .'s
            resourceName = resourceName.Replace('/', '.');
            return resourceName;
        }

        private Assembly GetAssemblyReference()
        {
            if (assemblyReference != null)
                return assemblyReference;

            String binDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            String assemblyName = Path.Combine(binDir, VirtualFileSystemAssembly);

            Assembly assembly;
            try
            {
                assembly = Assembly.LoadFile(assemblyName);
            }
            catch
            {
                return null;
            }

            // if it is a real assembly - keep it
            if (assembly != null)
                assemblyReference = assembly;

            return assembly;
        }

Download the source code: Legomaster.AssemblyPathProvider.

Categories: ASP.NET Tags: ,