The power of FUNC<T>. Not a new George Clinton song.

by Anthony 9. March 2014 09:32

So this post has been a long time coming.  I have been busy with life in general so it has been a while since I posted.  But, this post in particular was something I have wanted to get around to for about 6 years now.  So why so long?  I'm not really sure; I have been using this particular technique for a long time and I just never got around to writing about it.  Now I know many people reading this already know the benefits of Func<T> and Action<T>, but I wanted to show a practical example of its use.  

So recently at work I was presented with a task to create a server side BB Code parser, the why is not important.  I found one that was helpful, but it was a bit too rigid for me.  So I decided to modify it with a factory pattern.  Everything was going great and for the most part it was an easy translation until two tags completely wrecked my world.  OK, maybe that is a bit of an exaggeration, but they did pose a problem.

Most tags in BBCode map directly to HTML ([b]=<b>, [u] = <u>, [url=www.google.com] = <a href="www.google.com">, etc...) however the [list] and [size] tags do not. To specify an ordered uppercase roman numeral list in BbCode it is [list=I] in HTML it is <ol style="list-style-type:upper-roman;">.  So what to do?  Func<T> to the rescue.  Func<T> allowed me to include the functionality of my translation with out sacrificing encapsulation.  So lets look at the code.

My first implementation of my Tag class for BB Code was pretty simple:

/// <summary>
/// Name of the BB Code tag
/// </summary>
public string BbTagName { get; set; }
/// <summary>
/// Name of the corresponding Html Tag
/// </summary>
public string HtmlTagName { get; set; }
/// <summary>
/// The property/style attrib the BB Code links to 
/// </summary>
public string HtmlProperty { get; set; }
/// <summary>
/// If true links to a HTML style attribute not a property  
/// </summary>
public bool IsStyleReplacement { get; set; }
/// <summary>
/// Initializes the formatting tag /// </summary> public void Init(); /// <summary> /// formats the tag /// </summary> /// <param name="Data">The data that will be converted to HTML</param> /// <returns>The converted string</returns> public string Format(string Data)

This allowed me to create a factory like this:

public class BbCodeParser
{
    private List<Tag> _tags = new List<Tag>
    {
        new Tag
        {
            BbTagName = "list",
            HtmlTagName = "ol",
        },

        new Tag
        {
			//li maps directly to html li no translation needed
            BbTagName = "li",
        },
        
        new Tag
        {
            BbTagName = "size",
            HtmlProperty = "font-size",
            HtmlTagName = "span",
            IsStyleReplacement = true,
        },
        new Tag
        {
            BbTagName = "font",
            HtmlProperty = "font-family",
            HtmlTagName = "span",
            IsStyleReplacement = true,
        },
    };

    public void Init()
    {
        //init all tags
        _tags.ForEach(t => t.Init());
    }

    public string Format(string data)
    {
        //runformat on all tags
        _tags.ForEach(t => data = t.Format(data));
        return data;
    }
}

 

Easy Peasy. Next came the dreaded regex for BBCode (if you would like to see it explained go here it is an awesome regex site):

//{0} will be replace with the BBCode

private const string BbCodeRegEx = "\\[(?:\\b{0}\\b)(?:\\s*)(=?)((?:.|\\n)*?)(?:\\s*)\\]((?:.|\\n)*?)\\[\\/\\b{0}\\b(?:\\s*)\\]";

This was a generic regex that captures pretty much all BB code tags (my full implementation required some additional chicanery.)   So now for the format class:

return _regReplace.Replace(Data,htmlReplacement);

Everything was happy then came my [list=[a,i,I,A,1]] problem.  I had a few options:

  1. Allow the factory creator to use their own regex.  (I know as a library consumer I would hate this option).
  2. Create a special tag for list. (only if there was no other option).
  3. Tell my boss we can't do it :) (not really an option)
  4. Allow the factory creator to implement their own translation 

