Archive for the ‘How To’ Category

May 11th, 2009

Creating Your First MVC ViewEngine

A question that I have been hearing a lot lately is:

How do I change the view location in MVC?

But what they really mean to say is:

How do I create a new ViewEngine that uses the view locations of my choosing?

It is actually very simple to do, and once you see it, I think you will agree with my assessment.  The first thing we are going to do to create our custom ViewEngine, is define the paths that we want to use for our master pages, view pages, and shared pages.  I have taken the liberty to define the following paths, you can customize them however you wish:

  • Master Pages:
    ~/Templates
    it use to be ~/Views/Shared or the controllers view
  • View Pages:
    ~/Views
  • Shared Pages:
    ~/Common
    it use to be ~/Views/Shared

The next thing we need to do is create a new class for our ViewEngine, for this example we are going to call it SimpleViewEngine.

public class SimpleViewEngine : VirtualPathProviderViewEngine
{
}

As you might have noticed from above our SimpleViewEngine inherits from VirtualPathProviderViewEngine, this is the root ViewEngine that uses the VirtualPathProvider (VPP). The VPP provides a way for web applications to read files off the file system in their local web application, so it is perfect for what we are doing. If you don’t want a file system based ViewEngine, and maybe want a ViewEngine based from the database, you can use the IViewEngine interface to create your own custom ViewEngine that fits your needs. (MVC is very flexible, by design)

The next thing we need to do is code our paths in to our SimpleViewEngine. We will do this in the constructor, so that they only have to be initialized once for the entire life span of our SimpleViewEngine.

public SimpleViewEngine ()
{
	/* {0} = view name or master page name
	 * {1} = controller name
	 */

	// create our master page location
	MasterLocationFormats = new[] {
		"~/Templates/{0}.master"
	};

	// create our views and common shared locations
	ViewLocationFormats = new[] {
		"~/Views/{1}/{0}.aspx",
		"~/Common/{0}.aspx",
	};

	// create our partial views and common shared locations
	PartialViewLocationFormats = new[] {
		"~/Views/{1}/{0}.ascx",
		"~/Common/{0}.ascx"
	};
}

As you can see the format is pretty straight forward. We create a string[] array with the paths of where our master pages, views, and common views are located. The only thing that we need to do is set place holders in our path so the the VirtualPathProviderViewEngine can replace the master name, view name, and controller name to construct our appropriate path.

  • {0}: is the view name or master page name.
  • {1}: is the controller name.

After we have done the hard part, which honestly wasn’t that hard, of creating the constructor with the paths, we just need to return the view objects from the constructed partial paths. Since we are using the standard ASP.NET Web Form (ASPX/ASCX) rendering engine. We are able to leverage the work already done by the MVC team and just return a new instance of the WebFormView object.

protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
	return new WebFormView(partialPath, null);
}

protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
	return new WebFormView(viewPath, masterPath);
}

Nothing really earth shattering here, just simply filling out the constructor with the proper parameters from our method, and then returning the newly created view. If you wanted to create a view based out of the database, or off your own syntax (meaning not ASP.NET syntax) then you would have to create your own view based off of the IView interface. But for this example we are only concerned with changing where our views are located.

There is one more thing that we need to do, and that is register our new SimpleViewEngine for use in the framework. The registration of view engines is done in the Global.asax, similar to the same way we register new routes.

public static void RegisterViewEngines(ViewEngineCollection viewEngines)
{
	viewEngines.Clear();
	viewEngines.Add(new SimpleViewEngine());
}

public static void RegisterRoutes(RouteCollection routes) { ... }

protected void Application_Start()
{
	RegisterRoutes(RouteTable.Routes);
	RegisterViewEngines(ViewEngines.Engines);
}

So we are now done. You have created a new view engines, defined your own routes, and registered this view engine with the MVC framework. Some other types of paths you may want to consider trying for your applications, using a custom ViewEngine, are special folders for your mobile or Facebook versions of your website.

  • Mobile: ~/Views/{1}/Mobile/{0}.aspx
  • Facebook: ~/Views/{1}/Facebook/{0}.aspx

I told you it was simple and straight forward, and I hope you agree that the MVC team has done an awesome job at providing a very flexible framework for us to tweak and customize it so it fits our applications.

Tags: , ,

Posted in ASP.NET, C#, How To | kick it on DotNetKicks.com | Bookmark | View blog reactions | 10 Comments »

February 25th, 2009

How not to get help on an Open Source Project

So over the past couple of weeks I have been helping a user get up and running on a project I have developed called Managed Fusion Url Rewriter and Reverse Proxy.  Now I understand that my project isn’t well documented, and I really need to work on that.  So, until I get the time to better document the project, I willingly spend my time helping people through their issues.  99.9% of the time everything works out, and everybody walks away happy.  However there is this 0.1% of the time that the conversation ends like this:

