February 2009 - Posts

If you use LINQ to SQL (which if you're considering it, you should consider going to the ADO.NET Entity Framework), the DataContext object that's automatically generated for you has a variety of constructors that you can use.  One of the more important constructors is the one that takes a connection string.  This connection string overrides the default connection string generated.

When you make a change to the model, the LINQ data context takes the connection that you drag/drop from, and puts it in the application settings.  So if you create your context like:

CustomDataContext ctx = new CustomDataContext();

This connection will use this value.  I like to use the override, which takes a connection string:

CustomDataContext ctx = new CustomDataContext(ConfigurationManager.ConnectionStrings["cs"].ConnectionString);

I usually use the static factory approach to create this, as:

public static class ContextFactory
{
    public static CustomDataContext GetInstance()
    {
         return new CustomDataContext(ConfigurationManager.ConnectionStrings["cs"].ConnectionString);
    }
}

This works nicely, and centralizes the location of the connection string being used.

Posted by bmains | with no comments
Filed under:

When looking at developing with ASP.NET AJAX, you have to understand how server-side settings affect client-side settings.  Let's take a look at the button below.

<asp:Button ID="btnSave" runat="server" UseSubmitBehavior="false" />

This button renders the following:

<input type="button" onclick="__doPostBack(..)" .. />

If I would have not specified submit behavior, it would have used type="submit".  An input with type="button" actually works better with ASP.NET AJAX, because you can tap into the button events more easily.

I was reading "ASP.NET AJAX 3.5" from Wrox, and the authors had a good point; because of applications using more scripting, it may be beneficial to disable key features until all of the scripts have loaded.  For instance, if you have validation that validates input on a form based upon a button click, and the user clicks the button before the page is used to it, then an exception could occur and the page wouldn't work as expected.

A better solution is to disable the button, so I tried this:

<asp:Button ID="btnSave" runat="server" UseSubmitBehavior="false" Enabled="false" />

But this dealt me a blow; it omitted the onclick call to __doPostBack, which is not what I wanted.  Because Button inherits from WebControl, and WebControl implements IAttributeAccessor, this IAttributeAccessor specifies that any assignment that doesn't map to a server-side property automatically gets passed to the client.  This means that if I did:

<asp:Button ID="btnSave" runat="server" UseSubmitBehavior="false" disabled="disabled" />

The button would render as:

<input type="button" onclick="__doPostBack(..)" disabled="disabled" />

This is because disabled isn't a server side property, and it automatically gets passed to the client.  Getting back to the original point, the pageLoad() event fires on the client when the scripts have been loaded, so I can reenable the button as:

function pageLoad() {
    $get("<%= btnSave.ClientID %>").disabled = false;
}

This feature only works if scripts work; if JS is disabled, then this isn't a good approach.  Thanks to the Wrox authors for relaying that tip to me.

Posted by bmains | with no comments
Filed under: ,

ASP.NET AJAX provides some powerful networking tools to create dynamic applications on the fly.  Most people are aware of the ways that web services are used in ASP.NET AJAX, using the Sys.Net.WebServiceProxy (or a proxy that uses this class under the hood) to call the service's methods.

Another object involved in all of this is the WebRequest object.  Take a look at the code below.

function invokeRequest(url) {
  var request = new Sys.Net.WebRequest();
  request.set_url(url);
  request.set_httpVerb("POST");
  request.set_body("Some=True;Control=True");
  request.get_headers()["Content-Length"] = request.get_body().length;
  request.get_headers()["AJAXPostback"] = "True";
  request.add_completed(requestCompleted);

 request.invoke();
}

function getPageUrl() {
  return "WebRequestAspxTest.aspx";
}

The WebRequest object has a few parameters: url, httpVerb, body, headers,  and a few others not specified (timeout, etc.) that can perform a callback to the page.  This callback is not a web service callback and doesn't require the WebMethod attribute defined on a page method.  Instead, it will invoke a callback to the page, and run the page lifecycle.  By making the call above (which references the same page as where the script lives), it will invoke the following code:

if (Request.Headers["AJAXPostback"] != null)
{
 this.lblResults.Text += "Operation posted to the server<br/>";
 Response.Write("Output Text");
}

You may expect that it outputs the text and returns the results; actually, the execution success callback fires:

function requestCompleted(executor, args) {
 var label = $get("<%= lblOperationResults.ClientID %>");

 if (executor.get_responseAvailable())
  label.innerHTML += "Operation Completed<br/>";
 else {
  if (executor.get_timedOut())
   label.innerHTML += "Operation Timed Out<br />";
  else
   if (executor.get_aborted())
   label.innerHTML += "Operation aborted<br />";
 }
}

This code executes, but what is the actual result?  Actually, the results of the entire page rendering are available in the executor.get_responseData() property; this property contains the HTML contents as a string, because it's actually invoking a page request.  While this may not be the most helpful, we're going to look at ways to make it easier to use, using a HTTP handler. 

Posted by bmains | with no comments
Filed under: ,

It's really easy to perform dragging and dropping in JQuery.  Let's begin by looking at the content that can be dragged or dropped.

<div id="Drag1" class="Draggable">
 Drag Content
</div>
<div id="Drag2" class="Draggable">
 Drag Content
</div>

<br><br>

<div id="Drop" class="Droppable">
 Drop Here 
</div>

Here we have three div's; the first two define the content that can be dragged, and the last defines the drop region.  The css classes aren't important, they simply style the container and do not add anything pertinent to this function, except they identify the objects that can be dragged/dropped.

The scripts used from JQuery are defined below:

<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript" src="http://ui.jquery.com/testing/ui/ui.core.js"></script>
<script type="text/javascript" src="http://ui.jquery.com/testing/ui/ui.draggable.js"></script>
<script type="text/javascript" src="http://ui.jquery.com/testing/ui/ui.droppable.js"></script>

The first script is the JQuery script, along with a core script for UI (contains common functions and such).  The next scripts are the draggable/droppable scripts that perform the drag and drop functions, the core to this script.  Below is the script to perform the drag/drop.  Notice how easy it is.

<script language="javascript" type="text/javascript">
 $(document).ready(function() {
  $(".Draggable").draggable();
  $(".Droppable").droppable({
   hoverClass: "DroppableHover",
   drop: function(ev, ui)
   {
    $(this).append("Dropped");
   }   
  });
 });
</script>

All of the elements defining the Draggable class are returned and call the draggable() method, marking the elment as draggable.  There are a couple of options when it comes to the draggable method, most notably the ability to provide a range of settings to define the drag behavior.  The droppable feature works the same way; provided above is a class with various settings for defining the behaviors of the drop.  Below is some more information about the settings for drag/drop

http://docs.jquery.com/UI/Draggable
http://docs.jquery.com/UI/Droppable

Notice the drop method.  The drop method is fired when the element is dropped; $(this) references the drop div, which appends the text "Dropped" in its body as a sign it received the drop.  For more of a demo, check out this:  http://jqueryui.com/demos/droppable/

Posted by bmains | 1 comment(s)
Filed under: ,

JQuery is a powerful scripting language that allows you to match HTML elements pretty simplistically.  For instance, JQuery can perform bulk operations against a range of objects.  JQuery perform statements that match elements in a variety of ways.  For instance, the following statements can match elements as shown below:

$("p") //selects any P html element
$("#pid") //selects an element using the ID of an element
$(".pclass") //selects elements using a specific CSS class

JQuery can use the ">" character to navigate through a hierarchy of elements.  Let's take a look at an example.  Below is a simple table structure.

<table id="tbl">
 <thead>
  <tr>
   <th>Name</th>
   <th>City</th>
   <th>State</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>Brian</td>
   <td>Columbus</td>
   <td>OH</td>
  </tr>
  <tr>
   <td colspan="3">
    Some more information
   </td>
  </tr>
  <tr>
   <td>Jason</td>
   <td>Iving</td>
   <td>TX</td>
  </tr>
  <tr>
   <td colspan="3">
    Some more information
   </td>
  </tr>
  <tr>
   <td>Susan</td>
   <td>Redmond</td>
   <td>WA</td>
  </tr>
  <tr>
   <td colspan="3">
    Some more information
   </td>
  </tr>
 </tbody>
</table>

I want to use JQuery to do a few things.  First off, every odd row (with the row span of 3) will be hidden upon load.  When the even rows are clicked, a click event will run and show the odd rows.  This is sort of a toggling effect, though I didn't build in toggling yet.

<script src="http://code.jquery.com/jquery-latest.js"></script>

<script type="text/javascript">
 $(document).ready(function() {
  $("#tbl > tbody > tr:odd").hide();
  $("#tbl > tbody > tr:even").click(function(e) {
   var table = document.getElementById("tbl");
   
   table.rows[this.rowIndex + 1].style.display = "block";
  });
 });
</script>

In the example above, the latest code can be found on code.jquery.com.  JQuery uses the $(document) to hook up to the document, which it defines a ready event.  Ready fires when the document is loaded.  Upon loading, a table with the ID of "tbl" is located.  I want to affect all of the child TR rows, but I want to avoid the THEAD element, which is why I navigate to the TBODY.  The odd rows, using the :odd pseudo statement is used to hide the rows with a rowspan="3".  Even rows tap into a click event, showing the row.

This is one way to use JQuery scripting.

Posted by bmains | with no comments
Filed under: ,

I got a question about a blog post with using multiple ValidationSummary controls and multiple validation groups, and I think my original post is unclear.  Suppose you have a form that uses two validation groups: Account and Profile.  In order to validate the account portion of the form, ensure all  TextBoxes, Validation controls, and the ValidationSummary has this value supplied, and the same goes for the profile. This would look like:

<asp:ValidationSummary id="vs" runat="server" ValidationGroup="Account" />

<asp:TextBox id="user" runat="server" ValidationGroup="Account" />
<asp:requiredfieldvalidator id="userval" runat="server" ValidationGroup="Account" .. />
.
.

So all of the validators are linked to this validation summary.  Unfortunately, the ValidationSummary control only appears for validators within the same validation group.  Note that specifying no validation group is also a value, and only validators with no validation summary get validated.  So this would validate together correctly.

<asp:ValidationSummary id="vs" runat="server" />

<asp:TextBox id="user" runat="server" />
<asp:requiredfieldvalidator id="userval" runat="server" .. />

But anything within the Account val group won't appear in this validation summary.  So the ValidationSummary control is limited this way, an unfortunate aspect of the control.

Now, if there are multiple validation groups, that works OK.  To trigger validation on the back-end, you can use:

Page.Validate("Account");

To validate that validation group.  Use no parameters to validate all validation groups, and use:

Page.Validate(null);

To validate only the default validation group, where no ValidationGroup property is supplied.  A button can also trigger this by defining it's validation group, and not specifying the CausesValidation="false" declaration as such:

<asp:Button id="btnSave" runat="server" Text="Save" ValidationGroup="Account" />

Posted by bmains | 2 comment(s)
Filed under:

Since .NET 2.0 and Visual Studio 2005, the ability to encrypt the config file has been possible.  This functionality is built-in to the aspnet_regiis.exe utility.  This executable uses flags to designate a few items:

-pe for encrypting web projects registered with IIS
-pd for decrypting web projects registered with IIS
-pef for encrypting web projects using the Cassini (local) web server
-pdf for decrypting web projects using the Cassini (local) web server
-app for specifying the IIS registered virtual

To encrypt the connection strings settings, use the following approach:

aspnet_regiis -pe "connectionStrings" -app "/Virtual"
aspnet_regiis -pef "connectionStrings" "c:\websites\virtual"

To decrypt, use the following:

aspnet_regiis -pd "connectionStrings" -app "/Virtual"
aspnet_regiis -pdf "connectionStrings" "c:\websites\virtual"

This can be run even when Visual Studio is open; you'll be prompted that the file changed and do you want to reload it.

The difficult aspect to this is that the account that encrypts the password has to decrypt it, or you will get the "unable to descrypt connection strings error."  Obviously, this is impractical.  The way to get get around this is to add the account that will run the ASP.NET application (for IIS 5.1 on XP, this is the ASPNET account, but in Windows Server 2003 and IIS 6, it's the NETWORKSERVICE account).  To grant permissions for these accounts to decrypt the connection string in the application, use:

aspnet_regiis -pa "NetFrameworkConfigurationKey" "NT AUTHORITY\NETWORK SERVICE"

See more about this at:  http://msdn.microsoft.com/en-us/library/yxw286t2.aspx.  This grants the account permissions, and the automatic decryption can works successfully.

You do not need to write any code to decrypt the values; these values are automatically decrypted, with no apparent performance issues.

Posted by bmains | 2 comment(s)