Rants Tagged with “ASP.NET”

1  2  3  4  >  >>  (Total Pages: 4/Total Results: 39)

Wildermuth.com By Example - Part 1

Silverlight Logo

In response to some requests that I have received, I decided to write a several part blog on some of the techniques I used in developing Wildermuth.com. In this first example, I am going to discuss the use of LINQ and data in my site. 

In moving from www.adoguy.com to www.wildermuth.com, one of my goals was to use LINQ as much as possible to see the travails of using it on a real project. I have done a lot of small samples with LINQ but did it hold up for real work?  Suffice to say I am pretty impressed (though whether a blog is 'real work' is up for discussion, but its a better exercise of the technology than my samples had been).

When I say LINQ, I want to be specific.  LINQ to me really is "Language Integrated Query".  The data store behind is a secondary discussion and whether the Entity Framework, LLBLGen Pro, or nHibernate, I really wanted to make sure that the way I queried data in the C# code was LINQ. I was hoping to avoid dropping down into SQL as much as possible.

I knew I wanted to be able to use my model from most pages so I added the data context to the Master page so it would handle most of the build-up, tear-down for me:

public WilderEntities Ctx
{
  get
  {
    if (_ctx == null)
    {
      _ctx = new WilderEntities();
    }

    return _ctx;
  }
}

protected override void OnUnload(EventArgs e)
{
  if (_ctx != null)
  {
    Ctx.Dispose();
  }
}

This allowed me to create the Ctx on the master page but use it on any page/control that I neeeded.  The disposal of the context would happen during unloading of the the page.  The only thing that this required is that most pages needed a typed reference to the master page which you can do with a MasterType page directive:

<%@ Page Language="C#" MasterPageFile="~/StwMaster.Master" 
    AutoEventWireup="true" CodeBehind="default.aspx.cs"
    Inherits="stw._default" Title="Shawn Wildermuth's Blog" %>

<%@ MasterType VirtualPath="~/StwMaster.Master" %>

Much of the LINQ code is fairly pedestrian:

var qry = from b in MasterPage.Ctx.BlogEntry.Include("BlogEntryComments")
          where b.Published == true
          orderby b.DatePosted descending
          select b;

List<BlogEntry> rants = qry.Take(10).ToList();

 

You can see that I am doing a simple LINQ query to get all the published BlogEntry objects and order them by the date they were posted.  Of note, when I execute the query I am adding the Take() method to limit the results.  This is akin to TOP in SQL and we'll be revisiting it in a minute.

Because I need to have access to comments about the blog entries, I use the Include clause in the LINQ query to retrieve not only the BlogEntry objects, but also the related BlogEntryComment objects. This syntax is specific to the EntityFramework. If you are using a different LINQ provider, you may find that lazy loading is automatic (e.g. nHibernate) or not available.  I know this is a chief complaint about the Entity Framework, but I like it as it makes the developer have to think about loading sub-types and the side effects...but that's a whole other discussion.

Once we have a result its a simple as assigning our collection and forcing data binding to happen:

theRants.Blogs = rants;
titles.Blogs = rants;
DataBind();

 

Of particular interest here is that I am using the same list to bind to two collections (the list of blogs and the "On This Page" titles).   Because we are getting simple CLR object collection back, there isn't any magic here in how we're doing the binding.  This is in stark contrast to older data access (e.g. DataRows, DataReaders).

One thing I really like here is that I can do the paging directly during the execution of the LINQ query as seen in the LINQ query for the paged Rants page:

var qry = from b in Master.Ctx.BlogEntry.Include("BlogEntryComments")
          where b.Published == true 
          orderby b.DatePosted descending
          select b;

List<BlogEntry> rants = qry.Skip(PAGESIZE * (currentPage - 1)).Take(PAGESIZE).ToList();

 

Note that this LINQ query is identical to the earlier home page query but I am doing the paging by calculating both the Skip() and Take() value.  Like I mentioned earlier, Take() is like TOP in that it specifies the number of returned elements.  Whereas Skip() specifies how many results to ignore before starting the Take() amount. This allows us to manage paging directly using the LINQ code.

There was one place where I just couldn't get LINQ to bend to my wishes.  I am still not convinced that there is *not* a way to do this, but I dove down into Entity SQL to make the request instead. The case was where I am translating the URI pieces of the Rant URI (e.g. http://wildermuth.com/2008/07/07/Wildermuth_com_By_Example_-_Part_1) where I am being handed the year, month, day and title by the routing framework (I'll talk about that in a future post).  I needed a way of finding the right Rant based on that information (since I don't want to share the Rant ID with anyone). To do this, I used an ObjectQuery (Entity Framework's query syntax using Entity SQL):

string whereClause = @"SqlServer.DATEPART('yyyy', it.DatePosted) = @year AND 
                       SqlServer.DATEPART('mm', it.DatePosted) = @month AND 
                       SqlServer.DATEPART('dd', it.DatePosted) = @day AND 
                       it.Title LIKE @pattern";
ObjectQuery<BlogEntry> qry = Master.Ctx.BlogEntry
                                  .Include("BlogEntryComments")
                                  .Where(whereClause);

qry.Parameters.Add(new ObjectParameter("year", rantDate.Year));
qry.Parameters.Add(new ObjectParameter("month", rantDate.Month));
qry.Parameters.Add(new ObjectParameter("day", rantDate.Day));
qry.Parameters.Add(new ObjectParameter("pattern", pattern));

_blogEntry = qry.FirstOrDefault();

 

Note I am using the DATEPART syntax to compare the parts of the date.  The real issue with the query was the parameter as I take the title and replace all underscores with % to do a LIKE query.  This worked fine and once the query executes, I am dealing with a simple CLR object so it doesn't matter in the bit picture.

Other than that, the LINQ syntax is pretty straightforward in my example.  I ported old old nasty code from my original sites (in fact some routines were originally ported from ASP to ASP.NET back in pre v1.0).  Because of that I don't have a good separation of responsibilities, but that's for the next conversion...when MVC comes of age.

My last note I wanted to say is that I attempt to create pretty simple HTML. I don't use many actual controls except the repeater and create mostly CSS-based XHTML to be simple. With that in mind I enchew ASP.NET's DataSource stuff.  Whether its LINQ, Object or Sql DataSources they all are trying to do a lot of magic IMHO and I end up writing code instead of depending on them.

Opinions and observations are welcome!

Links and New Site Technology

Silverlight Logo

Now that the change to Wildermuth.com is complete I've gotten questions about broken links and such. I am keeping adoguy.com around and redirecting (permanent) the links so that old links aren't going to break. I don't plan on keeping it forever but for several years you can be sure. Its worth keeping them around. 

I didn't just do a quick and dirty port to a new CSS look and feel though. This was a conversion of the old code. I don't use a blog engine but write my own code, mostly as a test bed for new ideas. What was it this time?  Two thinks significant changed: Entity Framework/LINQ as the data access and the ASP.NET Routing Framework instead of .aspx links. Let's take these one at a time:

The use of the Entity Framework and LINQ was pretty straightforward. I used LLBLGen Pro in my old site to do the data access and it held up great.  I only switched so I could test out the Entity Framework on a production-ish system. Not because of any limitations in the old code. Creating a model with the Entity Framework was a snap. My data is not complicated so I didn't have any complex scenarios.  The only think I did do was change the collection names from singular to plural. Being able to use LINQ to do my queries, search and paging was just spectacularly useful. I am completely in the LINQ camp now.

