Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

Archive for Lambda Expressions

March 3, 2010

Lambda Expressions: Join

Filed under: C#,Lambda Expressions,VB.NET @ 12:24 am

If you have a set of business objects that are related to another set of business objects by a foreign key, you can use the Join method in a Lambda expression to join the two sets of business objects into one. You can then use the resulting set as a single list. This is useful when you need to perform operations on a single  list, like for binding.

Say you have a Customer class and a Contact class. The Customer class defines your set of customers. The Contact class tracks the contacts (email, text, or phone messages) from those customers. So the Contact class has a CustomerId foreign key that maps the message to the associated customer.

Here is the Customer class.

In C#:

public class Customer
{
    public int CustomerId { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string EmailAddress { get; set; }
}

In VB 10 (VS 2010):

Public Class Customer
    Public Property CustomerId As Integer
    Public Property FirstName() As String
    Public Property LastName() As String
    Public Property EmailAddress() As String
End Class

In VB 9 (VS 2008):

Public Class Customer

    Private _CustomerId As Integer
    Public Property CustomerId() As Integer
        Get
            Return _CustomerId
        End Get
        Set(ByVal value As Integer)
            _CustomerId = value
        End Set
    End Property

    Private _FirstName As String
    Public Property FirstName() As String
        Get
            Return _FirstName
        End Get
        Set(ByVal value As String)
            _FirstName = value
        End Set
    End Property

    Private _LastName As String   
    Public Property LastName() As String
        Get
            Return _LastName
        End Get
        Set(ByVal value As String)
            _LastName = value
        End Set
    End Property

