March 2007 - Posts
I saw a cool post recently from Jon Galloway called "Passing lists to SQL Server 2005 with XML Parameters". This is a pattern I've used several times while building the new version of Channel 9. If you'd like to learn how to pass in lists to stored procedures, check out Jon's post.
One of the times I've used it is to search our database for all entries from two of our forums. For this example, we'll say Techoff and Sandbox. Once you have a temp table with the two forum ids (actually forums in our system are just tags too) you can just do a where in statement like the following:
SELECT e.* FROM Entry e INNER JOIN EntryForum ef ON e.EntryID = ef.EntryID WHERE ef.ForumID IN (SELECT ForumID FROM ForumList)
Note: This is all pseudo code to represent the basics of how we do this. This is not the exact code.
This selects all the entries (or posts) from our database that are from the list of forums I passed into the ForumList temp table. WHERE IN specificies that all rows be returned that match ANY of the records in my temp table. The following statement would be equivalent and work exactly the same.
SELECT e.* FROM Entry e INNER JOIN EntryForum ef ON e.EntryID = ef.EntryID WHERE ef.ForumID = @ForumID1 OR ef.ForumID = @ForumID2
Note: In the above example, @ForumID1 and @ForumID2 have the values that were stored in the ForumList temp table in the example above that one.
This works pretty well. The other thing we do with passing in lists though is selecting only the entries that match ALL (not ANY) of the list we pass in to the stored procedure. The example of this is when searching by multiple tags. So for instance, you want to search on our site for all content that contains information on WPF AND WCF. The previous example won't work. It would instead need to be something like this...
SELECT e.* FROM Entry e INNER JOIN EntryTag et ON e.EntryID = et.EntryID WHERE et.TagID = @TagIDWPF AND et.TagID = @TagIDWCF
Using WHERE IN, we can't do this (at least I couldn't find anything in the docs or internet searching to say otherwise). Duncan helped figure out the idea on how to do this and here is the implentation I came up with:
DECLARE @TagCount int
DECLARE @Tags TABLE (TagID bigint)
DECLARE @Entries TABLE (EntryID bigint)
SELECT @TagCount = COUNT(*) FROM @Tags
;
WITH Entries(EntryID, MatchCount) AS
(
SELECT
e.EntryID,
COUNT(DISTINCT t.TagID) AS MatchCount
FROM
Entry e
INNER JOIN
EntryTag et
ON
e.EntryID = et.EntryID
INNER JOIN
@Tags t
ON
et.TagID = t.TagID
GROUP BY
e.EntryID
)
INSERT INTO @Entries (EntryID) SELECT EntryID FROM Entries WHERE MatchCount = @TagCount
What is this code doing? Well, first, it's doing a count on the tags that were passed in (again, from XML turned into a temp table) and storing it in a variable. Then, it creates a Common Table Expression or CTE around a query that returns all the entries that match the tag list and how many of those tags it matches up with. If you're not familiar with CTEs, they're basically a wrapper around a query so you can write a query against it. Kind of like a subquery, but much more organized. Recursive CTEs are particularly powerful and cool, but that's another blog post. So then after creating the CTE, fill another temp table with everything from the CTE where the MatchCount equals the count of how many tags were passed in originally. This means that the entry returned had ALL the tags passed in associated with it. So this will now only return entries that match ALL of the tags from the list that I passed in (stored in @Tags). I hope this helps someone. :)
UPDATE: Check out the first comment from Bryan. He points out a slightly better implementation. Thanks, Bryan!
I'm a Slashdot browser. I've never commented on anything thing and never will. Slashdot is 90% crap (mostly because of the comments left there). That's my opinion and I'm sticking to it. That said, it's good to watch what goes on there, because every once and a while there is a gem of great insight.
This isn't the kind of gem I'm referring to, but it is still a gem and TOO GOOD and TOO TRUE to pass up. My buddy Tobin pointed me to this one...
http://apple.slashdot.org/comments.pl?sid=44091&cid=4592270
I'm not a big fan of bashing people...unless they deserve it. :)
Rory posted a great first video taking a look at a couple new apps out of MSR.
There's going to be lots more good stuff coming soon from TechFest. To follow the happenings on Channel 9 in regards to TechFest, check out the TechFest tag.
Btw, this has to be the best preview image on any of our videos ever! ;)