The second testbed was the new ASP.NET Routing Framework .  What is the ASP.NET Routing Framework you say? Many of you have heard of ASP.NET MVC. The ASP.NET Routing Framework is the code that does all the custom routing for MVC.  The ASP.NET Routing Framework is going to be released with .NET 3.5 SP1, not part of MVC so its not expected to change nearly as much as I expect MVC to mature. In that light, I couldn't port my site to MVC wholly because of the limitations in ViewState and ControlState. I could have worked around these limitations but I wanted to be able to focus on the new pieces and not have to re-write every page of the application (or use client-side libraries).  So I found the middle ground of the ASP.NET Routing Framework.  I am using Phil Haack'd great WebFormRouting class to do much of the heavy work of not only routing to the pages but sending the same sort of context that I could have used with MVC.  Its much better than digging into the Response variables IMHO.  You will note that most of the pages are now tail-less (e.g. http://adoguy.com/rants.aspx is now http://wildermuth.com/rants) though the old tail'd versions still work for backwards compatibility. I was able to simple tail-less versions by using his class (note the 2nd one uses an optional URI part):

 
routes.Map("Search").To("~/Search.aspx");
routes.Map("Rants/{page}").To("~/Rants.aspx");

I also set up my Rant URI's like so:

 
routes.Map("{year}/{month}/{day}/{topic}.aspx").To("~/ViewRant.aspx");
routes.Map("{year}/{month}/{day}/{topic}").To("~/ViewRant.aspx");

There was a challenge I had that I wanted the tail-less URI's but I am hosted on Windows 2003 (which means IIS6). I bit the perf bullet though and mapped all requests through ASP.NET.  This means that all requests are running through ASP.NET which can be slower but I thought it was worth the better URI model. IIS7 can do the tail-less URI's without having to resort to everything being a ASP.NET request.  Here's a good article on setting up IIS6: http://biasecurities.com/blog/2008/how-to-enable-pretty-urls-with-asp-net-mvc-and-iis6/.  You can also accomplish it with URL re-writing but if I wanted to do URL re-writing why'd I use the routing framework instead of re-writing them all?

Its been a fun exercise as well as a re-branding effort.  I hope everyone likes the new site!

Silverlight and Line of Business Applications

Silverlight Logo

In case you didn't catch it, I participated in a webcast called geekSpeak.  This webcast was hosted by Glen Gordon and Chad Brooks. The topic today was "Silverlight and Line of Business Applications". While geekSpeak's usually focus on hands-on examples of creating code, we took a different tact today and discussed the larger topic of where Silverlight fits in the development ecosystem (at least Microsoft's ecosystem).

For my money, the real benefit in Silverlight is for applications that cross the firewall. This means Line of Business applicaitons are really for B2B and B2C solutions. Unfortunately, what I hear from the community is that people see Silverlight as a solution for porting their desktop and traditional 3-tier applications to the web. Is this a good idea? I don't think so. The problem is that desktop development usually involves business objects that tend to have a direct connection to the database. Moving these sorts of applicaitons to the web means that you need to create an extra layer of communications and serialization. There is a cost both in development and performance for these extra layers.

It comes down to a key question...why are you moving to a web model for your application? If you want to expand the reach of your application to more users and clients (outside your organization), Silverlight is still a great story.  Unfortunately many organizations see web applications as a deployment solution. No install, no framework, etc.  While clearly this isn't true for Silverlight per se, its also a bad reason to go to a web application. Technlogies like Click-Once and XBAP are a great solution for a better deployment story than traditional desktop applications.

Since I brought up XBAP, let's plug it a bit. I notice that even amongst WPF guys, XBAP is a lost story. If you're not familiar with it, essentially its an in-the-browser WPF applicaitons that is deployed via manifest files (e.g. like ClickOnce). This means you can have the richness of UI, the better data binding story and interactivity that WPF/XAML affords you without having to deal with the limitations of Silverlight. I suggest that many organizations that want to use Silverlight for internal applications (inside the firewall) should be doing XBAP instead.

So what about ASP.NET/AJAX? The big story here is that HTML-based interfaces still have the longest reach of all the Internet enabled applications. HTML just works on many more platforms and browsers than Silverlight or Flash. Before you commit to moving away from HTML-based UI's, spend some time with your server logs.  Understand who is really using your existing application before you leave anyone in the dust. A better strategy is often to include fall-back functionality. For example, in my Silverlight Tour website (http://www.silverlight-tour.com), I decided that developers may have Silverlight installed so I wanted to give them a better experience by showing an interactive map of tour stops. But their bosses and accounting departments were unlikely to have it installed. In that case I made a design decision to never prompt to install Silverlight, but instead if it wasn't installed to show a simple table of the classes instead of the Silverlight app. This is a great solution to moving forward without leaving old users in the dust.

Why is Silverlight not a good solution inside the firewall? The two issues are infrastructure and security. In order to build solid line-of-business solutions with Silverlight, you need to have a way to communicate data with the server. Building this infrastructure can be labor intensive, but more importantly adds complexity. More moving parts == more than can go wrong. Security is the second issue. Silverlight (for good reason) is pretty locked down. This means you will need to often learn to work in tighter confines (limited access to storage, no access to file system, registry, ports). If your application is meant to work in trusted scenarios (e.g. Integrated Windows Authentication), the limitations of the high-security environment will be a real limiter.

So what does this all mean? I still think Silverlight is still the solution when you need to *extend* the HTML model in the browser. Line of Business applications across the firewall still need to be web driven in my opinion, but enhancing that story with Silverlight for soutions like data visualization, user interactivity or media is a great solution. While I think that creating whole-browser solutions make sense for some applications, it doesn't for many many more. My fear is that we will move from monolithic desktop apps to monolithic Silverlight apps.  The web is still a disconnected model and deciding on building a single huge Silverlight app (instead of page-based functionality) just doesn't make much sense. If you are planning on building one of these monstrocities, please also read my recent blog about linkability in Silverlight:

http://wildermuth.com/2008/06/25/Doesn_t_Anyone_Bookmark_Anymore.aspx

Converting ASP.NET 2.0 to 3.5 for sites that use Silverlight 1.0

Silverlight Logo

Quick fix for a problem that was haunting me today:

If you upgrade an ASP.NET 2.0 app to 3.5 and have .xaml files in your project that are part of a Silverlight 1.0 or 1.1 project, the conversion wizard converters them to Build-type: "Page" and adds a custom build for building the WPF files.  If this happens you'll get a cryptic error:

"error MC6000: Project file must include the .NET Framework assembly 'WindowsBase, PresentationCore, PresentationFramework' in the reference list."

Go through your xaml files and mark them as "Content" and remove any custom tools and it will fix it.

Behind the Scenes at ADOGuy.com - Part 1

I recently was talking with a prominent developer in the Microsoft community as they were creating a new version of their website. They had used a code generator to create most of the code on the new site which I thought was cool, but I immediately wanted to know how much functionality was included. As I talk with other MVP's as well as Regional Directors, it seems that I am in the distinct minority.  wildermuth.com is written almsot completely as custom ASP.NET code. Sure I used some components and started with someone else's HTML template, but most of the code is still C# to do much of the heavy lifting. Most of the people I talk with use community use pre-packaged systems to host their sites (including Community Server, DASBlog, etc.). 

Why do I keep all that custom code?  Mostly to allow me to try out new web techniques.  I am facinated by CSS and like to learn how different sites do what they do. I also like to add new technologies as I can (e.g. Silverlight, LLBGenPro) as I learn them.

So what is interesting about wildermuth.com? Like most web sites, wildermuth.com is backed with a database.  I store each rant, talk, code example (et al.) in a database.  Much of this content is old.  My first Rant was created back five years ago.  Then the site was ASP and the database was a local Access database, and the website name was even different (comguru.com but I abandoned that name years ago...I was never really a COM Guru, more like a persistent hack but that's a story for another day).  Here's what an early version of wildermuth.com looked like (via archive.org...some images are missing):

It was pretty ghastly back then.  Originally I had these secretly named pages to edit the data-driven items.  I've always hated using Access or SQL Server tools to maintain the data.  I always thought it was better to have a web-driven administration so I could write blog entries anywhere.  That's what wildermuth.com is hiding.  For example, here is a typical story as you the reader sees it (click for a full resolution version):

Here is the same story as I see it as an Administrator of the site (note the red arrows):

Originally, clicking on edit would edit the items inline but I found this cumbersome and buggy so I simplified when I rewrote the site earlier this year to simply take me to pages to edit the items:

I am cheating a little using the FreeTextBox (a great free tool if anyone is interested).  I think this is the approach that most sites that are data driven.  Give up your iSQL and SQL Management Studio and take some extra time to allow you to edit your site on your site.  It introduces some interesting opportunities to learn. 

In the next part of this series, i'll delve into how I handle RSS on my site...

Cookies Ruined the Breadcrumbs...

Trish and I are moving soon.  She's been sending me links to rugs for our new living room floor. She found an interesting one today:

target web problem

(click for larger view)

Why should you care?  Ignore the rug, notice the breadcrumb to the rug:

Target thinks that this Rug is in its XBox 360 Console section?  Or does it? When I looked at the rug on Trish's computer, it was in the right place (under household items).  So what happened?

Seems that a few days ago I was browsing Target to find an XBox 360 Elite to replace my dead console. Putting two and two together, I think that cookie's ruined the breadcrumbs. I think that target stores data as I browse their in a cookie to re-create where on the site I am. When Trish sent me a link, Target incorrectly assumed I was still using the same browsing from earlier (several days earlier) and ruined their cookies. 

I saw this very problem several years ago when I was working in Boston on an Internet site. It taught us to think carefully about what data to store where.  It forces you to think about user data carefully. In this case the breadcrumb data is stored as a cookie on my machine but I am still an anonymous user.  In this case I don't see why they wouldn't just re-create the breadcrumb by either storing data server-side or more interestingly from their database data.  Sure depending on their schema, perhaps this rug exists in a number of places in their catalog, but that would certainly been better than this. 

The fact is that when you design a website, remember that people are going to save and send URL's and side affects are going to happen.  Think about how users use your site, test edge cases and plan for them. So imagine if it was more embarassing:

Ok, stop imagining now...

New Website Look

I've reworked my web site. It was looking a bit more like the uniform of a NASCAR driver than a web site, so I reworked the layout to make it cleaner. I admittedly stole many of my ideas from other web sites and templates I saw.

The other reason for the change was to eliminate ViewState.  I noticed my pages ballooning from the sheer size of the HTML that was being generated, much of it as ViewState.  I've eliminated ViewState in almost every case.  For example, the size of the HTML of my home page was reduced from 134K to 48K.  That's just HTML size, not images...so the real size change should be even more dramatic.

Let me know what you think and if you find anything not working.  Thanks!

ASP.NET AJAX RTM and WCF Do Not Play Well Together...

I am delving into WCF and AJAX (not at the same time) lately so I wanted to see if they were compatible.  According to this whitepaper on ASP.NET (follow the link and scroll down to "Support for WCF Web Services"), the RTM of AJAX does not support WCF.  It seems they removed it so they could make it work better in a later release.  The promise is that by the Orcas release of VS, they will be compatible. 

This further cements my opinion that releasing .NET 3.0 without FULL tool and compatibility is nonsense. Without a good across the platform support (e.g. WCF and ASP.NET stack working well together), a workable WPF editor (Cider is horribly broken currently...change the default editor for XAML to XML, you'll be happier), and projects that actually compile out of the box (WPF projects don't compile currently without some hand-editing of the XAML). Microsoft has always been about tools more than technology, that's why I've been with them so long.  If we need to cruft together a bunch of installs to make stuff work, I'd be doing that in Java and Linux.

It looks like most .NET 3.0 development should wait until late 2007 when Orcas ships...but that's just my opinion...

Sample Code for Custom ASP.NET Profile Provider

Until The ServerSide .NET can post the sample code, I am posting it here

- Sample Code

Thanks to Thomas Carpe for reminding me that I posted the wrong code!  Fixed!

SqlDataSource Issues...

I've blogged before about issues with the SqlDataSource.  I've crufted up an example of the problems that can be downloaded here (with usual caveat of changing the connection string in the web.config to point to a DB with the Northwind database).

There are three basic issues:

  • The problem is that if a database allows nulls (and a specific row has an NULL) then the automagically created queries in the SqlDataSource will affect zero rows. So data loss can happen without alerting the user.
  • The other related issue is that if an Update fires and affects zero rows it is swallowed unless the user handles the DataSource.Selected event.
  • Lastly, if your query includes SQL Server Timestamp fields, the SqlDataSource designer doesn't know how to handle it correctly (either by inserting it into the Keys field of the control, or that it isn't a varbinary field).

I want the ASP.NET team to use the SqlCommandBuilder so that we have a common SQL Generationh platform so problems fixed in one place don't re-rear their heads.
 
In general you can get around these issues by hand-crafting your own SQL (or preferrably using Stored Procs) and handing the Updated and Deleted events in your DataSources.  The problem with this is that since SqlDataSources seem to be a good RAD-type tool, then they should work in most databases 'out of the box' and they don't.

Any comments on the code or this issue are certainly welcomed!