Fuck all this shit crazy.  All I wanted was a revers proxy.  I can set this shit up in 20 minutes with ISA.  I don’t know why this is sooooooooooooooooooo hard.

The syntax of RewriteRule is soooooooo confusing.  WTF does ^/(.*)$ mean?
any why is there a $1 on the end of my other “thing”

RewriteRule ^/(.*)$        http://192.168.0.35/$1 [P]

What does port :8888 have to do with anything.

This is just getting to hard to make work.  I should need to have advanved knowledge of http to make stupid proxy work.
I want to proxy everything.

I don’t think the problem is with the traffic between the two web servers, the problem seems to only apply to the number of /’s in the query string and their placement.

My guess is if I could figure out how to make more RewriteRules and define all the / cases the shit would just go.  Somehow when there are a few slashes in the query string your guy drops the ball.  Maybe because my RewriteRule is balls or because it’s broken.  I wouldn’t know either way.

All I want it to do it fucking work.

Ugh

I mean how do you respond to this?  The guy basically has started freaking out on me, because he doesn’t understand the basic premis of what my open source project is trying to accomplish and is unwilling to spend the time to learn about the mod_rewrite syntax. Plus none of what he is saying is true, I eat my own dog food on this project.  So I imidiatly know about simple problems such as query strings are not working.

Basically the whole problem came down to the fact that this guy was in a crunch, didn’t understand the basics of the internet, and thought it would be easier to use a new technology instead of one that is proven for him such as ISA server.  None of this is my problem so it is inappropriate to swear at a person just trying to help you out.  I understood he was frustrated with a technology he has never used before, but I wasn’t going to continue a conversation with a guy who was proven to be unstable and irrational.  So I just replied with:

Ok I am done you are on your own.

There really wasn’t any point to this post besides airing my disgust at this type of user.  Also to shed light on the fact that even though I am developing this software on my free time, giving it away for free, and supporting it for free, that there are many people out there that don’t understand this and demand the same level of support as if they just paid you a months worth of their salery for support.

If you ever find yourself heading down this path, be aware that people who create open source software are dedicating their free time to help you out.  So be grateful for their help, because they could just as easily blow you off and spend time with their family.

Tags: , ,

Posted in How To, Rant | kick it on DotNetKicks.com | Bookmark | View blog reactions | 6 Comments »

February 17th, 2009

TF30042: The database is full. Contact your Team Foundation Server administrator.

Today I received the following error while trying to check in some code after a marathon night of coding:

TF30042: The database is full. Contact your Team Foundation Server administrator.

I got one of those “oh crap” sinking feelings, that some how my TFS server had decided to just die.  After doing a little research on this error, which there is very little (read close to none) information about on the internet.  So I gave up searching and decided to do a little trial and error adhock testing, and I found out that this error has nothing to do with the database but everything to do with the size of the database’s log file.  I came up with the following solution, that you will want to run in Microsoft SQL Server Management Studio:

WARNING!!! My TFS server is in a non-production environment and I am basically the only one who uses it.  Make sure to check with your network administrator and make a back up before you run the following code.

USE [master]

ALTER DATABASE [ReportServer] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [ReportServerTempDB] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [TfsWorkItemTracking] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [TfsIntegration] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [TfsVersionControl] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [TfsWorkItemTrackingAttachments] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [TfsActivityLogging] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [TfsBuild] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [STS_Config_TFS] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [STS_Content_TFS] SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE [TFSWarehouse] SET RECOVERY SIMPLE WITH NO_WAIT

ALTER DATABASE [ReportServer] SET RECOVERY SIMPLE
ALTER DATABASE [ReportServerTempDB] SET RECOVERY SIMPLE
ALTER DATABASE [TfsWorkItemTracking] SET RECOVERY SIMPLE
ALTER DATABASE [TfsIntegration] SET RECOVERY SIMPLE
ALTER DATABASE [TfsVersionControl] SET RECOVERY SIMPLE
ALTER DATABASE [TfsWorkItemTrackingAttachments] SET RECOVERY SIMPLE
ALTER DATABASE [TfsActivityLogging] SET RECOVERY SIMPLE
ALTER DATABASE [TfsBuild] SET RECOVERY SIMPLE
ALTER DATABASE [STS_Config_TFS] SET RECOVERY SIMPLE
ALTER DATABASE [STS_Content_TFS] SET RECOVERY SIMPLE
ALTER DATABASE [TFSWarehouse] SET RECOVERY SIMPLE 

