View Single Post
  #1 (permalink)  
Old July 26th, 2006, 09:20 PM
.MAttButler .MAttButler is offline
Authorized User
Join Date: Jul 2006
Location: , , .
Posts: 14
Thanks: 0
Thanked 1 Time in 1 Post
Send a message via AIM to .MAttButler
Default Chapter 10 fixes

I'm going to be working through the book and making the fixes. I got the code and text for the book last night and the first thing I looked at and fixed was the " Exception Details: System.ArgumentException: A ConsumerConnectionPoint has already been added with ID 'default'.
Parameter name: connectionPoints" exception. This means that the declaration of the three callback methods in ConsumerPart.vb lacked a unique ID. The overload used in the book left the default name in place by only passing in a string for the displayName eg:

<WebControls.WebParts.ConnectionConsumer("IBookInf o Consumer")> _
    Public Sub IBookInfoConsumer(ByVal bk As IBookInfo)

In the corrected code below we've used the overload that assigns an ID, too:

<ConnectionConsumer("IBookInfo Consumer", "MyUniqueIBookInfoConsumerName")>....

Below is the ConsumerConnectionPointCollection constructor and we can see on line 16 the Contains() test being run to see if there is a duplicate key (representing as ConsumerConnectionPoint) already in the HybridDictionary. If it returns true then code throws the exception that we finally see (line 17). Again, the code below is framework code and isn't something we'd need to deal with, it's just here to illustrate the "why" behind the exception. I make a habit of looking several layers below the root cause of an error or exception just so I can understand what consequences each line of code I write has within the framework:

1. Public Sub New(ByVal connectionPoints As ICollection)
2. If (connectionPoints Is Nothing) Then
3 Throw New ArgumentNullException("connectionPoints")
4 End If
5 Me._ids = New HybridDictionary(connectionPoints.Count, True)
6 Dim V_0 As Object
7 For Each V_0 In connectionPoints
8 If (V_0 Is Nothing) Then
9 Throw New ArgumentException(SR.GetString("Collection_CantAdd Null"), "connectionPoints")
0 End If
11 Dim V_1 As ConsumerConnectionPoint = TryCast(V_0,ConsumerConnectionPoint)
12 If (V_1 Is Nothing) Then
13 Throw New ArgumentException(SR.GetString("Collection_Invalid Type", New Object() { "ConsumerConnectionPoint" }), "connectionPoints")
14 End If
15 Dim V_2 As String = V_1.ID
16 If Me._ids.Contains(V_2) Then
17 Throw New ArgumentException(SR.GetString("WebPart_Collection _DuplicateID", New Object() { "ConsumerConnectionPoint", V_2 }), "connectionPoints")
18 End If
19 MyBase.InnerList.Add(V_1)
20 Me._ids.Add(V_2, V_1)
21 Next
22 End Sub

Here is the code for the ConsumerPart.vb class that correct this particular problem. It is not in it's final form, I still haven't gone through it in detail:

Imports System.ComponentModel
Imports System.Web.UI

<ToolboxData("<{0}:ConsumerPart runat=server></{0}:ConsumerPart>")> Public Class ConsumerPart
    Inherits System.Web.UI.WebControls.WebParts.WebPart

    Private ibk As IBookInfo
    Dim typ As Object
    Dim strTitle As String
    Dim pcSchema As PropertyDescriptorCollection

    'This method should have used the constuctor that accepts a string to set the ID like this:
    <WebControls.WebParts.ConnectionConsumer("IBookInf o Consumer", "MyUniqueIBookInfoConsumerName")> _
    Public Sub IBookInfoConsumer(ByVal bk As IBookInfo)
        Dim strProviderValue As String
        Dim bolSuccess As Boolean

        ibk = bk

        strProviderValue = "10345329"
        bk.ISBN = strProviderValue
        bolSuccess = bk.SortData("Desc")

    End Sub

    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)

        If ibk Is Nothing Then
            writer.Write("<b>No book information available.</b>")
            writer.Write("ISBN: <b>" & ibk.ISBN & "</b>")
        End If

    End Sub

    Private Sub ConsumerPart_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
        Me.Title = "Consumer"
    End Sub

    Dim ifld As WebControls.WebParts.IWebPartField
    'This method should have used the constuctor that accepts a string to set the ID like this:
    <WebControls.WebParts.ConnectionConsumer("IWebPart Field Consumer", "MyUniqueIWebPartFieldConsumerName")> _
    Public Sub IWebPartFieldConsumer(ByVal fld As WebControls.WebParts.IWebPartField)

        ifld = fld
        ifld.GetFieldValue(AddressOf ReceiveData)

    End Sub

    Sub ReceiveData(ByVal BookTitle As Object)
        Dim pd As PropertyDescriptor

        pd = ifld.Schema
        If pd.PropertyType.Name = "String" Then
            strTitle = BookTitle.ToString
        End If

    End Sub

    'This method should have used the constuctor that accepts a string to set the ID like this:
    <WebControls.WebParts.ConnectionConsumer("IWebPart Parameters Consumer", "MyUniqueIWebPartParametersConsumerName")> _
    Public Sub IWebPartParametersConsumer( _
                      ByVal prm As WebControls.WebParts.IWebPartParameters)
        Dim pdc As PropertyDescriptorCollection
        Dim iprm As WebControls.WebParts.IWebPartParameters

        iprm = prm

        pdc = TypeDescriptor.GetProperties(Me)

        If iprm.Schema.Count = pdc.Count Then
            iprm.GetParametersData(AddressOf ReceiveData)
        End If
    End Sub

    Sub ReceiveData(ByVal BookInfo As IDictionary)
        Dim entry As DictionaryEntry

        For Each entry In BookInfo.Keys
            Select Case entry.Key.ToString
                Case "BookTitle"
                    Me.BookTitle = entry.Value.ToString
                Case "BookAuthor"
                    Me.BookAuthor = entry.Value.ToString
            End Select

    End Sub

    Property BookTitle() As String
            Return "Custom Controls and Web Parts"
        End Get
        Set(ByVal value As String)

        End Set
    End Property

    Property BookAuthor() As String
            Return "Peter Vogel"
        End Get
        Set(ByVal value As String)

        End Set
 End Property

End Class