Tag Archives: C#

Read Lines in the New Way (or at least since 2010)

I recently discovered a method which has been around since .NET 4 (shame on me for not knowing about it before now). It is in the File class: File.ReadAllLines(string path)

It returns a string array, each string consisting of a line in the text file. So basically, where we previously would have written something like:

FileInfo templateFile = new FileInfo(pathToFile);
TextReader reader = templateFile.OpenText();
List parsedLines = new List();

while ((lineOfText = reader.ReadLine()) != null)
{
	parsedLines.Add(lineOfText);
}

We could now get it done with:

string [] parsedLines = File.ReadAllLines(pathToFile);

I thought I would road-test this method with a little real world situation. The task at hand is to populate a template at run-time with some data and configuration options for an external hardware unit; a high-volume industrial DVD writer. The template in our example is a text file:

# Job file for External Component
Stacker : {0}
Publisher : {0}
Label Details : Date-{0}, Title-{1}, Artist-{2}
Verify Write: {0}
Data: {0}

We will populate those placeholders (the numbers in curly braces) with an array of configuration values which we will store in the App.config file. This enables us to change those values from run to run without having to recompile:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="CurrentTemplate" value="sampleTemplate.job.txt" />
    <add key="Placeholders" value="1,CD_Pub1,12 July 2012,The Southern Harmony and Musical Companion,The Black Crowes,Do not Verify,D:\BurnDataBucket" />
  </appSettings>
</configuration>

The following code will complete the task (note the use of the File.ReadAllLines method at the beginning of the static method ProcessTemplateWithValues):

private const string PlaceHolderPattern = @"{\d}";

        static void Main()
        {
            var pathToCurrentTemplate = GetCurrentTemplate();
            var configOptionsForTemplate = GetConfigForTemplate().ToArray();

            var populatedLines = ProcessTemplateWithValues(pathToCurrentTemplate, configOptionsForTemplate);
            // ... do something with populated template
        }

        private static IList ProcessTemplateWithValues(string pathToCurrentTemplate, params object[] args)
        {
            string [] linesOfText = File.ReadAllLines(pathToCurrentTemplate);
            string populatedLine = string.Empty;
            var listOfLines = new List();
            int startingArgsIndex = 0;

            foreach (var lineOfText in linesOfText)
            {
                int numberOfPlaceholders = lineOfText.GetNumberOfPatternPlaceholders(PlaceHolderPattern);

                if (numberOfPlaceholders > 0)
                {
                    if (TryParseValues(lineOfText, numberOfPlaceholders, ref populatedLine, ref startingArgsIndex, args))
                    {
                        listOfLines.Add(populatedLine);
                    }
                    else
                    {
                        throw new FormatException(string.Format(
                            "The line beginning with the word {0} failed to parse.",
                            new String(lineOfText.TakeWhile(c => c != ':').ToArray())));
                    }
                }
                else
                {
                    listOfLines.Add(lineOfText);
                }
            }

            return listOfLines;
        }

        public static bool TryParseValues(string lineOfText, int numberOfPlaceholders, ref string populatedLine, ref int startingArgsIndex, params object[] args)
        {
            var argsForThisLine = new object[numberOfPlaceholders];

            if (startingArgsIndex > args.Length - 1 || startingArgsIndex < 0)
                return false;

            Array.Copy(args, startingArgsIndex, argsForThisLine, 0, numberOfPlaceholders);

            startingArgsIndex += numberOfPlaceholders;

            populatedLine = string.Format(lineOfText, argsForThisLine);

            return true;
        }

        static string GetCurrentTemplate()
        {
            return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Templates", ConfigurationManager.AppSettings["CurrentTemplate"]);
        }

        static IEnumerable GetConfigForTemplate()
        {
            return ConfigurationManager.AppSettings["Placeholders"].Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
        }

We can now take those configuration options and use them, as the elipses in the code samples is meant to indicate.

Now, compare with the same task being implemented using the pre-.NET4 style of code:

private const string PlaceHolderPattern = @"{\d}";

        static void Main()
        {
            var pathToCurrentTemplate = GetCurrentTemplate();
            var templateFile = new FileInfo(pathToCurrentTemplate);
            TextReader templateReader = templateFile.OpenText();
            var configOptionsForTemplate = GetConfigForTemplate().ToArray();

            var populatedLines = ProcessTemplateWithValues(templateReader, configOptionsForTemplate);
            // ... do something with populated template
        }

        private static IList ProcessTemplateWithValues(TextReader reader, params object[] args)
        {
            string lineOfText = string.Empty;
            string populatedLine = string.Empty;
            var listOfLines = new List();
            int startingArgsIndex = 0;

            while ((lineOfText = reader.ReadLine()) != null)
            {
                int numberOfPlaceholders = lineOfText.GetNumberOfPatternPlaceholders(PlaceHolderPattern);

                if (numberOfPlaceholders > 0)
                {
                    if (TryParseValues(lineOfText, numberOfPlaceholders, ref populatedLine, ref startingArgsIndex, args))
                    {
                        listOfLines.Add(populatedLine);
                    }
                    else
                    {
                        throw new FormatException(string.Format(
                            "The line beginning with the word {0} failed to parse.", new String(lineOfText.TakeWhile(c => c != ':').ToArray())));
                    }
                }
                else
                {
                    listOfLines.Add(lineOfText);
                }
            }

            return listOfLines;
        }

        public static bool TryParseValues(string lineOfText, int numberOfPlaceholders, ref string populatedLine, ref int startingArgsIndex, params object[] args)
        {
            var argsForThisLine = new object[numberOfPlaceholders];

            if (startingArgsIndex > args.Length - 1 || startingArgsIndex < 0)
                return false;

            Array.Copy(args, startingArgsIndex, argsForThisLine, 0, numberOfPlaceholders);

            startingArgsIndex += numberOfPlaceholders;

            populatedLine = string.Format(lineOfText, argsForThisLine);

            return true;
        }

        static string GetCurrentTemplate()
        {
            return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Templates", ConfigurationManager.AppSettings["CurrentTemplate"]);
        }

        static IEnumerable GetConfigForTemplate()
        {
            return ConfigurationManager.AppSettings["Placeholders"].Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
        }

There we see that a few extra calories are burned in extra code with the explicit creation and use of a TextReader. So a bit of a saving is made with less code. But note that there is only 1 iteration of the lines in the old way. Using File.ReadLines, we iterated once using that method in and of itself (inside the framework) and a second time in the foreach loop which we wrote. There’s always a tradeoff.

I should also point out a handy little extension method which I wrote and used in that code. GetNumberOfPatternPlaceholders returns the number of times a placeholder appears in a particular line:

public static int GetNumberOfPatternPlaceholders(this string source, string pattern)
{
	var regex = new Regex(pattern);
	var matches = regex.Matches(source);

	return matches.Count;
}

Whilst File.ReadAllLines does not buy us much in this scenario (in terms of briefer code), I still think it’s a welcome addition to the framework and I’m sure I will find good use for it from time to time.

Get the code:

HtmlHelper for Javascript Script Elements (Tags)

I wrote an HtmlHelper for Javascript elements which, I believe, makes for a more maintainable codebase for adding script elements to a view:

        public enum JavascriptPathType
        {
            Relative = 0,
            FullWebPath = 1
        };

        public static MvcHtmlString Script(this HtmlHelper helper, string jsFileName, JavascriptPathType pathType)
        {
            // Instantiate a UrlHelper
            var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);

            // Create tag builder
            var builder = new TagBuilder("script");

            switch (pathType)
            {
                case JavascriptPathType.Relative: builder.MergeAttribute("src", urlHelper.Content("~/Scripts/" + jsFileName));
                    break;
                case JavascriptPathType.FullWebPath: builder.MergeAttribute("src", urlHelper.Content(jsFileName));
                    break;
            }

            // Add attributes
            builder.MergeAttribute("type", "text/javascript");

            // Render tag. Note: script tags have to have an End tag and cannot be self-closing.
            return new MvcHtmlString(builder.ToString(TagRenderMode.Normal));
        }

The most common scenario is where your javscript file is located in the Scripts directory of your project, in which case, all you need to do is pass in the name of the javascript file:

@Html.Script("jquery-1.8.2.min.js", JavascriptPathType.Relative);

This saves you from typing the script tags and the text type=”text/javascript” everytime.
I also added the custom enum JavascriptPathType to account for scenarios where the src of the script tag is pointing to someplace other than within your own project. For example, if you were using the Google maps API, the usage would be:

@Html.Script("http://maps.google.com/maps/api/js?sensor=false", JavascriptPathType.FullWebPath);

I’m thinking next will be the same kind of helper for Css files.

Signing a Project and InternalsVisibleTo – Extract a Public Key for the Unit Test Project

I recently completed a project and went to give it a strong name by signing it, when an interesting thing happened. It didn’t compile anymore. The relevant error message was Friend assembly reference ‘WinFormsMvp.UnitTests’ is invalid. Strong-name signed assemblies must specify a public key in their InternalsVisibleTo declarations.
Upon investigation, I discovered what was going on.