DBCC SHRINKDATABASE(N'ReportServer')
DBCC SHRINKDATABASE(N'ReportServerTempDB')
DBCC SHRINKDATABASE(N'TfsWorkItemTracking')
DBCC SHRINKDATABASE(N'TfsIntegration')
DBCC SHRINKDATABASE(N'TfsVersionControl')
DBCC SHRINKDATABASE(N'TfsWorkItemTrackingAttachments')
DBCC SHRINKDATABASE(N'TfsActivityLogging')
DBCC SHRINKDATABASE(N'TfsBuild')
DBCC SHRINKDATABASE(N'STS_Config_TFS')
DBCC SHRINKDATABASE(N'STS_Content_TFS')
DBCC SHRINKDATABASE(N'TFSWarehouse')

The above code will actually put all the TFS databases in Simple Recovery mode, which basically means no log file, and then shrinks all the log files that were previously in use. After you run this script in Microsoft SQL Server Management Studio you should not get this error message anymore, when you try to check in your files.

Tags: , ,

Posted in How To, SQL | kick it on DotNetKicks.com | Bookmark | View blog reactions | No Comments »

January 19th, 2009

Creating a Wireless Access Point using DD-WRT - Refresh

I found a setup that I like even better than my previous one.  I have highlighted the main differences, in red, below.

To set it up as repeater do the following:

  • Do a 30 second reset

Under wireless->basic settings:

  • Wireless mode: AP
  • Wireless Network Name (SSID): Your prefer SSID
  • Wireless Channel: Your prefer channel ( Use channel with less interference from other access point)
  • Save Settings

Under Wireless > Wireless Security

  • Set your wireless authentication

Under Setup->Basic Setup:

  • Under WAN Connection Type:
    • Connection Type : Disabled
    • STP : Disable
  • Under Router IP:
    • Local IP address :Set your IP your address to a same subnet of your main router (if your main router IP is 192.168.1.1, set it to same subnet like 192.168.1.2)
    • Subnet Mask : same as your main router subnet
    • Gateaway : Your main router IP
    • DNS : Your main router IP
  • Under Network address Server Setting (DHCP)

    • DHCP Type : DHCP Forwarder
    • DHCP Server : Your main router IP
    • Time settings : disable
  • Save Settings

Under Security Tab

  • Un-check all items in the “Block WAN Request” section
  • Then disable the SPI firewall
  • Save Settings

Under Administration Tab

  • Enter all other necessary information, remember to change your password
  • Click the Save button
  • Then click Reboot button

Tags: , , , , ,

Posted in How To | kick it on DotNetKicks.com | Bookmark | View blog reactions | 4 Comments »

December 28th, 2008

Creating a Wireless Access Point using DD-WRT

This post is a reminder to myself in case I ever need to troubleshoot this setup process again, but I thought I would post it just in case it is useful to somebody else.

A couple days ago I ordered myself a Linksys WRT150N to replace my aging Linksys WRT54G wireless router. I wanted to update my wireless capabilities to the latest 802.11 Draft N standard, so that I could take advantage of the speed boost when working wirelessly.  However I had one problem the WRT150N will not run the Tomato Firmware.  And I didn’t want to switch to DD-WRT for a couple of reasons, the most important being that I really like the Tomato user experience and it would be a pain to setup the router with all my custom configurations again.  So I decided to try and have the best of both worlds and keep my WRT54G (old router running Tomato Firmware) as my gateway and turn the WRT150N (new router running DD-WRT) in to an access point on my network.

Before I started I made sure to disable the Wireless Radio on my WRT54G in preparation for the Wireless Access Point (WAP).  This is important so that the wireless signals and SID (Wireless Name) doesn’t conflict when you are done.

Step 1 - Upgrade to DD-WRT

The first step I had to do was get rid of the crappy Linksys Firmware and upgrade the WRT150N to the latest stable version of DD-WRT.  DD-WRT may not have the user experience that Tomato has, but it is still miles ahead of the Linksys Firmware.  So in the case where I cannot use Tomato, DD-WRT serves as a great alternative to me. The process of installing the DD-WRT firmware is pretty straight forward.  Download the mini generic .bin file and browse to the firmware upgrade page for the router and install.  You can find more indepth information at this blog post.

Step 2 - Turn DHCP Off

I turned off the DHCP on the router for my WAP.

If you do not turn off DHCP, when you plug your router into the network (after configuration), your WAP may provide IP addresses to clients on the wired network, and this may be inappropriate. Tracking down problems caused by multiple DHCP servers can be time-consuming and difficult.

Step 3 - Add A Start Up Script

This part took me the better part of a day to perfect, because like many UNIX commands it is a convoluted syntax of piped commands flowing over each other in order to massage the data for the desired results.