In .Net 2.0 the last option would require a delegate that pointed to a method in a separate class, not very fun or supportable.  But since 3.5 introduced Func<T> and Action<T>, it is actually a pretty easy change:

Firstly I added the property:

/// <summary>
/// Allow the consumer to provide a custom translation
/// </summary>
public Func<string, string> PropertyConversion { get; set; } 

Then I modified the RegEx replacement to support the new custom code:

/// <summary>
/// formats the tag
/// </summary>
/// <param name="Data">The data that will be converted to HTML</param>
/// <returns>The converted string</returns>
public string Format(string Data)
{
	return _regReplace.Replace(Data,MatchReplacer);
}

private string MatchReplacer(Match m)
{
	string htmlReplacement;
	//this will be the value after the '=' in bbcode
	string convertedValue = m.Groups[2].Value;
	
	//if a conversion exists use it
	if (PropertyConversion != null)
	{
		convertedValue = this.PropertyConversion(convertedValue);
	}

	//in indexed groups index 0 is the entire find so we need to start at 1
	if (m.Groups[1].Value == "=")
	{
		htmlReplacement = IsStyleReplacement ? _htmlReplacementForStyle : _htmlReplacementForAttrib;
		htmlReplacement = Regex.Replace(htmlReplacement, "\\$2", convertedValue);

	}
	else
	{
		htmlReplacement = _htmlReplacementBasic;
	}
	htmlReplacement = Regex.Replace(htmlReplacement, "\\$3", m.Groups[3].Value);
	return htmlReplacement;
}

I highlighted the important code.  And that was it.  Now the consumer can do all kinds of crazy stuff.

So my new list tag implementation looks like this:

new Tag
{
	BbTagName = "list",
	HtmlProperty = "list-style-type",
	HtmlTagName = "ol",
	IsStyleReplacement = true,
	PropertyConversion = s =>
	{
		switch (s)
		{
			case "1":
				return "decimal";
			case "A":
				return "upper-alpha";
			case "a":
				return "lower-alpha";
			case "i":
				return "lower-roman";
			case "I":
				return "upper-roman";
			default:
				return "decimal";
		}
	},
},

So no matter the translation I pretty much have it covered.  This has been a useful technique for me for years, I just always forget to post it.  Hopefully it helps someone.

I have attached the full code below:

BbCodeToHtml.zip (4.39 mb)

Happy Coding. 

Tags: ,

Linq | OOD

Not all JSON posts are created equal

by Anthony 16. November 2013 07:22

For a little while I was confused on how to perform a JSON post to MVC and have the objects model automatically resolve.  Should I use JSON.Stringify or should I just post the JSON directly?  It turns out the answer is YES with caveats?  What?!?!?  Let me explain.  Lets say you are posting a a simple list of parameters to a method with a signature like this:

public ActionResult _GetBooksbyGenre(string genre, int count, string requestedBy)

This action can be called by performing a simple JSON post like this:

 $.ajax({
        url: "_GetBooksbyGenre",
        type: "post",
        data: { genre: "Horror", count: 10, requestedBy: "me" },
        success: function(data) {
        model.currentGenre(data);
    }
});

No fuss no muss, a simple call with no JSON stringify.  Now lets say you have a complex nesting of JSON Objects like Genre>Book>Author

 

Here is a JSON Representation of that object:

{
	Name: "Horror",
	Books:
	[
		{
			Title: "The Call of Cthulhu",
			WritenBy: {
				FirstName: "H.P.",
				LastName: "Lovecraft"
			}
		},
		{ Title: "The Fall of the House of Usher" }, {
			WritenBy: {
				FirstName: "Edgar Allan",
				LastName: "Poe"
			}
		}]
};

If we post this to a action with the following signature :

public ActionResult _GetBooksbyGenre(string genre, int count, string requestedBy)

With a similar ajax method:

$.ajax({
	url: "_AddBooksbyGenre",
	type: "post",
	data: horrorGenre,
	success: function(data) {
		model.currentGenre(data);
	}
});

