Jim Rogers

Lives in Baton Rouge, LA, with two dogs, one cat, and one lovely wife. I'm a lead developer for GCR Incorporated.

Katrin and Jim

Month List

Programmatically Adding a User Control in ASP.NET

by jim May 24, 2012 3:49 PM

At first, this appears to work (don’t do this!)

<% If Me.Voter.VoterType <> Voter.VoterLoginType.Parish Then%>
    <uc:BallotByVoter ID="bcVoter" runat="server" />
<%Else%>
    <uc:BallotByRegion ID="bcRegion" runat="server" />            
<%End If%>

The drawback to this technique is that both controls are created and run through the page lifecycle – at least up to the Page_Load.  This may not be desirable if they hit the database to populate dropdowns, or perform other tasks when loaded.

The solution to this is create a placeholder on the page:

<asp:PlaceHolder runat="server" ID="plhChooser"></asp:PlaceHolder>

And programmatically load the controls and place them on the form:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Dim chooser As Control
    If Me.Voter.VoterType <> Voter.VoterLoginType.Parish Then
        chooser = LoadControl("BallotByVoter.ascx")
    Else
        chooser = LoadControl("BallotByRegion.ascx")
    End If
    AddHandler CType(chooser, BallotChooser).BallotChosen, AddressOf BallotChosen
    plhChooser.Controls.Add(chooser)
End Sub

This works fine for me with pre-populated dropdowns, and across postbacks. Note the use of LoadControl(), rather than just new-ing up the controls; this parses the file and creates the controls within your user control.

Tags:

Code

ASP Databinding Issues

by jim Oct 05, 2010 1:48 PM

There are a number of solutions out there for the error “Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.”

I fought with my bug for a while because the problem isn’t with an explicit call to any of these methods; it was with a ControlParameter on my ObjectDataSource.

<asp:DropDownList ID="cboBatchList" runat="server" 
    AppendDataBoundItems="true" 
    DataSourceID="srcBatchList" 
    DataTextField="Display" 
    DataValueField="ID" 
    SelectedValue='<%# Eval("BatchID") %>'>
    <asp:ListItem Text="Select a batch" Value="" />
</asp:DropDownList>                                                    
<asp:ObjectDataSource ID="srcBatchList" runat="server" 
    EnableViewState="false"
    OldValuesParameterFormatString="original_{0}" 
    SelectMethod="Select" 
    OnObjectCreating="TextListDataSource_ObjectCreating"
    TypeName="MyProject.Service.Reference.TextListService">
    <SelectParameters>
        <asp:Parameter Name="listName" DefaultValue="Batch" Type="String" />
        <asp:ControlParameter ControlID="frmLine" DefaultValue="" 
            Name="selectedID" PropertyName="DataKey['BatchID']" Type="UInt32" />
    </SelectParameters>
</asp:ObjectDataSource>

The Eval() in the DropDownList is fine (even a Bind will work,) but the control parameter reaches up into the containing form to get a property, and this fails. Presumably this uses the same methods internally that a call to Eval does, and therefore gives the same error message – which is a bit confusing in this context.

The fix is to set EnableViewState=”true” on the ObjectDataSource, which will then cache the list and not attempt to re-evaluate the control parameter on postbacks.

Tags:

Code

Testing with WindowsTokenRoleProvider

by jim Apr 06, 2010 8:38 AM

For the second time recently, I’m working on an ASP.NET website that uses windows authentication, and gets roles from Active Directory (AD) using WindowsTokenRoleProvider. The roles correspond to the AD groups that the logged-in user is a member of.

I don’t have permission to modify AD group membership, so how do I test my application’s role and permission code? Do I ask our IT guy to swap me in and out of groups while I’m testing?

clip_image001

Yeah, that’s not going to happen.

My solution to this problem is to use a different provider for the roles; the role provider and the membership provider don’t need to be a matched pair. So I can continue to use windows authentication to verify membership (all domain logins are associated with the application, by default.) But I get roles from an XML file, making them easy to change.

How to set it up:

Microsoft has a description of role providers with an example, the ReadOnlyXmlRoleProvider, which is perfect for our purposes. That sample was for IIS6. If you’re using IIS7 on Vista or Windows 7, the provider must be in the Global Assembly Cache. You can find detailed instructions for creating and registering the provider for IIS7 here. We only need the role provider, but the membership provider might be useful in other scenarios. Be careful about that PublicKeyToken when adding the provider to web.config.