ln -s /sbin/rc /tmp/udhcpc
udhcpc -i br0 -p /var/run/udhcpc.pid -s /tmp/udhcpc -H `nvram get router_name`
if test `ifconfig br0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'` != `nvram get lan_ipaddr`; then
    nvram set lan_ipaddr=`ifconfig br0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`;
    nvram commit;
fi

This startup script runs whenever the router is booted or rebooted, and the purpose of it is to request an IP Address from the DHCP server and set the Local IP Address (or LAN IP Address). There is no option available through the interface for this type of request because normally the LAN IP Address is the routers static local address (most cases 192.168.1.1) that acts as the constant in your network. The 192.168.1.1 acts as the main gateway to the internet, and if that wasn’t static, the LAN traffic wouldn’t be able to get out to the internet. This behavior is, however, undesirable for a Wireless Access Point, the WAP needs to pass through the wireless connections to the real router and needs to act as a client of the LAN instead of the master of the LAN.

Step 4 - Connect LAN to LAN

To complete the link between the two routers, I connected a LAN port on my main router, to a LAN port on the WRT150N (to be used as my WAP).

You may need a crossover cable to do this, although many modern routers have an automatic polarity sensing. To test this, connect a standard Ethernet cable between the two routers. If the LAN light comes on, the router has automatically switched the polarity and a crossover cable is not required.

Step 5 - Power On & Test

The last step required is to power on (or reboot) the router acting as the WAP.  After I powered on the WRT150N I was able to connect to my network through my new WAP.

So that is the process, it took a lot of hunting around on the internet.  But I think I finally got my setup to a state that I can deal with, until Tomato starts supporting the Linksys WRT 150N.

Tags: , , , , ,

Posted in How To, Personal | kick it on DotNetKicks.com | Bookmark | View blog reactions | 6 Comments »

December 9th, 2008

Creating an extension module for .NET URL Rewriter and Reverse Proxy

Wow that is a long title. Recently I have been looking for quick posts that I can put out each day to keep my blog relevant and also so I don’t feel like I am slacking off too much. Today I want to post about a little known feature in my .NET URL Rewriter and Reverse Proxy (aka. Managed Fusion URL Rewriter) that I have developed in my spare time, mostly out of necessity for this blog and other projects I have worked on.  Here is a quick run through of what it does.

Managed Fusion URL Rewriter is a powerful URL manipulation engine based on the Apache mod_rewrite extension. It is designed, from the ground up to bring all the features of Apache mod_rewrite to IIS 6.0 and IIS 7.0. Managed Fusion Url Rewriter works with ASP.NET on Microsoft’s Internet Information Server (IIS) 6.0 and Mono XPS Server and is fully supported, for all languages, in IIS 7.0, including ASP.NET and PHP. Managed Fusion Url Rewriter gives you the freedom to go beyond the standard URL schemes and develop your own scheme.

But one feature that I added that is not part of the official Apache mod_rewrite documentation is the ability to add custom modules to extend the use of the URL rewriter in non-traditional ways.  One great example of this was born out of wanting to clean up the SEO mess I created in the early days of this blog.  I had to support the following different types of URL patterns:

  1. http://www.coderjournal.com/?p=23
  2. http://www.coderjournal.com/2008/03/14/some-post.html
  3. http://www.coderjournal.com/2008/03/14/some-post

to transform them in to the URL pattern that I finally settled on today:

  • http://www.coderjournal.com/2008/03/some-post

In the above list #2 and #3 were pretty easy to transform using the following rules:

RewriteRule ^(/[0-9]{4}/.*).html$    $1/ [NC,R=301]
RewriteRule ^(/[0-9]{4}/[0-9]{1,2}/)[0-9]{1,2}/(.*)$    $1$2 [R=301]

Because they contained all of the elements that make up my current URL.  As you can imagine problems arose when I had to support links that used #1’s syntax.  It contains zero elements that I can use to create my current URL.  Being a programmer who beleives that each part of a system should handle gracefully the domain it was designed to support, in this case a URL rewriter should be able to handle any senario that has to do with URL rewriting.  I added in support that allowed developers to naturally extend the URL rewriter to accomplish any type of URL rewriting task they could think of.

Setting Up the URL Rewriter Rules

In my case I needed to handle the following SQL query everytime I saw a URL that matched #1.

select concat('http://www.coderjournal.com/',year(post_date),'/',month(post_date),'/',post_name,'/') from wp_posts where ID = $1;

What this query does is query the WordPress database table that contains all the posts by the post ID and have it return the actual absolute path to the post, that should be displayed in the URL.  To do this I created a new directive for the mod_rewrite syntax called RewriteModule.  I also had to extend the RewriteRule and RewriteCond directives to support these new module extensions.  The RewriteModule, RewriteRule, and RewriteCond are defined by the following syntax:

RewriteModule <Reference Name> <Namespace>,<Assembly>
RewriteRule[([<Left Module>],[<Right Module>])] <Pattern> <Substitution>
RewriteCond[([<Left Module>],[<Right Module>])] <Test String> <Condition Pattern>

The parts in light blue parts above are optional to creating the rule.  In my case for this blog the rewriter directives looked like the following:

RewriteModule PostQueryString CoderJournal.Rewriter.Rules.PostQueryStringRuleAction, CoderJournal.Rewriter.Rules
RewriteRule(,PostQueryString)   ^/\?p=([0-9]+)$    "select guid from wp_posts where ID = $1;" [R=301]

I have highlighted in red the important parts of the syntax that indicate the custom module processor that should be used on the RewriteRule directive and how it relates back to the class defined in the RewriteModule

Creating the Module

I have to warn you that I am not going to demonstrate and show all the properties and methods on the interface that are important for creating a custom module, but I am going to show you the actual meat of the module that is involved in the lookup of the URL from the database.

public Uri Execute(int logLevel, string logCategory, HttpContext context,
                   Pattern pattern, Uri url, string[] conditionValues,
                   IDictionary<string, string> flags)
{
	string inputUrl = url.GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped);
	string sqlCommand = pattern.Replace(inputUrl, Text, conditionValues);
	string substituedUrl = String.Empty;

	using (MySqlConnection connection = new MySqlConnection(Properties.Settings.Default.DatabaseConnection)) {
		using (MySqlCommand command = connection.CreateCommand()) {
			command.CommandText = sqlCommand;
			command.CommandType = CommandType.Text;

			try {
				connection.Open();
				substituedUrl = command.ExecuteScalar() as string;
			} finally {
				connection.Close();
			}
		}
	}

	return new Uri(url, substituedUrl);
}

It may not be clear right away what is going on, but on line 6, I am replacing the defined value in the regular expression (^/\?p=([0-9]+)$) with the SQL query (from above) to produce a query that will be run against the database. So if the following URL came in to my server:

It would produce a SQL query that looked like this:

select concat('http://www.coderjournal.com/',year(post_date),'/',month(post_date),'/',post_name,'/') from wp_posts where ID = 372;

Notice that the ID, 372, shows up in both the URL and the query, that is because this is the part I am most interested in, in the URL, because it is the only part of the URL that I need to query the database to find the actual path of the post.

Now that we have the query we can execute it on the database, using lines 9 through 21, and create the resulting URL on line 23. The resulting URL is then passed back through the URL rewriter, and processed using the flags defined. In my case [R=301], actually indicates that I want to do a 301 Permanent Redirect on the URL, which tells the browser and search engines, a like, that they need to update their URL for this page.

You can test out the above conditions by using the following URL’s that all redirect back to this page:

  1. http://www.coderjournal.com/?p=372
  2. http://www.coderjournal.com/2008/12/9/creating-extension-module-net-url-rewriter-reverse-proxy.html
  3. http://www.coderjournal.com/2008/12/9/creating-extension-module-net-url-rewriter-reverse-proxy/

The code as always is available on my SVN server at Google Code.

I hope this comes in handy to some of you developers that have to support legacy URL’s in your own product or a project that you are working on. As always if you have any questions or need anything clarified please feel free to contact me or leave a comment below.

Tags: , , , , ,

Posted in C#, How To, SEO, SQL | kick it on DotNetKicks.com | Bookmark | View blog reactions | 3 Comments »

December 7th, 2008

Tweaking Vista Services

Recently I have been trying to squeak more speed out of my laptop by shutting down unnecessary services that seem to come with most every software and device driver.  To do this I started with a clean install of Windows Vista and installed the driver only option for my hardware and then got rid of many of the useless services that come with software like iTunes, VMware, and other stuff I find useful.

However with all that tweaking it still doesn’t create an optimal installation of Windows Vista, because Windows by default still runs a ton of unnessisary services.  For example, Microsoft thought it was nessisary to enabled Tablet PC Pen Input by default which I venture to say is useless to a vast majority of Windows Vista users.

Coincidentally while checking out the new HD feature available on YouTube I stubled across an interesting video describing a website called Speedy Vista.

Windows Vista has around 130 services. According many reports, Vista is very computer intensive, so it may take a couple tweaks to lighten it up a bit to suit your needs. The names are rather vague like ‘ReadyBoost’ and ‘SuperFetch’. How will you know which are safe to disable? Hopefully we can help. We have a full list of all Vista services and recommended settings for them. This site contains registry files for easily resetting your services settings back to factory in case you mess it up or just wish to have a good way to go back to factory.

