Practical .NET

Batting Clean up with Reader Questions

Peter Vogel answers reader questions, including extending custom sections in the config file, using CreateQuery and ESQL in the Entity Framework, and reading lambda expressions.

Here are the answers to some questions I've gathered from readers (some triggered by previous columns), from my clients and from participants in classes that I've taught.

Q. I read your online column about adding custom sections to my config file.

Your design didn't include <add> elements, which I need so I can do something like this:

<ajaxSettings>
  <wcfUrls>
    <add name="inventory" url="..."/>
    <add name="customer" url="..."/>
  </wcfUrls>
</ajaxSettings>

I also want to ensure no two add elements have the same value for the name element so I retrieve items by name from code. Is this possible?

A. Yes, it is. You must tell .NET about your new ajaxSettings element by putting an entry in your config file configSections element:

<configSections>
  <section name="ajaxSettings" 
            type="CustomConfigurations.ajaxConfig"/>

For your nested add elements, you need a class that corresponds to those elements (this class must inherit from ConfigurationElement). You define the attributes on your add element (in this case, the name and url attributes) by defining properties within the class and decorating those classes with the ConfigurationProperty attribute. Because you want the name property to uniquely identify each wcfUrl element, you should also set to True the IsKey property on the ConfigurationProperty attribute that decorates the name property:

Public Class wcfUrlConfig
  Inherits ConfigurationElement

<ConfigurationProperty("name", 
  IsRequired:=True, IsKey:=True)> _
Public ReadOnly Property name() As String
  Get
    Return Me.Item("name").ToString
  End Get
End Property

<ConfigurationProperty("url", IsRequired:=True)> _
Public ReadOnly Property url() As String
  Get
    Return Me.Item("url").ToString
  End Get
End Property

Now you have to create a class that inherits from Configuration-ElementCollection to manage the collection:

Public Class wcfUrlConfigs
  Inherits ConfigurationElementCollection

If you don't intend to update the collection from code, you only need to override two methods in this collection class: CreateNew-Element and GetElementKey. Both methods contain "almost boilerplate" code -- the only difference from one copy of these methods to another is the name of your class and the name of your unique identifier property. The CreateNewElement method must return a new instance of the add element class (wcfUrlConfig), and GetElementKey must return the value of the identifier attribute (in this case, name) of the element passed to it:

Protected Overloads Overrides Function CreateNewElement() 
  As System.Configuration.ConfigurationElement
  Return New wcfUrlConfig
End Function

Protected Overrides Function GetElementKey( 
  ByVal element As System.Configuration.ConfigurationElement) 
    As Object
  Return CType(element, wcfUrlConfig).name
End Function
You'll also need to add an Item method so that you can retrieve elements from code. This code is also almost boilerplate -- you just need to specify the type of your ConfigurationElement class:
Default Public Shadows ReadOnly Property Item( 
  ByVal Name As String) As wcfUrlConfig
  Get
    Return CType(BaseGet(Name), wcfUrlConfig)
  End Get
End Property

Back when you set up your ajaxSettings element in the config file, you specified the class that supported that element (CustomConfigurations.ajaxConfig). I covered that class in detail in the original column, but now you must add a property to it that returns your collection class (this property also needs the ConfigurationProperty attribute). Again, the code for this property is almost boilerplate, except for the name of your section and the type of the class. Here's your class with the property to support your add elements:

Imports Microsoft.VisualBasic
Imports System.Configuration
Imports System.Configuration.ConfigurationManager

Public Class ajaxConfig
  Inherits ConfigurationSection

<ConfigurationProperty("wcfUrls")> 
Public ReadOnly Property wcfUrls() As wcfUrlConfigs
  Get
    Return CType(Me.Item("wcfUrls"), wcfUrlConfigs)
  End Get
End Property

In code, to retrieve a wcfUrls entry that has its name attribute set to "inventory," you'd use server-side code like this:

Dim ajaxSetting As CustomConfigurations.ajaxConfig
Dim url As String