One of my unit tests needed access to the internals of a class in my project i.e. I had a class with the protection level of internal being exposed to my unit tests. This was done using the following line of code in the AssemblyInfo class:

[assembly: InternalsVisibleTo("WinFormsMvp.UnitTests")]

However, when I signed the project, the solution wouldn’t build because the Unit Test project also needs to be signed. A strong-named assembly can only reference strong-named assemblies.

So, the solution:

  1. create a separate strong-name key for the unit test project:
    sn -k WinformsMVPTestsKey.snk
  2. extract the public key for the key you just created (prepare yourself for a big string):
    sn –tp WinformsMVPTestsKey.snk
  3. add the extra detail into the line of the AssemblyInfo class described above, assigning the public key string to the PublicKey value:
[assembly: InternalsVisibleTo("WinFormsMvp.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100eb6fea45976e16daadfd3f49c17d7d8abec952b6acd435ceb9ea24efdcfe7b2a2a528f04a6a8269ee88a2cf4b839668a11a580caeab03762abf968cff05d0107c786dc5b7d9d97b14a13277df132222350ad4dc1dd24c03b1d10db168066fbe8ddf3f49b04b6248264479d21bfdabd79b68291da2d62a5ab17fc55819869a4e9")]

That’s how it’s done!

String Split – Really!?

I’ve seen a lot of code recently where the developer has written something like this:

            string cars = "Honda;BWM;Mazda;Audi";
            string[] carsArray = cars.Split(new char[] {';'});

Although the method signature specifies a char array, it’s just as easy and effective to write:

            string cars = "Honda;BWM;Mazda;Audi";
            string[] carsArray = cars.Split(';');

Or if you like to use constants, as I do:

    class Program
    {
        private const char SemiColon = '\x003B';

        static void Main(string[] args)
        {
            string cars = "Honda;BWM;Mazda;Audi";
            string[] carsArray = cars.Split(SemiColon);

            carsArray.ToList().ForEach(Console.WriteLine);

            Console.WriteLine("{0}Press any key to kill ...", Environment.NewLine);
            Console.ReadLine();
        }
    }

Thank you. Good night.

How to Write a Service to Run Under a Custom User Account

So you want to write a Windows Service which runs under a particular user account, but you’re having trouble figuring out how to configure it to use your custom user? This is the small hurdle I faced a couple of weeks back.

To start at the beginning, I wanted to write a Service which detects when iTunes downloads podcast files via its podcast update function. It then copies details of the newly downloaded podcast into a database. I wanted to lock this service down, by making it run under a particular custom user account, which will have its permissions completely locked down to a bare minimum (as is best practice).

I’ve written services before, so I got right up to the point where you tell it which user account to run under. I clicked the dropdown box (pictured in Figure 1), selected User from the choices and then wondered to myself, “Where do I put the authentication credentials.”

Figure 1

This was not immediately obvious. And surprisingly enough, my various books and Google were not forthcoming. This, I found surprising, given the fact that this should be a reaonably frequent scenario.

Lets take a step back for a moment. On the design surface displayed in Figure 1, you can see a PodcastWatcherServiceInstaller component (derives from System.ServiceProcess.ServiceInstaller) and a PodcastWatcherServiceProcessInstaller component (derives from System.ServiceProcess.ServiceProcessInstaller). There is 1 PodcastWatcherServiceInstaller for every service installed as part of the package and a single PodcastWatcherServiceProcessInstaller for the executable which will install all those services. In most cases, there will be only 1 service installed with any given installment package, so it’s usually a 1-to-1 mapping. So where do the user credentials fit in?

As can be seen from the above screen shot, the GUI does not provide any input fields for usernames or passwords. With some extensive reading of MSDN and some trial and error, I discovered the solution. The class in which those two components are members is the ProjectInstaller class. Just press F7 at the designer view and you will be presented with the code. It is in the code that you can specify the Username and Password for the User:

    [RunInstaller(true)]
    public partial class ProjectInstaller : System.Configuration.Install.Installer
    {
        public ProjectInstaller()
        {
            InitializeComponent();

            PodcastWatcherServiceProcessInstaller.Account = ServiceAccount.User;
            PodcastWatcherServiceProcessInstaller.Username = @"PACEMAN\PodcastWatcherUser";
            PodcastWatcherServiceProcessInstaller.Password = "s0m3pa55w0rd";
        }
    }