    Private _EmailAddress As String
    Public Property EmailAddress () As String
        Get
            Return _EmailAddress
        End Get
        Set(ByVal value As String)
            _EmailAddress = value
        End Set
    End Property
End Class

And the Contact class:

In C#:

public class Contact
{
    public int ContactId { get; set; }
    public int CustomerId { get; set; }
    public string MessageText { get; set; }
}

In VB 10 (VS 2010):

Public Class Contact
    Public Property ContactId As Integer
    Public Property CustomerId() As Integer
    Public Property MessageText() As String
End Class

In VB 9 (VS 2008):

Public Class Contact
    Private _ContactId As Integer
    Public Property ContactId() As Integer
        Get
            Return _ContactId
        End Get
        Set(ByVal value As Integer)
            _ContactId = value
        End Set
    End Property
    Private _CustomerId As Integer
    Public Property CustomerId() As Integer
        Get
            Return _CustomerId
        End Get
        Set(ByVal value As Integer)
            _CustomerId = value
        End Set
    End Property
    Private _MessageText As String
    Public Property MessageText() As String
        Get
            Return _MessageText
        End Get
        Set(ByVal value As String)
            _MessageText = value
        End Set
    End Property
End Class

Then you populate the list of business objects, probably from a table. In this example the lists are hard-coded so you don’t have to hook up a database.

In C#:

List<Customer> custList = new List<Customer>
                    {new Customer()
                          { CustomerId = 1,
                            FirstName="Bilbo",
                            LastName = "Baggins",
                            EmailAddress = "bb@hob.me"},
                    new Customer()
                          { CustomerId = 2,
                            FirstName="Frodo",
                            LastName = "Baggins",
                            EmailAddress = "fb@hob.me"},
                    new Customer()
                          { CustomerId = 3,
                            FirstName="Samwise",
                            LastName = "Gamgee",
                            EmailAddress = "sg@hob.me"},
                    new Customer()
                          { CustomerId = 4,
                            FirstName="Rosie",
                            LastName = "Cotton",
                            EmailAddress = "
rc@hob.me"}};

List<Contact> contactList = new List<Contact>
        {new Contact()
              { ContactId = 1,
                CustomerId = 1, 
     MessageText="Please provide me with the status of my order."},
         new Contact()
              { ContactId = 2,
                CustomerId = 1,
                MessageText="Can I get the order by Friday?"},
         new Contact()
              { ContactId = 3,
                CustomerId = 2,
                MessageText="Follow up on Order # 2355"}};

In VB:

Dim custList As New List(Of Customer)
custList.Add(New Customer With {.CustomerId = 1, _
                                .LastName = "Baggins", _
                                .FirstName = "Bilbo", _
                                .EmailAddress="
bb@hob.me"})
custList.Add(New Customer With {.CustomerId = 2, _
                                .LastName = "Baggins", _
                                .FirstName = "Frodo", _
                                .EmailAddress = "
fb@hob.me"})
custList.Add(New Customer With {.CustomerId = 3, _
                                .LastName = "Gamgee", _
                                .FirstName = "Samwise", _
                                .EmailAddress = "
sg@hob.me"})
custList.Add(New Customer With {.CustomerId = 4, _
                                .LastName = "Cotton", _
                                .FirstName = "Rosie", _
                                .EmailAddress = "
rc@hob.me"})

Dim contactList As New List(Of Contact)
contactList.Add(New Contact With _
                  {.ContactId = 1, _
                    .CustomerId = 1, _
   .MessageText = "Please provide me with the status of my order."})
contactList.Add(New Contact With _
                  {.ContactId = 2, _
                    .CustomerId = 1, _
                    .MessageText = "Can I get the order by Friday?"})
contactList.Add(New Contact With _
                  {.ContactId = 3, _
                    .CustomerId = 2, _
                    .MessageText = "Follow up on Order # 2355"})

To perform the join, use the Join method from the Enumerable class:

In C#:

var contactJoin = contactList.Join(custList,
                    msg => msg.CustomerId,
                    cust => cust.CustomerId,
                    (msg, cust ) =>
                        new {LastName = cust.LastName,
                            FirstName = cust.FirstName,
                            Message = msg.MessageText });

In VB:

Dim contactJoin = contactList.Join(custList, _
               Function(msg) msg.CustomerId, _
               Function(cust) cust.CustomerId, _
               Function(msg, cust) _
                    New With {.LastName = cust.LastName, _
                        .FirstName = cust.FirstName, _
                        .Message = msg.MessageText})

In this example, the desired result is a list of contact messages but with the CustomerId replaced with the first and last name of the customer.

Notice in the above code  that the Join method is used on the contactList. This ensures that all contact messages are in the list along with their matching customers. This is called the outer list.

The first parameter to the Join method is the inner list. This is the list joined to the first (outer) list. In this case, it is custList to join the appropriate customers to their contact messages.

The joining is done using keys. So the next two parameters define the two keys. In this example, the name of the keys are the same: CustomerId.

So, the second parameter of the Join method defines the outer list’s key. In this case it is the key in Contact class that is used for the join.

The third parameter of the Join method defines the inner list’s key. In this example, it is the key in the Customer class that is used for the join.

The fourth parameter defines the values to return. In this example, the code is creating an anonymous type. The anonymous type has the contact message along with the customer’s last and first name.

You can then bind the result to a DataGridView or other control as follows.

In C#:

CustomersDataGridView.DataSource = contactJoin.ToList();

In VB:

CustomersDataGridView.DataSource = contactJoin.ToList()

And the result appears as shown below.

image

Use this technique any time you want to join two related lists.

Enjoy!

EDITED 5/2/2010: Added the VB 10 code to demonstrate the new auto-implemented properties in VB 10.

February 27, 2010

Binding to a ComboBox using a DataTable and Linq

If you retrieve data into a DataTable, it is easy to bind it to a ComboBox. And before Linq, you would filter the DataTable using a DataView. But with Linq, you have some easy to use features for filtering the contents of your ComboBox.

First, here is the basic code for binding a ComboBox to a DataTable.

In C#:

private DataTable dt = Customers.Retrieve();

ComboBox1.DataSource = dt;
ComboBox1.DisplayMember = "FullName";
ComboBox1.ValueMember = "CustomerId";

In VB:

Private dt As DataTable = Customers.Retrieve

ComboBox1.DataSource = dt
ComboBox1.DisplayMember = "FullName"
ComboBox1.ValueMember = "CustomerId"

The Retrieve method on the Customers class retrieves a DataTable of customers. If you want to try this code, you can build a DataTable in code following the techniques covered here.

You can then set the DataSource to the DataTable, set the DisplayMember to the name of the field to display in the ComboBox, and set the ValueMember to the name of the field to use as the field value. This is most often the unique key.

The result looks like this:

image

With Linq you can add filtering criteria. So let’s add a second ComboBox that lists only the customers with a last name that starts with "B".

NOTE: Be sure to set a reference to System.Data.DataSetExtensions.

In C#:

var query = dt.AsEnumerable().Where(c=>
        c.Field<String>("LastName").StartsWith("B"));

ComboBox2.DataSource = query.AsDataView();
ComboBox2.DisplayMember = "FullName";
ComboBox2.ValueMember = "CustomerId";

In VB:

Dim query = dt.AsEnumerable.Where(Function(c) _
       c.Field(Of String)("LastName").StartsWith("B"))

ComboBox2.DataSource = query.AsDataView
ComboBox2.DisplayMember = "FullName"
ComboBox2.ValueMember = "CustomerId"

This code uses a Lambda expression to filter the DataTable to only those rows where the LastName starts with "B".

The AsEnumerable extension method is necessary to allow Linq/Lambda expressions to work with a DataTable. Any field in the DataTable is accessed using c.Field<T> Or c.Field(Of T) where T is the type of the field. In this example, the field is a string.

The second ComboBox is then bound to the query using the AsDataView extension method. This allows the binding to bind the result as a DataView.

The result looks like this.

image

But wait, there is more. Because of the way that binding works. adding rows to the DataTable will add rows to the first ComboBox that is bound to the DataTable. AND if the new row starts with the letter "B", it will add it to the second ComboBox as well.

In this example, code in the Add button does this:

In C#:

private void Button1_Click(object sender, EventArgs e)
{
    dt.Rows.Add(5, "Bond", "James", "Bond, James", DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, 3);
}

In VB:

Private Sub Button1_Click2(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles Button1.Click
    dt.Rows.Add(5, "Bond", "James", "Bond, James", DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, DBNull.Value, 3)
End Sub

Click the button and the result is as follows:

image

No refreshing or re-executing binding code required. It just works!!

Use this technique any time you want to bind a ComboBox to a filtered set of data from a DataTable.

Enjoy!

January 31, 2010

Finding a Node in an XML String

Filed under: C#,Lambda Expressions,LINQ,VB.NET,XML @ 4:21 pm

A common requirement with an XML file is to find a particular node. If you are targeting the .NET framework 3.5 or higher, you can find the node using Linq to XML or by using Lambda expressions.

As with many of my prior XML examples, the XML string is as follows:

<States>
  <State name="Wisconsin">
    <Regions>
      <Region name="Milwaukee">
        <Area name="Mukwanago"/>
        <Area name="Germantown"/>
      </Region>
      <Region name="Fox Valley">
        <Area name="Oshkosh" />
        <Area name="Appleton" />
      </Region>    
    </Regions>
  </State>
</States>

The code to find the node for the Milwaukee region is as follows:

In C#:

// Be sure to set a reference to System.Core and System.Xml.Linq
XElement states  = XElement.Load("testXML.xml");

// Using LINQ
XElement foundNode;
var query = from XElement r in states.Descendants("Region")
                   where r.Attribute("name").Value == "Milwaukee"
                   select r;
foundNode = query.FirstOrDefault();

// Using Lambda expressions
foundNode = states.Descendants("Region").
     Where(r => r.Attribute("name").Value ==
                         "Milwaukee").FirstOrDefault();

In VB:

‘ Be sure to set a reference to System.Core and System.Xml.Linq
Dim states As XElement = XElement.Load("testXML.xml")

‘ Using LINQ
Dim foundNode As XElement
Dim query = From r As XElement In states…<Region> _
                  Where r.@<name> = "Milwaukee"
foundNode = query.FirstOrDefault()

‘ Using Lambda expression
foundNode = states…<Region>.Where(Function(r) r.@<name> =  _ 
                                 "Milwaukee").FirstOrDefault

This code first loads the XML file containing the XML. The next set of code can be done using LINQ or using Lambda expressions. Use either one, but not both. 🙂

The C# code uses the XElement properties and methods. The VB code uses XML literals.

NOTE: The XElement properties and methods work in VB as well.

Enjoy!

NOTE: This post was created based on a prior post that included both finding a node and adding new nodes. This post separates the first step to provide a more straightforward example.

October 16, 2009

Lambda Expressions: Execution

Filed under: C#,Lambda Expressions,VB.NET @ 3:26 pm

Lambda expressions can be assigned to a delegate variable. The lambda expression is then executed when the delegate is called.

[To begin with an overview of lambda expressions, start here.]

In the following example, a lambda expression is assigned to a variable (f).

In C#:

Func<int,string> f = x => (x + x).ToString();
Debug.WriteLine(f(5));
Debug.WriteLine(f(10));

In VB:

Dim f = Function(x as Integer) (x + x).ToString()
Debug.WriteLine(f(5))
Debug.WriteLine(f(10))

The lambda expression in this example is a Func<int, string> [Func(Of Integer, String) in VB] meaning it takes one integer input parameter and returns a string. The lambda expression simply takes the input value, adds it to itself, and returns the result as a string.

[For more information on Func delegates, see this post.]

When the expression is defined, it is not executed. It is not executed until it is called.

The first Debug.WriteLine statement calls the lambda expression, passing in a 5. The result displays "10" in the debug window.

The second Debug.WriteLine statement calls the lambda expression again, passing in a 10. The result displays "20" in the debug window.

Lambda expressions are executed when they are called, not when they are constructed. This is important to consider, especially when the lambda expression contains local variables.

Local variables used in a lambda expression are “captured” or “lifted”. The variable value used is the value at execution time. The variable lifetime extends to the lifetime of the delegate.

The following code updates the original example to use a local variable.

In C#:

int y = 0;
Func<int,string> f = x => (x + y).ToString();
y = 10;
Debug.WriteLine(f(5));

In VB:

Dim y As Integer = 0
Dim f = Function(x) (x + y).ToString()
y = 10
Debug.WriteLine(f(5))

The lambda expression in this example takes the input value, adds it to the current value of the local variable (y), and returns the result as a string.

The Debug.WriteLine statement in this example calls the lambda expression, passing in a 5.  At the point of executing the Debug statement, y is 10, so the result displays "15" in the debug window.

For another example of using lambda expressions with local variables, see this post.

Enjoy!

October 11, 2009

Lambda Expressions: Func Delegates

Filed under: C#,Lambda Expressions,VB.NET @ 6:49 pm

A Func delegate encapsulates a method that returns a value. It takes up to four parameters (and this number is increased in .NET 4.0) plus the return value.

[To begin with an overview of lambda expressions, start here.]

The following signature is a Func delegate that takes two integer parameters and returns a string.

In C#:

Func<int, int, string>

In VB:

Func(Of Integer, Integer, String)

There are many examples of Func delegates.

The following code demonstrates the Sum function that sums the sales total for all customers in the list (and assumes that a Customer object has a SalesTotal property).

In C#:

var total = custList.Sum(c =>
            c.SalesTotal);

In VB:

Dim total = custList.Sum(Function(c) _
              c.SalesTotal)

Enjoy!

Lambda Expressions: Action Delegates

Filed under: C#,Lambda Expressions,VB.NET @ 6:04 pm

Just as the name implies, an action delegate encapsulates a method that performs an action and has no return value. It takes up to four parameters (and this number is increased in .NET 4.0).

[To begin with an overview of lambda expressions, start here.]

The ForEach method of the List<T> is an example of an action delegate. It takes an action delegate as a parameter.

One of the common uses of ForEach is to display all of the elements in a list using one line of code.

In C#:

custList.ForEach(c =>
            Debug.WriteLine(c.LastName));

In VB:

custList.ForEach(AddressOf WriteToDebug)

NOTE: VB lambda expressions do not currently support action delegates. In the above example, the code uses a named method as the action delegate. VB 10 lambda expressions will support action delegates using the Sub keyword instead of the Function keyword.

Enjoy!

Lambda Expressions: Predicate Delegates

Filed under: C#,Lambda Expressions,VB.NET @ 5:45 pm

According to Wikipedia, a predicate is “a function which returns a Boolean value”.

[To begin with an overview of lambda expressions, start here.]

A predicate delegate encapsulates a method that evaluates to True or False. It takes a single parameter.

The Array class Find method is an example of a predicate delegate. It takes an array as a first parameter and a predicate delegate as its second parameter.

In C#:

var foundCustomer = Array.Find(custArray, c =>
        c.LastName.StartsWith("K"));

In VB:

Dim foundCustomer = Array.Find(custArray, Function(c) _
        c.LastName.StartsWith("K"))

This code iterates through the array, checking each entry and evaluating the expression as true or false.  If the expression is false, it continues. If the expression is true, it returns the entry. Basically, this code finds the first customer in the array with a last name that starts with the defined letter.

Enjoy!

Lambda Expressions: Syntax

Filed under: C#,Lambda Expressions,VB.NET @ 4:02 pm

This post covers the syntax for lambda expressions. It uses the customer list defined in this prior post.

[To begin with an overview of lambda expressions, start here.]

The first example below uses the FirstOrDefault extension method on the Enumerable object to find the first customer that matches the defined expression. The FirstOrDefault method takes a delegate as a parameter and that delegate is defined with a lambda expression.

In C#:

‘ General syntax:
Customer foundCustomer =
     custList.FirstOrDefault((Customer c) =>
                    c.CustomerId == 4);)

‘ With inferred typing:
var foundCustomer =
     custList.FirstOrDefault(c => c.CustomerId == 4);)

The lambda expression in this example is as follows:

c => c.CustomerId == 4

The code begins with the set of parameters to the lambda expression. In this example, there is one parameter (c). The => is the “goes to” or lambda operator. The remainder of the code is the expression itself. In this case, checking for the item in the list where CustomerId is 4.

In VB:

‘ General syntax:
Dim foundCustomer as Customer = _
    custList.FirstOrDefault(Function(c as Customer) _
                                  c.CustomerId = 4)

‘ With inferred typing:
Dim foundCustomer = _
    custList.FirstOrDefault(Function(c) _
             c.CustomerId = 4)

The lambda expression in this example is as follows:

Function(c) c.CustomerId = 4

The code begins with the word “Function” along with the set of parameters to the lambda expression. In this example, there is one parameter (c). The remainder of the code is the expression itself. In this case, checking the item in the list where CustomerId is 4.

In either language, notice the shorter syntax with inferred typing. The compiler recognizes that the list contains customers, so it can infer the type of the parameter and the type of return value.

You can combine some of the extension methods to form more complex statements.

In C#:

var query = custList.FindAll(c =>
    c.LastName.StartsWith(“K”)).OrderBy(c => c.FirstName));

In VB:

Dim query = custList.FindAll(Function(c) _
        c.LastName.StartsWith(“K”)). _
       
OrderBy(Function(c) c.FirstName))

This code finds all of the entries that match the first lambda expression, basically all customers with a last name that begins with “K”. The code then performs an OrderBy using the second lambda expression, basically sorting the resulting list by first name.

The above examples demonstrate single-line lambdas. You can also use multi-line lambdas as shown in the example below.

In C#:

var foundCustomer = 
   
custList.FirstOrDefault(c =>
{
   Debug.WriteLine(c.LastName);
   if (c.CustomerId == 4)
      return true;
   else
      return false;
});

In VB:

Not available in VB9, coming in VB10.

Multi-line lambda expressions require the standard {} syntax to define the statement block. Whereas single-line lambda expressions automatically handle  the return value for you, you have to handle it yourself in multi-line lambda expressions.

Enjoy!

Delegates

Filed under: C#,Lambda Expressions,VB.NET @ 3:29 pm

A delegate is an object that holds a reference to a method with a particular parameter list and return type.

It is basically like an object-oriented, type-safe function pointer.

Delegates basically make it possible to treat methods as entities. You can then assign a delegate to a variable or pass it as a parameter.

The classic delegate example uses events.

In C#:

HelloButton.Click += new EventHandler(HelloButton_Click);

Or the short-cut version of this code:

HelloButton.Click += HelloButton_Click;

This passes the HelloButton_Click method to a new EventHandler delegate.

In VB:

AddHandler HelloButton.Click, AddressOf HelloButton_Click

In VB, the AddressOf assigns the address of the HelloButton_Click method to the delegate.

Instead of using a named method for the delegate (in this example, a HelloButton_Click method), you can use a lambda expression.

In C#:

HelloButton.Click += (s, ev) =>
           MessageBox.Show("Hello World");

In VB:

AddHandler HelloButton.Click, Function(s, ev) _
           MessageBox.Show("Hello World")

In this case the lambda expression has two parameters: s (sender) and ev (eventArgs). When the Click event is generated on the HelloButton, the code displays the MessageBox.

You can use a lambda expression (inline function) anywhere a delegate is required. However, if the function requires more than a few lines of code, a named method is the preferred and recommended technique.

Enjoy!

Lambda Expressions: Finding an Item in a Generic List

Filed under: C#,Lambda Expressions,LINQ,VB.NET @ 2:52 pm

In this prior post, I detailed how to build lists of things using a generic List<T>. Once you have a list, you may need to find items in the list. There are several ways to accomplish this, but using lambda expressions is the most concise.

[To begin with an overview of lambda expressions, start here.]

For example, here is how you find an item in a generic list using a for/each statement.

In C#:

Customer foundCustomer = null;
foreach (var c in custList)
{
    if (c.CustomerId == 4)
    {
        foundCustomer = c;
        break;
    }
}
Debug.WriteLine(foundCustomer);

In VB:

Dim foundCustomer As Customer = Nothing
For Each c As Customer In custList
    If c.CustomerId = 4 Then
        foundCustomer = c
        Exit For
    End If
Next
Debug.WriteLine(foundCustomer)

Notice the amount of code required to loop through each item in the list and find the one with a particular customer Id.

You may have heard about language integrated query (LINQ) and how you can use it to search a  list.

In C#:

Customer foundCustomer = null;
var query = from c in custList
            where c.CustomerId == 4
            select c;
foundCustomer = query.FirstOrDefault();
Debug.WriteLine(foundCustomer);

In VB:

Dim foundCustomer As Customer = Nothing
Dim query = From c As Customer In custList _
            Where c.CustomerId = 4 _
            Select c
foundCustomer = query.FirstOrDefault()
Debug.WriteLine(foundCustomer)

So LINQ is cool, but the code does not seem much shorter.

Now let’s look at using lambda expressions to find the item in the list.

In C#:

Customer foundCustomer = null;
foundCustomer = custList.FirstOrDefault(c =>
                        c.CustomerId == 4);
Debug.WriteLine(foundCustomer);

The lambda expression syntax in C# looks like this:

c => c.CustomerId == 4

The code begins with the set of parameters to the lambda expression. The => is the “goes to” or lambda operator. The remainder of the code is the expression itself. In this case, checking for the item in the list where CustomerId is 4.

In VB:

Dim foundCustomer As Customer = Nothing
foundCustomer = custList.FirstOrDefault(Function(c)
                        c.CustomerId = 4)
Debug.WriteLine(foundCustomer)

The lambda expression syntax in VB looks like this:

Function(c) c.CustomerId = 4

The code begins with the word “Function” along with the set of parameters to the lambda expression. The remainder of the code is the expression itself. In this case, checking the item in the list where CustomerId is 4.

.NET 3.5 added a large list of extension methods on the Enumerable class. Any object that implements IEnumerable or IEnumerable<T> (basically any object you can do a for/each over) can use these methods. Many of these extension methods (such as the FirstOrDefault method  shown in the above example) support lambda expressions.

The FirstOrDefault extension method of the Enumerable class returns the first item in the list or the default value for the object. If you pass it a lambda expression, it returns the first item in the list that matches the Boolean expression.

Enjoy!

« Previous PageNext Page »

© 2020 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs