Three Is It

Because two isn't enough and four is just too many

We know that dictators are quick to choose aggression, while free nations strive to resolve differences in peace.
George W. Bush, Sept. 2004
Home Blogs Genealogy Brad's Bookshelf Subscriptions Contact Sign in
 

About the author

Brad Butts is a .NET developer and architect. He is married with children and enjoys reading, working out, and genealogy is his five minutes of spare time.
E-mail me Send mail
National Debt Clock

Recent comments

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010

Unit Testing in ASP.NET

I’m all for unit testing and even Test Driven Development (although I can do without some of the TDD zealotry out there).  Recently, I began writing some libraries for ASP.NET applications and really struggled with how I was going to write my unit tests to test my work.  Certainly, we have the MVP and MVC patterns and lots of great examples, frameworks, and miscellaneous tools to help you implement these patterns and greatly simplify the unit tests you need to write—but I’m writing an ASP.NET-based library, not an end-to-end application.

Here’s what I’m doing:

I have to write components around a commercial security device that injects certain identity information into the request header of http requests (via an ISAPI filter).  Specifically, I am writing an HttpModule (a few, actually, for different scenarios) to build out a custom Identity object and insert it into the Current.User object.  Additionally, I’m writing a couple of RoleProviders to manage the roles returned by the device.

These components obviously have a tight reliance on HttpContext, so how do you unit test under such a scenario?  Incidentally, these components have to work with a pure ASP.NET 2.0 solution, which means I don’t have access to the new System.Web.Abstractions assembly which is supposed to make testing in this space much easier—I’d still like to see what the Abstractions assembly could do for me, though.

Phil Haack offered up this effort at mocking HttpContext, but I simply couldn’t get that to work for my scenario.  I then looked at TypeMock.  Here’s some code I wrote against TypeMock that still doesn’t work:

[Test]
[VerifyMocks]
public void TestOnAuthenticateRequest2()
{
string fname = "John";
string lname = "Smith";
string fullName = fname + " " + lname;
string roles = "cn=Role1,ou=APPS,o=SomeCompany:cn=Role2,ou=APPS,o=SomeCompany";
string login = "jxsmith";
string commonName = "Smith";
//TestedImplementation is an alias for my custom HttpModule
TestedImplementation target = new TestedImplementation();
MockObject<HttpApplication> applicationMock = MockManager.MockObject<HttpApplication>();
MockedEvent authRequestEvent = applicationMock.ExpectAddEvent("AuthenticateRequest");
target.Init(applicationMock.Object);
MockObject<HttpRequest> httpRequestMock = MockManager.MockObject<HttpRequest>();
applicationMock.ExpectGetAlways("Request", httpRequestMock.Object);
MockObject<HttpResponse> httpResponseMock = MockManager.MockObject<HttpResponse>();
applicationMock.ExpectGetAlways("Response", httpResponseMock.Object);
httpRequestMock.ExpectGetAlways("HttpMethod", "POST");
httpRequestMock.ExpectGetAlways("AppRelativeCurrentExecutionFilePath", "foobar.aspx");
httpRequestMock.Object.Headers.Add("LOGIN", login);
httpRequestMock.Object.Headers.Add("FIRSTNAME", fname);
httpRequestMock.Object.Headers.Add("LASTNAME", lname);
httpRequestMock.Object.Headers.Add("ROLE", roles);
httpRequestMock.Object.Headers.Add("COMMONNAME", commonName);
authRequestEvent.Fire(applicationMock.Object, EventArgs.Empty);
//once I can actually mock the context, I'll write some asserts
}

As I recall, I could never figure out how to stub in data to the request header of the mocked request.  Doing this is pretty important as my components behave differently based on the data present (or not present) in the header.  A sister project to TypeMock called Ivonna (no pun intended) actually might let me do this, though.  Of course, both these products cost money—so, I’d still like to find a free answer to my problem.

Some folks have suggested refactoring my IHttpModule and RoleProvider implementations such that I move as much code as possible out of the direct implementations and into separate classes that can be more easily tested.  I assume they mean separate private classes that aren’t exposed to clients using my components.  To that end, here’s what I tried in my implementation:

public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest += OnAuthenticateRequest;
}
private void OnAuthenticateRequest(object sender, EventArgs e)
{
HttpApplication httpApp = sender as HttpApplication;
MyHttpModuleHelper helper = new MyHttpModuleHelper();
helper.DoStuff(httpApp.Request.Headers);
}
public void Dispose()
{
//dispose stuff here
}
}
//the private class that can be tested
class MyHttpModuleHelper
{
public string[] DoStuff(NameValueCollection headers)
{
//process the headers in some way
return new[]{"hello world"};
}
}

And in my unit test class:

private MethodInfo DoStuffMethod;
private object MyHttpModuleHelper;
[SetUp]
public void Init()
{
Assembly testAssembly = Assembly.GetAssembly(typeof (MyHttpModule));
MyHttpModuleHelper =
testAssembly.CreateInstance("MyNamespace.HttpModules.MyHttpModuleHelper");
Type helperType = MyHttpModuleHelper.GetType();
BindingFlags bf = BindingFlags.Instance | BindingFlags.Public;
DoStuffMethod = helperType.GetMethod("DoStuff", bf);
}
[Test]
public void DoStuffTest()
{
NameValueCollection stubHeaders = new NameValueCollection();
//stubHeaders.Add();  //add some fake data
object returnValue = DoStuffMethod.Invoke(MyHttpModuleHelper, new object[] { stubHeaders });
Assert.IsTrue(returnValue.GetType().IsArray, "Expected the type to be a string array");
string[] roleArray = returnValue as string[];
Assert.IsTrue(roleArray.Length == 1, "Expected the array length to be 1");
StringAssert.IsMatch(roleArray[0], "hello world", "Expected the first value to be hello world");
}

This works, but it’s such a pain.  One frustration I’ve had is that I tend to refactor my helper methods a lot—the parameters I pass, the return values I return, even the names of the methods themselves.  Every time I refactor, I have to go back to my unit tests and refactor them accordingly—even Resharper isn’t smart enough to cascade through such refactoring when I’m using Reflection to invoke those methods.  Do I have any other options?

Virtually at the end of my rope, I even contemplated the mildly insane notion of writing a test ASP.NET app to Response.Write out values from my Identity and Principal objects rendered by my HttpModules and RoleProviders…then use a product like Selenium or WatiN to perform assertions on those values in the pages.

Just when I thought all hope was lost, a friend of mine reminded me of the ASP.NET host adapter.  The ASP.NET host adapter allows you to run a unit test that smells a lot more like an integration test—like integration testing, you have to develop a web app within which your test will run.  The big difference, though, is that you can perform assertions on server-side objects. 

You invoke the ASP.NET host adapter by decorating your MSTTest test method with the attribute, [HostType(“ASP.NET”)].  You also have to include the attribute [UrlToTest(“someUrl”)]—where you specify the ASPX page that establishes the server-side context you’d like to test.  You can add one more attribute, AspNetDevelopmentServerHost, to have you test web app run under the Development Server (a.k.a. Cassini) as opposed to IIS.

It’s lame that I need a web app to run a unit test, but I’m desperate, so I’ll play along.  As I started all this work, one obstacle I kept running into was the UrlToTest attribute: it seemed to only understand the “localhost” domain name.  As I mentioned earlier, I’m writing components that have to play nicely under this third party ISAPI filter.  The ISAPI filter will only be invoked when it detects certain domain names it’s configured to listen for.  Localhost is not one of these—if it were, every time anyone would run an app under localhost, the ISAPI filter would invoke a particular set of policies that may or may not have anything to do with your work—frankly, it would just be bad.

Instead, in Development, until we can get a real IP assigned to our app, we create fictitious domain names by mapping 127.0.0.1 to that fictitious name in our local hosts file.  We configure the ISAPI filter to apply our particular policy when it detects this fictitious host name being requested and all is right in the world.  That is, until you try to invoke the ASP.NET host adapter using your fictitious domain name: it seems that the ASP.NET host adapter could care less about what mappings I have in my hosts file.  I even tried my own IP address and it didn’t like that, either.  Finally, I tried my machine name and, ta-da, the ASP.NET host adapter worked and my test ran.  It just so happens that I can use my machine name as one of these fictitious domain names, but I consider that bad form—it tightly associates my machine to the development environment.  Now, even my tests are tightly associated to my machine.  That really stinks—so much for running my tests on a separate build server…they just won’t work.

Here’s an example of one of my test methods:

[TestMethod]
[HostType("ASP.NET")]
[UrlToTest("http://MyMachineName/SomeVirDir/Default.aspx")]
public void IdentityType()
{
Assert.IsNotNull(HttpContext.Current.User, "Current.User object is null");
Assert.IsNotNull(HttpContext.Current.User.Identity, "Current.User.Identity object is null");
Assert.IsInstanceOfType(HttpContext.Current.User.Identity, typeof(MyCustomIdentity),
"Current identity is not of type MyCustomIdentity");
}

Another thing I noted regarding the UrlToTest attribute is that it only likes my “short” machine name—I had to truncate off my DNS suffix in order to get my test to run properly.

So, this is a terrible way to test—my tests are tightly bound to my machine and require a web app of all things to run a simple unit test—but, at least it works on my machine.  The alternatives would be scraping together a few hundred bucks to buy TypeMock/Ivonna (which would come out of my pocket, not my company’s—see here on why that’s the case) or upgrade to .NET 3.5 SP1 with hopes that the new Abstractions assembly can help me out.

For the moment, I’ll stick with this approach.  My next challenge is to figure out how to dynamically change my web configuration between tests to swap out HttpModules and RoleProviders, as I need to test a few of these.  Any ideas?

Currently rated 2.5 by 4 people

  • Currently 2.5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Brad on Saturday, February 21, 2009 7:14 PM
Permalink | Comments (7) | Post RSSRSS comment feed

Suggestions for Microsoft, Part 1

Let's just call this Part 1 in a potentially lengthy series of frustrations suggestions aimed at Microsoft.  

To be clear, I like Microsoft: I make my living using Microsoft tools.  However, as with all things, I occasionally encounter frustrations with their tools and/or services and need a place to vent.  Well, here it is.

Suggestion: Please decouple the different tools within Visual Studio
One huge frustration for me is the apparent assumption that most/all Microsoft customers use the full stack of Microsoft tools.  I see this theme manifest itself in many different areas of software development, but for this post, I just want to focus on the tools for building, testing, and deploying .NET applications (mainly web applications).

So, here's my caveman view of the software development lifecycle (absent project management methodology jargon and concepts like iterative development):



In my diagram, I've identified different activities and where they occur: for instance, coding and authoring of unit tests take place on the developer's workstation while I expect formal builds, formal unit testing and analysis and even formal packaging operations to occur on the build server.  Finally, I've added suggested tools that are used for each activity.

Now let's talk about my frustrations: thanks to the tight coupling of the different tools within the umbrella of Visual Studio, we have to be very careful about what tools we use in development because those tools may not be available to build/test/package our code on the build server.  Unlike other shops, my company has quite a mixture of technologies in the software development stack--most important, this means no Team Foundation Server.  Note: I've observed most of these frustrations under Visual Studio 2005, but I'm not optimistic that they've been remedied under Visual Studio 2008.

My first problem is with unit testing.  When Visual Studio 2005 rolled out, my inclination was to run with MSTest.  That is, until I realized that MSTest required an instance of Visual Studio be installed on the build serverOthers have railed against MSTest in general, but my most immediate problem with it is the fact that I would have to corrupt my formal build environment with a developer tool.  It seems to me that having a development tool on the build server would be a violation of the separation of duties we try to practice at my organization, not to mention the silliness of having to buy an extra Visual Studio license just so it can sit on a build server and facilitate unit testing.  Frustration #1: MSTest cannot stand alone from the Visual Studio IDE.

So, let's scrap MSTest like Jeff Palermo did and go with one of the open source testing frameworks like NUnit (maybe, some day, Microsoft will decouple MSTest from Visual Studio).  That's great, but how do I calculate code coverage?  Visual Studio Team System will calculate code coverage but I assume that only works against tests written on the MSTest framework.  If I don't go MSTest, I guess that means I'll have to scrape together a few bucks (albeit not that much) and buy something like NCoverFrustration #2: VSTS Code Coverage only works with MSTest (this is an assumption, to be sure, so if I'm wrong here, someone please let me know--and let me know how to configure VSTS to calculate code coverage on NUnit tests).

Alright: let's assume we've moved past the unit testing and code coverage issues.  We've made all the right decisions so that our code is going through formal builds and unit tests on a nice, clean build server.  Now, how do we push out our compiled artifacts to our Production server?  I get the impression that most folks at Microsoft advocate either XCopy or Visual Studio Publish.  These tools either require the Visual Studio IDE outright or a deeper knowledge of Microsoft deployment tools than the average deployment lackey will have (remember, separation of duties means that developers won't be the individuals deploying applications to Production servers).  

Personally, I'm a fan of deploying via Windows Installer files--that is, an MSI or installation EXE.  What's more, Visual Studio includes Setup project templates that make it easy for a developer to author the script that installs his product on the Production server--after all, who better knows how an application should be installed on a server than the developer himself?  

So, on my clean build machine, I have MSBuild or, perhaps, Nant compile my source code, run my unit tests, and maybe even do some code analysis.  Once that's all done, I have MSBuild use the instructions of my Setup project to build my MSI and...er, that didn't work?  You mean to tell me that MSBuild can't build my Setup project?!  Well, that's just nice.  And what's Microsoft's suggestion: use devenv.exe.  Yes, once again, sully my formal build environment with the Visual Studio IDE.  Frustration #3: MSBuild can't build my MSI.  

So, now, I either have to scrape together more dollars for a commercial product like Wise or InstallShield or learn to grapple with Windows Installer Xml (there do appear to be some MSBuild tasks for building WiX artifacts, though).

Confound it, Microsoft!  You have great tools, but your build and deploy story is a Grimms fairy tale!  Ok.  I feel a little better (not really).  If I'm totally off track here or if you have any better suggestions, please let me know.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Brad on Sunday, October 26, 2008 9:12 PM
Permalink | Comments (19) | Post RSSRSS comment feed

aspnet_merge.exe exited with code 1

Pretty descriptive error code, eh?  Well, I was going to put together an example of what aspnet_merge.exe can do, so the first thing I wanted to do was grab an example application, particularly one that makes use app_code files.  I decided to use the code from BlogEngine.NET.

Yes...I know, I know...the BlogEngine.NET code was never designed to live under a WAP-type model, but surely it can live under that model, right?  Well, uh, not without difficulty.

Step 1: Run aspnet_merge and see what happens
So, I used the Visual Studio 2005 Web Deployment Projects to construct a WDPROJ--an MSBuild project with sweet tasks to run the aspnet_merge utility.  (Note: as far as I can tell, BlogEngine.NET is still a Visual Studio 2005 solution)

At the command line, I typed: msbuild BlogEngine.web_deploy.wdproj



The build started and 54 seconds later...blam!  error MSB6006: "aspnet_merge.exe" exited with code 1.

So, what happened?  Sounds like this error happens when class names collide.  Like so many things, the Web Site project tends to let you get away with bad coding practices like using the same class name for a page's code behind in multiple places--so long as the pages are in separate folders.  Aspnet_compiler will compile separate DLLs so there's never the chance that a single assembly will contain two classes of the same name; however, when aspnet_merge tries to merge all the assemblies into one, the class names will clashes and cryptic errors like "error code 1" will get thrown.

Step 2: Figure out where my problems are and fix them
Unfortunately, the default console logging behavior of MSBuild does not tell me where my errors are, so, after consulting with my handy-dandy MSBuild command line reference, I figure I can do something like this to get more detail on where my errors are occuring:

msbuild /nologo /v:diag BlogEngine.web_deploy.wdproj > msbuild.log

Scrolling up from the bottom of my log, I see this error:

An error occurred when merging assemblies: ILMerge.Merge: ERROR!!: Duplicate type 'widgets_LinkList_edit' found in assembly 'App_Web_nbdnprem'.

Ah-ha!  Multiple classes each named widgets_LinkList_edit.  A quick search for that name reveals that, yes, that class name is used for both the edit.ascx code behind of the LinkList and TextBox widgets.  To fix, I'll go to the TextBox's edit.ascx.cs file and change that class name to widgets_TextBox_edit and to the page declaration in the associated ASCX page and update that, as well.

1...2...3...4 error code 1 issues later and it looks like I have a build!  But do I have a good build?

Step 3: Fire up my BE build to see if it works
Seems like the easiest thing to do is to fire up an instance of the ASP.NET Development Server to host my BE build, so I execute this at the command line:

WebDev.WebServer.EXE /port:8080 /path:"D:\MyTests\BE_Test1\BlogEngine.Web_deploy\Debug_test1" /vpath:"/BE_Test1"

And I get...a Parse Error:



It looks like the page is barfing when it tries to load the PostCalendar user control.  Interestingly, the PostList user control loads fine.  I wonder why?

Looking at the default.aspx markup code, I see that the PostList user control is registered at the top of the page, but the Calendar user control is not.  What gives?



Well, it looks like, as Phil Haack once explained, the controls with the "blog" prefix are registered in the web.config.  Didn't know you could do that.  Pretty cool.

<pages enableSessionState="false" enableViewStateMac="true" enableEventValidation="true">
<controls>
<add namespace="Controls" tagPrefix="blog"/>
</controls>
</pages>


This doesn't fix my problem, though.  ASP.NET Parse Errors are usually an indication that the CLR can't find the referenced class in the assembly.  Well, I know the PostCalendar class is in my single assembly (.NET Reflector told me that), so maybe the CLR is not able to find the assembly itself.  Maybe I can help.  Let's modify the control registration in the web.config with the assembly attribute:

<pages enableSessionState="false" enableViewStateMac="true" enableEventValidation="true">
<controls>
<add namespace="Controls" tagPrefix="blog" assembly="BlogEngine.Web_deploy"/>
</controls>
</pages>


Hey, what do you know?  It worked!  Now, the page loads up just fine.  Note that this last change I made in the web.config in my build folder; if you try to change the original web.config and then run BE from the IDE, you'll get a compiler error.  It seems to me that these kinds of issues can introduce a lot of challenges to development, as you try to account for the fact that the web.config you deploy to a Production server will need to look slightly different than the web.config that you run in your IDE.  I wonder if there are any MSBuild tasks designed to modify the web.config spit out by the build engine?  Scott Guthrie has talked about other important changes that should occur in your web.config before deploying it to Production.  I wonder if anyone has written a MSBuild task to accommodate those changes?

Clicking around in my pre-compiled BE instance, everything seems to work fine; however, I did encounter one show-stopping error that prevents me from doing important administration tasks--like adding a new entry.  At least I know that issue's related to the BE distribution I downloaded, not my build experiment with aspnet_merge.

So, what have I learned?  Doing pre-compilation on complicated Web Site projects can be tricky business--particularly when using aspnet_merge.  I wonder if I should just demo how to migrate a Web Site project to a Web Application project?

 

 

Currently rated 2.0 by 1 people

  • Currently 2/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Brad on Friday, October 24, 2008 1:48 PM
Permalink | Comments (74) | Post RSSRSS comment feed

Deploying to different environments

My company maintains different environments--Dev, IT, UAT, Production, and Disaster Recovery--in which to run and test software solutions: particularly web solutions.  My company also (I think rightly so) places restrictions on what environments developers can access and which ones they can't--the old Segregation of Duties principle.  So, the question is, how do developers build applications mallable enough to run under different environments that can be deployed as simply as possible so as to not confuse the server admins and deployment personnel?

Our deployment vehicle of choice is the old Microsoft Installer file (MSI).  With regard to environment-sensitive settings, we simply "extend" applicable sections of the web.config to a second environ.config file.  There are always edge cases, but this approach seems to work out pretty well on the whole.  The following is an example...

Step 1: Abstract whatever environment settings to a separate file.  In this example, I use the appSettings section to house my configuration values, but this pattern works for most other sections I've tried (eg. connectionStrings)

In my web.config


<appSettings file="environ.config">
<add key="key1" value="someOtherValue"/>
</appSettings>


In my environ.config
<appSettings>
<add key="MyEnviron" value="Development"/>
</appSettings>
 

Interestingly, the appSettings node seems to be the only node that supports the "file" attribute.  All other configuration sections I've extended use the attribute "configSource".  I noticed the other day, though, that appSettings also seems to support the "configSource" attribute.  It looks like you need to be careful about your use of that attribute, though (see the Note in the description of the "file" attribute).

Step 2: Create other environment configuration files for the different environments you need to address.  In my example, I've created a IT.environ.config, UAT.environ.config, and Prod.environ.config.  Each configuration should look the same--only the values should change.


Step 3: Test your app to make sure it works in your "Development" environment with your default environ.config file.

Step 4: Now, add a web setup project to your solution to package/deploy your solution.  For simplicity, I've used Scott Guthrie's Web Application Project so I can easily add the Primary Output and Content Files to my web setup project.  On the Content Files output, make sure to add the ExcludeFilter: *environ.config.  This will exclude our environment specific config files--we'll add them in later.

 

Step 5: At some point, someone has to know/decide what environment your web app is being deployed into.  I contend that that decision should be made at the point of deployment.  That decision can be fairly easily automated (and I'll probably talk about that in a future post), but for now, we'll place that decision in the hands of the human installing your MSI on a given server.  

  1. In Solution Explorer, right-click on your web setup project and select View > User Interface from the context menu.
  2. In the User Interface tab, right-click on the Start node and select Add Dialog from the context menu.
  3. In the Add Dialog dialog, select the RadioButtons (4 Buttons) choice and click OK
  4. Move your new dialog to the appropriate order (usually after the Welcome dialog)
  5. Set the properties of the dialog more or less as follows:


 

Step 6: Go back to the File System tab.  Under the Web Application Folder, one by one, add each of your environment config files.  For each file you will need to set two properties:

  1. On the Condition property, set and equation like this: ENVIRON="Dev"
  2. On the TargetName property, set the value to "environ.config"

Obviously, your Condition equation will change for each file.  For the Prod.environ.config file, the Condition property will be ENVIRON="Prod".

 

Step 7: Finally, build your MSI and test it out.  When you choose, say, the UAT environment, make sure that your UAT config file gets deployed and renamed appropriately.  Attached is a sample app for your perusal.
 

 


MSIEnviron.zip (568.92 kb)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Brad on Friday, September 26, 2008 7:51 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Who needs the CodeBehind attribute?

Maybe this is obvious to everyone but me because I've found no discussions on the topic...or maybe my googling skills just stink...or maybe both...but after looking at the markup in some of SharePoint's ASPX pages and seeing neither a CodeBehind nor a CodeFile attribute in the Page Directive, it occurred to me that at least precompiled ASPX pages don't need either of these attributes.  The required attribute is Inherits.  Presumably, the .NET runtime uses the fully qualified assembly name in the Inherits attribute to discover the code behind for the requested page.  

Here's what I mean...

When I start a new Web Application Project in Visual Studio 2008 (or add a new page to such a project) and look at the markup in the ASPX page, I see a page directive declaration like this:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Examples.CodeBehindAttrTest.Default" %>

I can jump to the code-behind file (Default.aspx.cs) and add this code to the Page_Load event:

protected void Page_Load(object sender, EventArgs e)
{
Response.Write(String.Format("The time is: {0}", System.DateTime.Now.ToString()));
}


I can compile and run my project, browse to the default.aspx page, and see the timestamp print out with no problem.  

So, what happens when I remove the CodeBehind attribute?  Well, the very same behavior happens.  This makes sense, too, because I don't deploy CS files to my production Web servers yet I make great use of code-behind files.  Of course, Microsoft basically says as much:

"This attribute [CodeBehind] is not used at run time." (from the Page Directive reference)

For your Web Site projects, I assume you could equally remove the CodeFile attribute, but that appears much stickier, what with having to precompile the site, setting the Inherits attribute correctly, and all.

The drawback of removing the CodeBehind attribute from your pages seems to be one of development-time convenience.  Without knowing what code file represents a given ASPX page, should I, say, try to add a click event handler to my server side button by double-clicking the button in the Design tab, Visual Studio will add server-side code inline to my ASPX page instead of the default behavior of taking me to my code-behind page.  If I cut and paste the inline code, drop it into my code behind page, recompile, and run the app, the event will fire as coded in my compiled DLL.  Visual Studio is even smart enough to declare my button control in the designer partial class.

Currently rated 3.0 by 2 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Categories: Technology Blog
Posted by Brad on Friday, September 12, 2008 5:30 PM
Permalink | Comments (10) | Post RSSRSS comment feed