Jeff and .NET

The .NET musings of Jeff Putz

Sponsors

News

My Sites

Archives

August 2004 - Posts

SQL paging advice needed

OK, so saying that SQL Server isn't really my thing is kind of lame, but I'm not one to pretend I know everything. Generally I use a procedure like this to get paged data (parameters replaced with actual values for simplicity).

CREATE TABLE #PageIndex
(
 idnum int IDENTITY(1, 1),
 TopicID int
)

INSERT INTO #PageIndex (TopicID) SELECT TopicID FROM Topics
WHERE Topics.ForumID = 11
ORDER BY Topics.LastPostTime DESC

SELECT #PageIndex.idnum, Topics.* FROM #PageIndex JOIN Topics
ON #PageIndex.TopicID = Topics.TopicID
WHERE #PageIndex.idnum > 5000 AND #PageIndex.idnum < 5031
ORDER BY #PageIndex.idnum

This returns results quickly enough, but it seems to result in a bit of a CPU hit that I'm not entirely comfortable with. Is there a better way to go about this?

Posted: Aug 22 2004, 09:08 PM by Jeff | with 7 comment(s)
Filed under: ,
Jeers for LoginView control

Awhile back I was crying about how LoginView is designed wrong. Apparently Microsoft doesn't agree with me. At issue is the fact that controls inside of a LoginView are created late in the page lifecycle so they can't be accessed directly from PageLoad or postback events. The stated purpose of the control, according to the documentation, says: "Displays the appropriate content template for a given user, based on the user's authentication status and role membership."

I don't know if that sentence represents the broad requirements of the control, but I suppose it depends on how you define "content." The first thing that comes to mind when wanting to use this control is putting a login box in the anon template and some kind of navigation for logged in users in the authenticated template. Or in a feedback mechanism under an article, put the text box for logged in folks and a link to register for the anon folks. As it stands now, the static content is fine, but anything else requires more work.

This is eventually what the Web platform team came up with:


We have identified improving the ease of use of "FindControl" as an issue for a future version of the product. In Whidbey we did introduce a control attribute that allows you to designate controls to be "single instance" templates -- in effect setting this promotes the first level of child controls to the page and they are easier to access (e.g. the behavior you see WebPartZones). However there are trade-offs in this design that make it impractical to do for LoginView, one of the trade-offs is the change in naming container behavior that can cause problems with other login controls when they are templated. We initially did have a LoginView as a single instance template but reverted that change based on feedback from early adopters and internal users of the control.

So I guess it won't work the way I think it should, and that's a bummer. The next best thing is to use a MultiView control, or a derived version thereof, to duplicate this functionality.

Posted: Aug 22 2004, 03:24 PM by Jeff | with no comments
Filed under:
Why electronic banking is better

Last month I went inside of my bank to make a deposit and get some cash, somehing I almost never do because I use the ATM. That particular day, the ATM was broken, so I had little choice.

So wouldn't you know it, the one time I go in during a span of like three years, the teller manages to debit my savings account $200 on behalf of the next customer. How stupid is that? The one time I deal with humans, they screw it up?

Posted: Aug 22 2004, 10:24 AM by Jeff | with no comments
Filed under:
.NET seems to make everything easier

Back when I used Overture, I always thought it was kind of a cool security feature where you had to type in letters in a graphic that is, theoretically, not machine readable. This deters automated attacks. It occurred to me that'd be a nice feature for the forum registration.

I never really played with anything graphical in .NET, aside from reading a little of the Managed DirectX 9 Kick Start. I also did some image resizing on the crappy little game site I did. From what little I knew about stuff in the Drawing namespace, I figured it couldn't be that hard to generate a GIF out of an HttpHandler with some text on it.

I was right. It's like a dozen lines of code, and that includes code to draw some random lines on it and move the characters around. It generates the text and saves it to Session.

Once you can find a good example, or encounter some of the better documentation, it's like you can do anything on .NET. What's even more exciting is that once you learn one area, you adapt quickly to other areas. I'm an ASP.NET guy, but Windows forms are easy enough. Graphics aren't that hard, and after reading a little of a beginning game programming book, that's not hard either.

I'm generally too busy creating things to get as academic as a lot of bloggers do, but it's fun to see that new stuff can come so easily in .NET.

Posted: Aug 22 2004, 10:21 AM by Jeff | with 4 comment(s)
Filed under: ,
More adventures in Web app threading

Responses in my last post about this were either, "Threading in a Web app isn't a good idea," or a lengthy example that works, but wasn't really what I was after. After looking a little harder, I realized the error of my ways. The goal was to launch a Timer from an HttpModule. This is where I started:

public void Init(HttpApplication application)
{
 myTimer = new Timer(new TimerCallback(this.TimerMethod), application.Context, 60000, 60000);
}

static Timer myTimer;

private void TimerMethod(object sender)
{
 HttpContext context = ((Application)sender).Context;
 MyClass.StaticMethod(context);
}

Then in the class being called by the timer, something like this:

public static void StaticMethod(HttpContext context)
{
   Cache cache = context.Cache;
}

The problem there was that the cache object was always null. So at some point someone on the asp.net forums suggested this to me:

public void Init(HttpApplication application)
{
 appHandle = GCHandle.Alloc(application.Context, GCHandleType.Normal);
 myTimer = new Timer(new TimerCallback(this.TimerMethod), application.Context, 60000, 60000);
}

static Timer myTimer;
protected GCHandle appHandle;

private void TimerMethod(object sender)
{
 HttpContext context = (HttpContext)appHandle.Target;
 MyClass.StaticMethod(context);
}

This did indeed work. I wasn't sure why, but I did get that it prevented the GC from disposing of the HttpContext object, which otherwise would die. After some toying around, mostly through chance Intellisense encounters, I arrived at this, which works as it should, without the GCHandler:

private void TimerMethod(object sender)
{
 HttpContext context = (HttpContext)sender;
 MyClass.StaticMethod(context);
}

...with the static method establishing the cache this way:

Cache cache = HttpRuntime.Cache;

What do you know, it works. Watching the output in the debugger, it keeps doing its thing, on and on and on. I'm not sure if the context object in the static method can access the Application object, but seeing as how I have the cache, that's more than adequate.

Posted: Aug 20 2004, 02:22 PM by Jeff | with 5 comment(s)
Filed under:
Why is threading so much work in a Web app?

Doing stuff in seperate threads in a Web app is a pain when you need a reference to general application state. Why does it have to be so hard?