Once the provider is installed and configured, just add the desired windows accounts to Users.xml, in the App_Data folder.

<Users>
  <User>
    <UserName>MYDOMAIN\jsmith</UserName>
    <Password>boo</Password>    <!-- doesn't matter what's here -->
    <EMail>jsmith@mycompany.com</EMail>
    <Roles>Admin,SuperGuy,Etcetera</Roles>
  </User>
</Users>

Now we can access roles in the usual way, without having to bug IT…

string[] userRoles = ((RolePrincipal)User).GetRoles();
            
bool inRole = User.IsInRole(SomeRole);

In production, I just change the web.config to use WindowsTokenRoleProvider rather than my ReadOnlyXmlRoleProvider; no recompiling is necessary.

Tags: ,

Code

Redirecting to posts in BlogEngine

by Jim Feb 15, 2010 9:38 PM

Since I’ve moved my blog from Blogger to BlogEngine, the URLs have of course changed. The old posts were .html files, so my asp.net application doesn’t process them, the and resulting 404 error falls to Brinkster (my host) to handle.

I can configure my Brinkster account to load BlogEngine’s Error404.aspx page in these cases, and the following code will successfully redirect those requests to the new BlogEngine post.

Blogger and BlogEngine both follow the convention of naming a post with the title, replacing spaces with commas.

// Jim: check for brinkster 404 redirect query string
// The query string is not name=value, it's this:
// 404;http://www.jimandkatrin.com:80/CodeBlog/2009/12/some-thoughts-on-code-reviews.html

string reg = @"^404;http://www.jimandkatrin.com:80/CodeBlog/[0-9]{4}/[0-9]{2}/(.*)\.html$";
string qs = Server.UrlDecode(Request.QueryString.ToString()); // raw query string
if (!string.IsNullOrEmpty(qs) &&
  System.Text.RegularExpressions.Regex.IsMatch(qs, reg,
    System.Text.RegularExpressions.RegexOptions.IgnoreCase))
{
  // Get the match, and construct a new URL out of it, in the BlogEngine format:
  System.Text.RegularExpressions.Match match =
  System.Text.RegularExpressions.Regex.Match(qs, reg);
  string url = string.Format("~/post/{0}.aspx", match.Groups[1]);

  Response.Redirect(url);
  Response.End();
  return;
}

// Jim: Set response code so that redirects from Brinkster 404 will actually 
// return the response code 404
Response.StatusCode = 404;
Response.Status = "404 Not Found";

The status code at the end causes the page to actually return a 404 status code, rather than a 200 OK, when Brinkster redirects to this page.

SOAP vs. HTTP Post Serialization

by Jim Dec 15, 2009 12:29 PM

We recently got bitten by a tricky bug in our web service (asmx.) One of the returned classes had a setter marked as internal, and the serialization failed for an HTTP Post request.

Now you’re thinking, “What’s tricky about that? XML serialization doesn’t work with readonly properties.”

But you would be wrong. The service passed all of our automated tests just fine. These were Visual Studio web tests, set up by default to send SOAP requests. When SOAP headers are sent, the SoapServerProtocol happily uses XMLSerializer to serialize the return value, complete with internal or private properties.

I’ll jump in here with the moral of the story: if you want to ensure that serialization will work regardless of how your service is called, then it should be tested with both SOAP and Post requests – or perhaps just Post, as this is the more restrictive.

Another option, if you aren't doing integration tests, is to add unit tests which simply serialize the service's arguments and return values. These should catch any new or changed properties which break serialization - assuming that they're kept up to date with the method signatures.

The explanation is more complicated. Two different ServerProtocol-derived classes handle these requests: HTTPServerProtocol and SOAPServerProtocol. They both use XMLSerializer.FromMappings() to create the XMLSerializer which handles the return value. This method is “not intended to be used directly from your code,” according to MSDN.

I attempted to use the Microsoft symbol server to step through the creation of the serializers, but it didn’t want to give me the appropriate web service classes. And .NET Reflector got tiring before I tracked down exactly what was going on. But somewhere in there is the capability to construct and use XMLSerializer in such a way as to serialize readonly properties – something that isn’t possible with the methods intended for public consumption.

Tags: ,

Code