The website offers a cheat sheet of services you can safely shut off, and if you are unsure what the service does, and don’t want to make a mistake, it has a description of what the services does.  But my favorite part of this website is that it provides a batch file and or a registry file that will shut off the unnessisary services automatically.

Windows Vista ‘Safe’ Settings reg bat - Use at your own risk. I’ve tried to weed out services that are obviously unnecessary to give you a good starting point to tweaking for your own needs/preference. Please e-mail me any problems, etc.

Windows Vista Tweaked Settings reg bat- Use at your own risk. Works for me for internet, Windows Update, DVD playing, most other things I want to do. May cause some software, etc. to stop working, but I would like to hope not. Please send me feedback on this file as well as safe if you have any issues running them. It assumes that you have your own Firewall software and Spyware software.

Windows Vista Minimal Settings reg bat - Use at your own risk. Works for me for internet, Windows Update, DVD playing, most other things I want to do. May cause some software, etc. to stop working, but I would like to hope not. Please send me feedback on this file as well as safe if you have any issues running them. It assumes that you have your own Firewall software and Spyware software.

Disclaimer: Use this site at your own risk. I am not responsible for damage to your computer, or anything else.

If none of these above options work for you, you can even use their wizard to turn on and off the services that you don’t want to use.  This allows you to create your own custom registry file to your own specifications.

I just wanted to post this interesting website since I haven’t posted in the past month, because of my book writing.  Which I am happy to say that I am starting my last chapter and I should be done by the end of the year so I can resume my normal posting schedule.

Tags: , ,

Posted in How To | kick it on DotNetKicks.com | Bookmark | View blog reactions | No Comments »

October 11th, 2008

Add Your Twitter Status To Your Blog

For the longest time I have been wanting to add my Twitter status to my blog in place of my quote right under my blogs name in the header.  (see above)  Today I sat down and figured out what I needed to do to accomplish this and to my surprise it only took all of 10 minutes to complete.

What you need in order to achieve the same for your own blog is:

  1. Managed Fusion Url Rewriter and Reverse Proxy
    This is used to get around cross site scripting blocks that modern browsers impose on JavaScript.  It is used to create a proxy from a local URL to an external URL by fooling the browser in to thinking it is requesting the feed locally.
  2. jQuery
    This is my favorite JavaScript framework, you can use your own, but my examples will be in jQuery.

The first thing you need to do is add a defined spot in your blog where you want the jQuery to write your twitter text status to. I created the following tag in my blog that has an ID of “twitter-status”, the inside of this tag will be replaced with whatever status is downloaded from twitter.

<span><a href="http://www.twitter.com/nberardi">twitter status:</a>&nbsp;<q cite="http://www.twitter.com/nberardi" id="twitter-status">status loading...</q></span>

The second thing you need to do is add a RewriteRule to the rewriter rules for a reverse proxy to the Twitter API. The rewriter rule that I used is:

# get twitter status
RewriteRule ^/twitter-status\.(.*) http://twitter.com/users/show/nberardi.$1 [NC,P]

So if you go to http://www.coderjournal.com/twitter-status.xml, you will get the direct feed that comes from http://twitter.com/users/show/nberardi.xml.

The third and last thing you need to do is add the jQuery JavaScript to the bottom of the page right above the </html>. What this script does is make a GET request to the URL that I defined above and downloads the content as JSON, which creates an object so that it can be used by JavaScript.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script type="text/javascript">
    $.getJSON(
        "/twitter-status.json",
        { },
        // on successful call back
        function (data, textStatus) {
            $("#twitter-status").text(data.status.text);
        }
    );
</script>

So it was really that easy. And it can be done for any feed that you want to pull from anywhere on the internet, it just happened to be that in this case I wanted to grab a feed from twitter.

Tags: , ,

Posted in How To, JavaScript | kick it on DotNetKicks.com | Bookmark | View blog reactions | 6 Comments »

September 24th, 2008

How NOT To Optimize LINQ Statements

About a month ago I was experimenting with different ways to optimize my LINQ queries against the IdeaPipe database, in order to improve the read times. I wanted to improve the read times because our new Facebook Application was being launched and I anticipated an increase in our traffic to the server, which is used to host IdeaPipe and the Facebook Application component.

Whenever I am trying to optimize SQL queries I fire up SQL Server Profiler and take a look at how the queries are performing. This helps me identify queries that are taking a longer time to execute and probably need to be looked at or re-thought. One of the queries that I identified as needing improvement was the following LINQ query:

Old Query

from i in source
join xgl in GroupIdeaLinks on i.IdeaId equals xgl.IdeaId into groupLinksGroup
from gl in groupLinksGroup.DefaultIfEmpty()
let visibility = (gl == null ? 'O' : gl.Group.VisibilityPermission)
let groupId = (gl == null ? -1 : gl.GroupId)
where visibility == 'O' || GroupMembers.Count(m => m.GroupId == groupId && m.IsApproved && m.UserId == userId && (visibility == 'G' || (m.RoleId & (int)Role.Manager) == 0)) == 1
select i;