ajaxSetting = CustomConfigurations.ajaxConfig.GetConfig
url = ajaxSetting.wcfUrls("inventory").url

Q. Were lambda expressions designed to be hard to read?

A. Yes. Well, actually, lambda expressions are methods created for people who don't like writing code.

We're all used to passing method parameters that include both literals and variables (including object references). Some developers are even used to passing delegates (references to methods) as parameters. Lambda expressions let you pass a method as a parameter.

However, seeing the relationship between a method and a lambda expression isn't obvious. Here's a typical method:

Public Function MyFunction(InputParm As String) As Boolean
  Return Result
End Function

public bool MyFunction(string InputParm)
{
  return result;
}

If you're passing this as a lambda expression to a method, there's a bunch of stuff you don't need and, as a result, can omit. You don't need the scope ("Public") because parameters are always local. You don't need a name ("MyFunction") because you'll never refer to this method from any other place. Because the last line of a function always begins with the Return keyword, you can omit that also (it's assumed). You don't really need the "End Function" or braces because the parentheses and commas used in passing parameters will mark the end of the method. So you're left with this:

Function (InputParm As String) As Boolean
  Result

bool (string InputParm)
  result;

But wait, there's more. The method that accepts a lambda expression as a parameter will have defined the parameter it expects (you can see it in IntelliSense). That definition specifies two key things: the data type of what will be passed to the parameter in the lambda expression and the data type that is to be returned from the parameter. Because the method knows the parameter's definition, the compiler knows it also. So, thanks to implied declarations, you can omit specifying those data types -- the compiler will fill in the blanks. That leaves just this as a lambda expression:

Function (InputParm) Result

(InputParm) result;

That was a little too terse for the C# team, so they inserted some punctuation between the parameter and the body of the method. But, to compensate, you get to omit the semicolon and, if you only have one parameter, you can also omit the parentheses around the parameters. In C#, a lambda expression can be as short as this:

InputParm => result

So, from this terse code, how do you figure out what's being passed to your method's parameter and what your method should return? For that, you'll have to read the documentation of the method that accepts the lambda expression as a parameter. However, you'll probably most often encounter lambda expressions as parameters being passed to extension methods that attach themselves to collections. In that case, the input parameter is usually a member of the collection that the extension method is attached to. After that, you're on your own.

Q. I read your Web column on issuing SQL in the Entity Framework. I get the ExecuteStore* methods, but what is the CreateQuery method for? Doesn't it, well, create a query also?

A. You're right ... and wrong. Like ExecuteStoreQuery, the CreateQuery method is used to retrieve entity objects when passed a query as a text string. The CreateQuery adds one more step: While the ExecuteStoreQuery just returns the entities when called, the CreateQuery method returns an ObjectQuery. You then execute the ObjectQuery as often as you need to get back a collection of entity objects from your Entity Framework.

Unfortunately, unlike the ExecuteStore* methods, you don't use SQL with CreateQuery -- you use Entity SQL (ESQL), which isn't quite the same thing. However, using ESQL does allow you to work with the types defined in your Entity Framework model, leveraging some of the functionality of the Entity Framework. Because ESQL is vendor-neutral, your code will work with any database engine (unlike SQL). And, like generating SQL in code, you can generate ESQL in code, which allows you to create very dynamic applications. The ability to pass a predefined ObjectQuery around is also a nice feature.

This example uses CreateQuery and an ESQL query to return all of the columns from the Customers table in the Northwind database:

using System.Data.Objects;
using northwndModel;

northwndEntities ne = new northwndEntities();
ObjectQuery<Customer> qury = 
  ne.CreateQuery<Customer>(
    "Select value c from Customers As c");

Entity SQL doesn't support "*" as a way of specifying all columns, which is why I'm using the "value C" syntax you see. And, with ESQL, you don't think in terms of Joins. Instead, you think in terms of navigating through the relationships embedded in your Entity Framework model.

Having created an ObjectQuery containing the ESQL query, you can retrieve data just by calling the ObjectQuery's Execute method, passing a value from the MergeOption enumeration. In this example I'm using the AppendOnly option that causes Entity Framework to hand me any objects sitting in the cache that it retrieved as part of previous calls before it goes to the database server for any new objects:

var res = qury.Execute(MergeOption.AppendOnly);

Once I've retrieved the objects I asked for, I can then process the objects (using LINQ if I wish), update them and save my changes using Entity Framework. Here, I'm just using a foreach loop:

foreach (Customer r in res)
{
  r.City = "Goderich";
}
ne.SaveChanges();

As long as we're on the topic, if you want more flexibility, you can also use ESQL with EntityConnection, EntityCommand and EntityDataReader objects retrieved from your Entity Framework model. The benefit here is that these classes are similar to the ADO.NET classes you're used to. If you want to use them, save yourself some typing by first adding these directives to your code:

using System.Data.EntityClient;
using System.Data;
Now you can take advantage of some ADO.NET-like objects:
northwndEntities ne = new northwndEntities();
EntityConnection econn = (EntityConnection)ne.Connection;
EntityCommand ecmd = econn.CreateCommand();
ecmd.CommandText = 
  "Select value c from northwndEntities.Customers as c;";
econn.Open();
EntityDataReader rdr = 
  ecmd.ExecuteReader(
    System.Data.CommandBehavior.SequentialAccess);
        
string City; 
while (rdr.Read())
{
  City = rdr["City"].ToString();
}
econn.Close();

These classes support data-retrieval scenarios only; however, there's no ExecuteNonQuery option. There's also no EntityDataAdapter, so you can't pour your results into a DataSet. You'll still need to use the Entity Framework to do your updates.

Q. In your Language Lab column on speeding up data access, you mentioned asynchronous ADO.NET as a way of creating the appearance of a faster application. How does that work?

A. It's simple. For instance, for an asynchronous update, instead of calling the SqlCommand object ExecuteNonQuery method, you call its BeginExecuteNonQuery method, passing an AsyncCallback object. That AsyncCallback points to some other method in your application:

Private cmd As SqlCommand
Public Sub DoAsynchronousUpdate
Dim con As New SqlConnection("...")
Dim cmd = con.CreateCommand
Dim finish As New AsyncCallback(AddressOf handleFinish)
  
 cmd.BeginExecuteNonQuery(finish, Nothing)

End Function

The method specified in the AsyncCallback object will be automatically called when the update finishes. That method will be passed an IAsyncresult object, which (for an update) you pass to the original SqlCommand object's EndExecuteNonQuery method. That gives you the number of rows that were touched by the query. There are a couple of ways to pass the SqlCommand object between the two methods but, in this example, I've chosen to just declare it as a field:

Private Sub handleFinish(ByVal res As IAsyncResult)
Dim rc As Integer = cmd.EndExecuteNonQuery(res)

If rc = 0 Then
  MessageBox.Show("No updates made")
End If

End Sub

If you call the BeginExecuteReader method of the SqlCommand object, then you'll want to call the EndExecuteReader method in the handleFinish method -- it will return the SqlDataReader you need to process the retrieved records. The query takes just as long as before, but the UI doesn't lock up while it's executing.

Just because the code is simple, don't think that the resulting application will be. As just one example, you'll need to ensure that your program is still executing when the handleFinish method finally gets called.

Q. Can I use the standard ASP.NET controls in an ASP.NET MVC application?

A. Well ... yes, you can ... but that doesn't mean it's a good idea.

ASP.NET MVC is all about a particular set of benefits, one of which is absolute control over your HTML. If you'd rather have your HTML generated by the control than by yourself, perhaps you should look at ASP.NET (without the Model-View-Controller, or MVC) as your development platform. For some controls -- the GridView for instance -- ASP.NET MVC 3 now includes an equivalent tool that should be your first choice. Finally, some of the third-party tool providers are also creating ASP.NET MVC tool suites that reflect the "MVC orientation" (Developer Express and Telerik both have ASP.NET MVC suites).

But I'm not here to judge (as my aunt Harriet would say after having judged someone). You can use the ASP.NET controls without violating the MVC paradigm. Using the existing ASP.NET controls would be cost effective because you've already installed them as part of .NET. As long as you stay away from anything that depends on the ViewState and don't try to initiate a postback (for example, do not set AutoPostBack to True), it's surprising -- at least, surprising to me -- how well it works.

For instance, you can add an ASP.NET TextBox to your View, either by dragging it from the Toolbox or just typing this:

<asp:TextBox ID="TextBox1" ClientIDMode="Static"
                               runat="server"/>

You can still maintain the MVC pattern by creating a controller that retrieves from the model any data to go into your ASP.NET controls. You'll want one Action method to prepare the page to go to the browser on the user's original request (decorate this method with the HttpGet attribute). You'll need a second method to process the results coming back from the browser after the user has updated the page (decorate this method with the HttpPost attribute).

The data that's returned to the server will be passed to that second Action method as a FormCollection object -- a dictionary of name/value pairs with one entry for each control in the View. Putting all that together, a Controller to work with the TextBox might look like the following (the HttpGet method retrieves a Customer object from the model and passes it to the view; the HttpPost method retrieves whatever the user entered into TextBox1 when it was displayed in the browser):

<HttpGet()>
Function Index() As ActionResult
  Dim cust As New Customer("A123")
  Return View("ASPNETControls", cust)
End Function

<HttpPost()>
Function Index(data As FormCollection) As ActionResult
  Dim fname As String
  fname = data("TextBox1")
End Function

I'm assuming that you're not using a View Master Page. If you are, then ASP.NET will modify the id attributes of your controls. You'll need to use those modified values when retrieving the TextBox value from FormCollection. For non-repeating controls, just set the control's ClientIdMode to Static. For repeating controls (such as a GridView) the simplest solution is to test the page in a browser and use View Source to determine the modified names being generated.

Back in the View, you can still tie the View to the object being passed to it (in this case, a Customer object) in the View Page directive:

<%@ Page Language="VB" Inherits=
  "System.Web.Mvc.ViewPage(Of List(of Customer))" %>

You can then add some code to your View to set the TextBox Text property:

<% Me.TextBox1.Text = Me.Model.FirstName %>

You can also databind the TextBox, as I'll show later on in this answer.

Using a GridView is a little more complicated. If you haven't upgraded to ASP.NET MVC, with its native grid control, it's an upgrade worth considering (in fact, I'll be looking at the control in an online column next month). However, if you do want to use the ASP.NET GridView, you can do it and -- amazingly! -- still honor the MVC paradigm.

This example of a Controller retrieves all the customers from the model in order to display them in a GridView:

<HttpGet()>
Function Index() As ActionResult
  Dim custs As New List(Of Customer)
  Dim custModel As New CustomerModel
  custs = custModel.GetAll()
  Return View("ASPNETControls", custs)
End Function

In the View, you'll want to tie the View to the List being passed to it:

<%@ Page Language="VB" Inherits=
  "System.Web.Mvc.ViewPage(Of List(of Customer))" %>

Then it's a matter of adding the GridView to your view. You can do that in Design view by dragging the GridView onto the page from the Toolbox. Eventually, however, you'll get a message that Visual Studio is going to convert the page into an ASP.NET page. At that point, it's time to switch to Source view and start typing.

If all you want is to have your GridView display data, you do that with this element and some code (the code must precede the asp:GridView element):

<% Me.GridView1.DataSource = Me.Model
   Me.GridView1.DataBind() %>

<asp:GridView AutoGenerateColumns="true" ID="GridView1" 
                runat="server">
</asp:GridView>

But if you want to accept user input from your GridView, then you'll need to enhance your GridView element. Remember that, without violating the MVC paradigm, you shouldn't let the controls do their own postbacks. That means you won't be able to take advantage of the GridView's ability to switch between edit and display mode on the fly. Instead, to have your GridView accept inputs, you'll have to explicitly insert ItemTemplates into the GridView in your View. Within the ItemTemplates you can add input controls (like a TextBox) and bind those input controls to the property you want to display.

This example creates a GridView with two columns. The first displays the FirstName property from the current Customer object, while the second column displays the BirthDate property in a TextBox so that the user can change it:

<asp:GridView ID="GridView1" ClientIDMode="Predictable" 
                                  runat="server">
  <Columns>
    <asp:BoundField DataField="FirstName" 
                       DataFormatString="{0:d}" 
                       HeaderText="First Name" />
    <asp:TemplateField>
      <ItemTemplate>
        <asp:TextBox ID="BirthhDate" 
                       Text='<%# Bind("BirthDate") %>' 
                       runat="server">
        </asp:TextBox>
      </ItemTemplate>
    </asp:TemplateField>
  </Columns>
</asp:GridView>

For the HttpPost Action method that accepts the user input, the FormCollection parameter will contain one entry for each updatable element in each row. For the GridView in this example, that means there will be one entry in the FormCollection for the TextBox, repeated for every row. ASP.NET MVC will generate a unique name for those TextBoxes, but by setting the GridView ClientIdMode to Predictable, you get some control over how those names are generated. Still, it may make sense to use a set of nested For...Next loops and retrieve items from the FormCollection by position rather than by name when processing the data being returned.

Q. I'm using a tool that generates some code for me. It generates partial classes so I can extend the class with my own code. But I want to add some attributes to the methods and properties that are created by the code generator. If I add the attributes to my generated code, they just get wiped out the next time my code is generated (which seems to happen a lot). Is there any way around this?

A. There is. I'm going to assume that your tool has generated a class like this:

Partial Public Class Order
Private _OrderId As String
    
Public Property OrderId() As String
  Get
    Return _OrderId
  End Get
  Set(value As String)
    _OrderId = value
  End Set
End Property

You've decided to extend the class by creating your own public partial class with an additional property:

Partial Public Class Order
Private _OrderStatus As String

Public Property OrderStatus As Integer
  Get
    Return _OrderStatus
  End Get
  Set(value As Integer)
    _OrderStatus = value
  End Set
End Property

You haven't said what attributes you want to add. Because I just finished an ASP.NET MVC question, I'll assume that you want to add one of the DataAnnotations that allow you to centralize validation processing. This example makes your OrderStatus property a required field:

<System.ComponentModel.DataAnnotations.Required(
  ErrorMessage:="Order Status is required")>
Public Property OrderStatus As Integer

The question is, how do you apply that DataAnnotation to the auto-generated OrderId property? The answer is to create another class that includes your attributes and tie it to your partial class.

The first step is to decorate your partial class with the MetadataType attribute that points to the class that will contain your attributes. This example ties the Order partial class from the previous example to a class called OrderWithAttributes:

<System.ComponentModel.DataAnnotations.MetadataType(
  GetType(OrderWithAttributes))>
Partial Public Class Customer
  Private _BirthDate As String
  <System.ComponentModel.DataAnnotations.Required(
    ErrorMessage:="Birth Date is required")>
  Public Property BirthDate As DateTime

Now, in the OrderWithAttributes class, I add the OrderId property and apply any attributes that I want (this class doesn't have to be a partial class):

Public Class CustomerWithAttributes

  <System.ComponentModel.DataAnnotations.Required(
    ErrorMessage:="Birth Date is required")>
  Public Property FirstName As String
End Class

When the Order class is assembled from all of its constituent parts, the OrderId property generated by your tool will acquire the DataAnnotation specified in the CustomerWithAttributes class.

Q. In your "WCF and Service-Oriented Architectures" article in June, you mentioned creating services that support response times measured in minutes, hours or days by using Windows Workflow Foundation (WF). But you seemed to assume that only WF developers would be interested. I'm interested. How does that work?

A. That requires a longer answer than I have room for in this column -- tune in next month and I'll show a complete application that supports a couple of typical scenarios for long-running services. However, I can say right now that, if you have .NET, Visual Studio and IIS installed, then you have all the technology you need.

comments powered by Disqus

Featured

Subscribe on YouTube