It posts just fine and we get the name of the Genre But all of the nested objects are null?  This occurs because the MVC object serializer does not understand the nested representation since it is not JSON it is POST data.  The difference is the standard post data is not JSON, the $.ajax method is interpreting the json and submitting it as form data.  The post data looks like this:

Name=Horror&Books%5B0%5D%5BTitle%5D=The+Call+of+Cthulhu&Books%5B0%5D%5BWritenBy%5D%
5BFirstName%5D=H.P.&Books%5B0%5D%5BWritenBy%5D%5BLastName%5D=Lovecraft&Books%5B1%5D%
5BTitle%5D=The+Fall+of+the+House+of+Usher&Books%5B2%5D%5BWritenBy%5D%5BFirstName%5D
=Edgar+Allan&Books%5B2%5D%5BWritenBy%5D%5BLastName%5D=Poe

That is not JSON, and it is not in a format MVC understands.

If we make minor changes to the ajax call:

$.ajax({
	url: "_AddBooksbyGenre",
	type: "post",
        contentType: "application/json; charset=utf-8",
	data:  JSON.stringify(horrorGenre),
	success: function(data) {
		model.currentGenre(data);
	}
});

 

The key to the changes are the highlighted items.  When this is posted you get the following in the request stream:

{"Name":"Horror","Books":[{"Title":"The Call of Cthulhu","WritenBy":{"FirstName":"H.P.","LastName":"Lovecraft"}},{"Title":"The Fall of the House of Usher"},{"WritenBy":{"FirstName":"Edgar Allan","LastName":"Poe"}}]}

Now that is JSON.  So the moral of the story is, if you are just posting flat params you can use JSON.stringify but it doesn't really matter.  If you are posting complex JSON you need to use JSON.stringify with the "application/json" content type.  I hope that clears up the confusion for some people.  I know, for a while, I just thought I was going crazy when it worked sometimes but not other but when I finally understood what was going on it made complete sense.  

 

 

Tags: , , ,

AJAX | jquery | JSON | MVC5

Visual Studio 2013 Don't do this...

by Anthony 10. November 2013 13:35

I just installed Visual Studio 2013 and wanted to create a new MVC project.  I could wait to see all of the new cool templates. So I did like I usually do Open visual studio and picked the File>New>Project:

And I went to web templates:

 

Then I thought PFFF, I don't want no stinking ASP.NET Web Forms, not noticing it did not say "ASP.NET Web Forms" it says "ASP.NET Web Application".  So I went and looked at the Visual Studio 2012 templates:

 

 

OH THERE IT IS... Wrong, don't do this.  You will spend the next 20 minutes re-factoring to MVC 5.  If I just paid more attention I would have seen this:

 

When I paid more attention I clicked the correct link which was the first link I came to.  Then I was presented with the following:

 

Color my face red...  Once I created the app this way all of the new features (Twitter Bootstrap, MVC 5, and EF 6.0) were present.  Hopefully this save someone the time I wasted.

 

Happy Coding!!!

Tags: ,

MVC5 | Visual Studio 2013

MVC 4.0, Knockout and Twitter Bootstrap

by Anthony 2. November 2013 11:30

I recently did a presentation that was mainly going to be focused on MVC 4.0 and Knockout, but I have to say I was really impressed with Twitter bootstrap.  It was really easy to create a professional looking consistent, responsive web site with just a bit of customization.  

I posted the bits to GitHub, but the PowerPoint is attached here.  Sorry for the lack of posts recently, I have been swamped at work.  Hopefully I will be able to do more side stuff going forward.

The Adventure Works DB 2012 can be found here.

MVC 4.pptx (925.12 kb)

Tags: , ,

Knockout JS | MVC4 | Twitter Bootstrap

Calendar

<<  April 2014  >>
MoTuWeThFrSaSu
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar

Page List

RecentComments

Comment RSS