That produced this monster of a SQL statement:

SELECT [t5].[IdeaId], [t5].[CategoryId], [t5].[UserId], [t5].[IsPopular], [t5].[PopularOn], [t5].[Title], [t5].[SafeDescription], [t5].[Description], [t5].[Path], [t5].[Score], [t5].[Rank], [t5].[ExternalLink], [t5].[VideoLink], [t5].[BumpUpCount], [t5].[BumpDownCount], [t5].[TotalBumpCount], [t5].[TotalCommentCount], [t5].[CreatedOn], [t5].[rowversion]
FROM (
    SELECT [t4].[IdeaId], [t4].[CategoryId], [t4].[UserId], [t4].[IsPopular], [t4].[PopularOn], [t4].[Title], [t4].[SafeDescription], [t4].[Description], [t4].[Path], [t4].[Score], [t4].[Rank], [t4].[ExternalLink], [t4].[VideoLink], [t4].[BumpUpCount], [t4].[BumpDownCount], [t4].[TotalBumpCount], [t4].[TotalCommentCount], [t4].[CreatedOn], [t4].[rowversion], [t4].[value],
        (CASE
            WHEN [t4].[test] IS NULL THEN @p1
            ELSE [t4].[GroupId]
         END) AS [value2]
    FROM (
        SELECT [t0].[IdeaId], [t0].[CategoryId], [t0].[UserId], [t0].[IsPopular], [t0].[PopularOn], [t0].[Title], [t0].[SafeDescription], [t0].[Description], [t0].[Path], [t0].[Score], [t0].[Rank], [t0].[ExternalLink], [t0].[VideoLink], [t0].[BumpUpCount], [t0].[BumpDownCount], [t0].[TotalBumpCount], [t0].[TotalCommentCount], [t0].[CreatedOn], [t0].[rowversion], [t2].[test], [t2].[GroupId],
            (CASE
                WHEN [t2].[test] IS NULL THEN @p0
                ELSE CONVERT(NChar(1),[t3].[VisibilityPermission])
             END) AS [value]
        FROM [Ideas].[Ideas] AS [t0]
        LEFT OUTER JOIN (
            SELECT 1 AS [test], [t1].[GroupId], [t1].[IdeaId]
            FROM [Groups].[GroupIdeaLink] AS [t1]
            ) AS [t2] ON [t0].[IdeaId] = [t2].[IdeaId]
        INNER JOIN [Groups].[Groups] AS [t3] ON [t3].[GroupId] = [t2].[GroupId]
        ) AS [t4]
    ) AS [t5]
WHERE (UNICODE([t5].[value]) = @p2) OR (((
    SELECT COUNT(*)
    FROM (
        SELECT
            (CASE
                WHEN ([t6].[GroupId] = [t5].[value2]) AND ([t6].[IsApproved] = 1) AND (([t6].[UserId]) = @p3) AND ((UNICODE([t5].[value]) = @p4) OR (([t6].[RoleId] & @p5) = @p6)) THEN 1
                WHEN NOT (([t6].[GroupId] = [t5].[value2]) AND ([t6].[IsApproved] = 1) AND (([t6].[UserId]) = @p3) AND ((UNICODE([t5].[value]) = @p4) OR (([t6].[RoleId] & @p5) = @p6))) THEN 0
                ELSE NULL
             END) AS [value]
        FROM [Groups].[GroupMembers] AS [t6]
        ) AS [t7]
    WHERE [t7].[value] = 1
    )) = @p7)

So I started playing around with the LINQ statement until I reduced the size of my SQL query significantly. The following is the result of that optimization:

New Query

var groupMembership = (from gm in GroupMembers
                       let visibility = gm.Group.VisibilityPermission
                       where visibility == 'O' || (gm.IsApproved && gm.UserId == userId && (visibility == 'G' || (gm.RoleId & (int)Role.Manager) == 0))
                       select gm.GroupId).Distinct();

from i in Ideas
join xgl in GroupIdeaLinks on i.IdeaId equals xgl.IdeaId into groupLinksGroup
from gl in groupLinksGroup.DefaultIfEmpty()
where gl == null || groupMembership.Contains(gl.GroupId)
select i;

Which outputs the following SQL query:

SELECT [t0].[IdeaId], [t0].[CategoryId], [t0].[UserId], [t0].[IsPopular], [t0].[PopularOn], [t0].[Title], [t0].[SafeDescription], [t0].[Description], [t0].[Path], [t0].[Score], [t0].[Rank], [t0].[ExternalLink], [t0].[VideoLink], [t0].[BumpUpCount], [t0].[BumpDownCount], [t0].[TotalBumpCount], [t0].[TotalCommentCount], [t0].[CreatedOn], [t0].[rowversion]
FROM [Ideas].[Ideas] AS [t0]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[GroupId], [t1].[IdeaId]
    FROM [Groups].[GroupIdeaLink] AS [t1]
    ) AS [t2] ON [t0].[IdeaId] = [t2].[IdeaId]
WHERE ([t2].[test] IS NULL) OR (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM (
        SELECT DISTINCT [t3].[GroupId]
        FROM [Groups].[GroupMembers] AS [t3]
        INNER JOIN [Groups].[Groups] AS [t4] ON [t4].[GroupId] = [t3].[GroupId]
        WHERE (UNICODE([t4].[VisibilityPermission]) = @p0) OR (([t3].[IsApproved] = 1) AND (([t3].[UserId]) = @p1) AND ((UNICODE([t4].[VisibilityPermission]) = @p2) OR (([t3].[RoleId] & @p3) = @p4)))
        ) AS [t5]
    WHERE [t5].[GroupId] = [t2].[GroupId]
    ))

As you can tell the second query is much more compact and it does the exact same thing as the first query. I was pretty proud of my self and riding high on my genius, until this happened:

Well this didn’t really happen, but you get the point. I quickly came down from my high when I tested the performance of the new query in SQL Server Profiler, and received these results from the two queries:

  • OLD QUERY (Reads 130, Durration 5)
  • NEW QUERY (Reads 218, Durration 28)

That is right my optimization increased the number of times SQL has to read the table by 68% and time it takes to execute by 460%. So I reversed all my changes and learned a lesson on how not to optimize a LINQ statement.

The moral of the story is you probably don’t need to optimize your SQL query through LINQ, just keep it simple and optimize your LINQ statement and leave the rest up to the professionals at Microsoft who created the LINQ to SQL expression query generator.

Tags: , , ,

Posted in C#, How To, SQL | kick it on DotNetKicks.com | Bookmark | View blog reactions | 7 Comments »

August 25th, 2008

Deadlocked!: “read committed snapshot” Explained

I just recently read Jeff Atwood’s Deadlocked! article. I just wanted to give some more insight in to the read committed snapshot so that it is not perceived as “magic”. It has some definite advantages when dealing with deadlocks, however if your code relies on row level locking you are not going to be able to use this type of reading in SQL Server.

First lets talk about how you enable it. It is not a transactional isolation level, so if you set it, it will effect your whole database. You have been warned!

alter database [YourDatebaseHere]
set read_committed_snapshot on
go

Basically what this does is create a snapshot or read-only database of your current results that is separate from your live database. So when you run a SELECT statement, to read your data, you are reading from a read-only copy of your database. When you change your database, it happens on the live database, and then a new copy or snapshot is created for reading against.

Personally I am using it on IdeaPipe, because like most Web 2.0 applications there are a heavy amount of reads and very few updates that effect the row. So chances are if you have a website this will decrease your number of deadlocks. But make sure to test thoroughly before implementing read committed snapshot.

When I was doing my initial research a while ago I found this article talking about how snapshot isolation can bite you where it hurts.

For example, suppose READ COMMITTED SNAPSHOT is not enabled in the database and you want to assign one more ticket to a person, but only if that user does not already have high priority tickets:

BEGIN TRANSACTION
UPDATE Tickets SET AssignedTo = 6 WHERE TicketId = 1
AND NOT EXISTS(SELECT 1 FROM Tickets WHERE AssignedTo = 6 AND Priority='High')
--- do not commit yet

Note that you have not explicitly specified an isolation level, so your transaction runs under the default READ COMMITTED level. If another connection issues a similar update:

UPDATE Tickets SET AssignedTo = 6 WHERE TicketId = 2
AND NOT EXISTS(SELECT 1 FROM Tickets WHERE AssignedTo = 6 AND Priority='High')

it will hang in a lock waiting state. Once you commit your first transaction the second one will complete, but it will not assign ticket 2 to user 6, which is the correct behavior as designed.

However if read committed snapshot is enabled on the database the user will end up with two high priority tickets, because the first read happens against a snapshot and the update happens against the live database. So this will obviously cause problems for specifications and business rules that rely on row level locking. So be careful, and make sure you specifically know what is happening with your code before turning this on

Note: Chances are if you are using LINQ you don’t have to worry about the above scenario, however I am not a DBA expert, only a student of the practice. So take what I say with a grain of salt.

Tags: , , ,

Posted in How To | kick it on DotNetKicks.com | Bookmark | View blog reactions | 1 Comment »