The first time I did something in this realm, I was trying to make an asynchronous mailer to notify people in a forum thread that there was a reply to the thread. With some help from someone in the ASP.NET forums, I eventually arrived at something like this in the class (the GCHandle class is in System.Runtime.InteropServices, if you're curious):

public void Send()
{
 appHandle = GCHandle.Alloc(HttpContext.Current, GCHandleType.Normal);
 // notify by email those who are hooked up
 ThreadStart workerstart = new ThreadStart(Mailer);
 Thread myThread = new Thread(workerstart);
 myThread.Start();
}

protected GCHandle appHandle;

private void Mailer()
{
 HttpContext context = (HttpContext)appHandle.Target;
 // mail work here
}

This works perfectly, though I'm still not clever enough to understand exactly why. Adapting this same logic to a static Timer object in an HttpModule wasn't much harder, provided I kept passing along the context through whatever objects and code I wanted to run:

public void Init(HttpApplication application)
{
 appHandle = GCHandle.Alloc(application.Context, GCHandleType.Normal);
 myTimer = new Timer(new TimerCallback(this.TimerMethod), application.Context, 60000, 60000);
}

static Timer myTimer;
protected GCHandle appHandle;

private void TimerMethod(object sender)
{
 HttpContext context = (HttpContext)appHandle.Target;
 // do whatever
}

So now that I'm clever enough to follow this pattern, can anyone explain why exactly it works? I'm not satisfied without knowing the "why" along with the "how."

Posted: Aug 19 2004, 11:01 PM by Jeff | with 10 comment(s)
Filed under:
XP SP2 issues

I'm sure that 90% of people running SP2 will not have any problems, but we've already had a lot of issues.

First is the default IE security. My wife decided to upload photos to Ofoto on her machine (usually it's on mine), and for whatever reason, the browser would not allow her to install the ActiveX control under any circumstances. It didn't even prompt for it. She changed the settings to at least allow for the prompt. That's going to piss-off a lot of site publishers I'm sure.

She's also a big City of Heroes player, and since installing SP2, the game slowed to an unplayable state when things got busy. It wasn't the video driver (had the latest from Nvidia). We don't know if it was something else in the graphics or motherboard pipelines, or a network issue, but it sucked to epic proportions.

While checking to see what the pagefile settings were (which seem to be arbitrarily set to follow a range of a half-gig to a gig, same settings on my machine), I noticed the "Data Execution Prevention" tab. Never heard anything about that, but it's on, and you can't turn it off without modifying the boot.ini file (which also happens to be read-only). The theory is to apparently stop the execution of any program that tries to write to memory it shouldn't. I assume this is a catch-all for buffer overrun vulnerabilities. It's just weird that I've never heard anything about it. Regardless, turning it off had no effect, but I'm curious to know how it affects performance.

We ended up uninstalling SP2 on her machine. City of Heroes runs great again. They're going to be in a world of hurt as it rolls out.

I still have the issue with Avid Xpress. Avid, in a shocking move given their reluctance to do anything to maintain compatibility (they wouldn't support XP for ages), posted in their forum that they're working on "the few remaining issues."

Posted: Aug 19 2004, 08:04 PM by Jeff | with 10 comment(s)
Filed under:
Nested grid woes

I've done nested grids before, something like this:

<asp:Repeater ID="CatRepeater" Runat="Server">
 <ItemTemplate>
  <p><%# Eval("Name") %></p>
  <asp:GridView ID="ForumGrid" Runat="Server" AutoGenerateColumns="true" AllowPaging="false"
  DataSource='<%# new Category(Convert.ToInt32(Eval("CategoryID"))).GetForumCollection(false, false) %>'>
  </asp:GridView>
 </ItemTemplate>
</asp:Repeater>

I tried to do it in a more event driven manner today like this:

<script runat="server">
 void Page_Load(object sender, EventArgs e)
 {
  CategoryCollection c = Category.GetCategories(false);
  c.Insert(0, new Category(0));
  CatRepeater.DataSource = c;
  CatRepeater.DataBind();
 }

 void CatRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
 {
  GridView g = (GridView)e.Item.FindControl("ForumGrid");
  ForumCollection f = ((Category)e.Item.DataItem).GetForumCollection(false, false);
  g.DataSource = f;
  g.DataBind();
  Trace.Warn(g.Rows.Count.ToString());
  Trace.Warn(f.Count.ToString());
 }
</script>
...
<asp:Repeater ID="CatRepeater" Runat="Server" OnItemDataBound="CatRepeater_ItemDataBound">
 <ItemTemplate>
  <p><%# Eval("Name") %></p>
  <asp:GridView ID="ForumGrid" Runat="Server" AutoGenerateColumns="true" AllowPaging="false">
  </asp:GridView>
 </ItemTemplate>
</asp:Repeater>

Here's what I don't get. In the two traces of the binding handler, the rows in the grid return 0 after databinding, yet the ForumCollection does indeed show the actual count. Am I missing something?

This is actually going to change later on, because I don't want to be making multiple hits to the data every time, but I'm experimenting with something else.

Posted: Aug 18 2004, 12:30 AM by Jeff | with no comments
Filed under: ,
NBCOlympics.com sucks

[Note: If you check the date on this post, it's from 2004. The 2008 site is much, much better. -J]

It's absolutely astounding to me that important commercial sites can go into production without any usability testing. NBCOlympics.com comes to mind.

I'm not that much of a sports fan, but seeing as how I coach junior Olympic volleyball in the ridiculously competitive Ohio Valley Region, and 17's at that, I can't get enough of the stuff during the Olympics. It's also really fun to watch Misty May and Kerri Walsh on the beach. There's one problem though: NBC's online schedule absolutely sucks.

If you search TV listings for a time span and by sport, you get a result list of links and vague summaries. If you click on these links, you get a pop-up with a little more detail, and you're still stuck with a vague four-hour time block. What's worse is that there is no grid that shows if something is on one of the other channels at the same time.

What the hell would've been so difficult about either:

  • A grid with channels across the top and times down the side, filled in with the sport you choose?
  • Or a grid with channels across the top and days down the side, with each entry including times for the sport that day?

Oh, it's also slow as hell.

If you work independently, I'm sure you feel as strongly as I do that with all of the mediocre crap out there, it's a crime that someone out there gets paid for something you could probably do ten times better and for less money.

Posted: Aug 17 2004, 09:47 PM by Jeff | with 9 comment(s)
Filed under:
Not every website is Match.com

In my post yesterday there were some interesting comments about the code sample I posted regarding the use of StringBuilder vs. straight concatenation. Yes, I created a ridiculous scenario that would probably not ever occur in real life, but the intention was to illustrate a point.

David Hayden made the comment that, "Not every website is Match.com." I totally agree with that, and I especially take issue with the thing about stored procedures always being "right." The code sample is for my book, and it comes with a liberal disclaimer that such a situation is fairly extreme. (And oh yeah, I argue against the use of sprocs too as the "right" thing.)

That's one of the hardest things to confront when you're trying to teach. Do you go with the ultimate best practice when there's an easier way that doesn't perform as well but is still adequate? I've never used any kind of drag-and-drop data access because I fear for its performance, but clearly it's adequate for someone out there or Microsoft wouldn't keep including it.

Posted: Aug 16 2004, 03:31 PM by Jeff | with 10 comment(s)
Filed under:
More Posts « Previous page - Next page »