Quick Resource Helper Class

The production app I’m currently working on involves reading a client-provided extract (on the magnitude of hundreds of thousands of lines), transforming that data, and importing it into our system for processing. This happens once a day. The current process involves importing this information into staging tables on the DB side, then running stored procedures to transform the data.

As part of an effort to both improve efficiency as well as add in scalability, the DB team has decided this process should import into session-level temporary tables. This will allow multiple imports to go on at once if need be, as well as give some performance enhancements. The software side of this requires that the importing software opens a connection, and then creates the temporary tables on that connection. From there, the software reads the client-provided extract in about one thousand lines batches, and uses SqlBulkCopy to transfer it to SQL server.

Most of the architecture in this problem was laid out already, and not having any previous experience dealing with data sets this large, I followed the rest of the team’s leads. The temporary table creation needed to be done on the software side, and the scripts themselves needed to be part of the project. Not wanting to rely on folder paths to read in the data, and really not wanting to have string variables that held the scripts, I opted to leave the scripts intact, but use them as embedded resources. I haven’t worked with embedded resources before, honestly, so I did find out a couple of things.

The main call you’ll need is:

Assembly.GetAssembly(yourAssembly).GetManifestResourceStream(fullResourceName)

The tricky thing can be the file path. It uses periods to denote folder paths to load your project. Not difficult, but not the most obvious thing either. I wanted to “hide” that from my consuming apps, as they really don’t care about how the resource accessor likes to make it’s names. They just want their file. They do know about the project structure of the assembly that has their resource, however.

The next thing that I accidentally did wrong was the assembly – you need to provide the assembly that the resource lives in, for obvious reasons. Given that this helper class lives in a “Utilities” type of project, it’s no shock that as soon as I refactored my class out, my tests broke.

So, with those two thoughts in mind, here’s the API I wound up with, along with the full class:

public static class ResourceHelper
    {
        public static string GetResourceAsString(Type typeOfCallingObject, string namespaceName, string fileName)
        {
            return GetResourceAsString(typeOfCallingObject, string.Format("{0}.{1}", namespaceName, fileName));
        }

        public static string GetResourceAsString(Type typeOfCallingObject, string namespaceName, string folderName, string fileName)
        {
            return GetResourceAsString(typeOfCallingObject, string.Format("{0}.{1}.{2}", namespaceName, folderName, fileName));
        }

        public static string GetResourceAsString(Type typeOfCallingObject, string namespaceName, string[] folderPaths, string fileName)
        {
            var fullFolderPath = new StringBuilder();

            if(folderPaths != null && folderPaths.Count() > 0)
            {
                foreach (var folderPath in folderPaths)
                {
                    fullFolderPath.AppendFormat("{0}.", folderPath);
                }

                // Remove the trailing period
                fullFolderPath.Remove(fullFolderPath.Length -1, 1);
            }

            return GetResourceAsString(typeOfCallingObject, string.Format("{0}.{1}.{2}", namespaceName, fullFolderPath, fileName));
        }

        public static string GetResourceAsString(Type typeOfCallingObject, string fullResourceName)
        {
            var fileStream = Assembly.GetAssembly(typeOfCallingObject).GetManifestResourceStream(fullResourceName);

            if (fileStream == null)
            {
                throw new Exception(string.Format("Resource '{0}' not found!", fullResourceName));
            }

            var sqlStreamReader = new StreamReader(fileStream);

            return sqlStreamReader.ReadToEnd();
        }
    }

Note that most times, the first parameter is liable to just be “GetType()”. I could’ve maybe made it an extension method, or made the type a generic parameter, but it really felt like overkill for such a small class. Here’s some examples of calling it (from my unit tests):

ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests.RootResource.txt");
ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests", "RootResource.txt");
ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests", "ResourcesFolder", "FolderResource.txt");
ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests", new[] { "ResourcesFolder" }, "FolderResource.txt");
ResourceHelper.GetResourceAsString(GetType(), "UtilitiesTests", new[] { "ResourcesFolder", "NestedFolder" }, "NestedResource.txt");
 

Yeah, this is all extremely basic, but thought it still might be useful for someone. I had fun doing it, none-the-less.

0 comments:

Designed by Posicionamiento Web | Bloggerized by GosuBlogger