This is really great. The way they went about making the sounds for Windows Vista is really cool. There are some really great ideas in this video like having calm music (you have to listen to the video to hear them) in the background while installing Windows Vista. It's sad that it didn't make it into the final product. Maybe they'll come out with some music from this we can buy. Check it out...
Making Windows Vista Sing: Robert Fripp and the Vista Melody
I just got a comment from an old post from over 2 years ago about some custom auth problems I had. It's funny looking back that far to see how you were. As with all typical developers, I look back and laugh at myself. :)
The question posted was if I could share my custom authentication code. The fact of the matter is, the way I was doing authentication at the time was kind of silly and I thought I'd post how we do it now. It's much simpler and uses everything that's already built into ASP.NET 2.0 already. When a user logs into your site (using whatever type of authentication you want), the Context.User (same User object that shows up on the Page class, etc) is set to an IPrincipal. Depending on what "username" you passed in for it to tack on to the cookie, you'll be ablel to access a key for looking up more detailsl about the user. Then the same applies from the rest of my 2 year old post. Create a BasePage class that inherits from Page and shadow the User property with your own. Here's the code from our platform in the BasePage class.
public bool IsAuthenticated
{
get {
return Request.IsAuthenticated; }
}
private string username;
public string Username
{
get{
if (username ==
null)
{
if (IsAuthenticated)
username =
base.User.Identity.Name;
elseusername =
"";
}
return username;
}
}
private bool isUserSet;
private EvNetUser user;
public new EvNetUser User
{
get{
if (!isUserSet)
{
if (IsAuthenticated)
user =
Users.Retrieve(Username);
isUserSet =
true;
}
return user;
}
}
Now anywhere in our site we can say Page.User and get back an object filled with everything we need to know about the current user. If the request is anonymous, Page.User will return null. Hope this helps !
This is really great to see!
http://blogs.msdn.com/webdevtools/archive/2007/03/02/jscript-intellisense-in-orcas.aspx
I've been developing a lot of client controls in ASP.NET AJAX lately and this would be so helpful.
I'm writing up a post about creating custom client controls at the moment (well, as of a few minutes ago anyway). It is getting pretty long and it's way past my bedtime. So I thought in the meantime I'd post a quick little blurb about how you can use JavaScript from an assembly for your ASP.NET AJAX code and why you would want to.
It's pretty easy to setup a .js file in your web project with some code in it. Sometimes though the code in said file is associated with say a custom client control (something that inherits from Sys.UI.Control) that is in its own assembly (not your web project). This is how our controls are setup for our platform. This is so we can use the same controls across Channel 9, Channel 10, etc. The problem is now that we have code in js files, they have to be replicated across all our web projects and that's just no fun. So instead we moved to having the js files embedded in our main class library. The server controls register these files and they are then pulled out of the assembly and sent down to the client and cached. Here's how to set this up yourself...
Add the following line as the last line in your js file:
if (Sys != undefined) Sys.Application.notifyScriptLoaded();
This tells ASP.NET AJAX that the file is done loading. This is needed because all embedded js files stream down in the same "file" so the end of the file isn't necessarily the end of what's streamed down to the client. Unlike when you just like to a js file regularly.
Now, in Visual Studio go to the properties window while your js file is selected. Change the Build Action to Embedded Resource. This will compile the file into the assembly as a resource. If you open up reflector and venture through, you'll find the js file. Now in your server control, add this line so ASP.NET knows about the resource (and what mime type to send it down as):
[assembly: WebResource("EvNet.Web.Templates.Scripts.Toolbar.js", "text/javascript")]
Now anytime you add a ScriptReference to a ScriptManager, your file will be streamed down to the client. Just specify the resource name (This is the physical file path down to the file starting at the root of your class library with slashes replaced by periods [see below]) and the assembly the resource is in and you're done. No need to worry about where the file is anymore. :) This of course works when implementing IScriptControl.GetScriptReferences in your server control too:
public IEnumerable<ScriptReference> GetScriptReferences()
{
return new ScriptReference[] { new ScriptReference("EvNet.Web.Templates.Scripts.Toolbar.js", "EvNet") };
}
Enjoy!
More Posts