Partial Page Post-Back and The Property Proxy Validator – CTD

As a quick follow-up to this post, I disabled the PropertyProxyValidator on the RowUpdating Event Handler of the other control and it worked fine

 

        protected void GridViewFamily_RowUpdating(object sender, GridViewUpdateEventArgs e)

        {

            PropertyProxyValidator propertyProxyValidator =

                GenericUtilities.FindAChildControl(this.DetailsViewInsertFamily, "PropertyProxyValidatorInsertPersonName") as PropertyProxyValidator;

            propertyProxyValidator.Enabled = false;

 

        }

 

FindaChildControl is a just a recursive method to find a control on a web page:

        public static Control FindAChildControl(Control control, string controlId)

        {

            Control targetControl = null;

            foreach (Control childControl in control.Controls)

            {

                if (childControl.ID == controlId)

                {

                    targetControl = childControl;

                }

                if (targetControl == null && childControl.Controls.Count > 0)

                {

                    targetControl = FindAChildControl(childControl, controlId);

                }

            }

            return targetControl;

        }

 

The fact that I have to do this is very annoying – it adds more code to the project, therefore increasing the cost to develop and maintain it.  I noticed that the out of the box ASP.NET validators do not have this problem – they only fire when the control that they are associated is updating/inserting…   I would argue that the Ent Lib validators need to add this feature.

 

Advertisements

Partial Page Post-Back and The Property Proxy Validator

I ran into a problem recently that had me scratching my head.  I have a DTO with Enterprise Library Validation like so:

    public class Person

    {

       

        [RangeValidator(0,RangeBoundaryType.Inclusive,10,RangeBoundaryType.Inclusive,MessageTemplate="{0} is an invalid Person Id")]

        public int PersonId { get; set; }

        [StringLengthValidator(10, MessageTemplate = "Name is 10 characters")]

        [NotNullValidator(MessageTemplate="{1} Can’t be null}")]

        public string PersonName { get; set; }

        public DateTime DateOfBirth { get; set; }

        public DateTime? DateOfDeath { get; set; }

 

This person class was served up by a Factory (Builder, really) like so.  I threw some test data into the class just so I can inspect value in the round trip

 

    [DataObject(true)]

    public class PersonFactory

    {

        static Collection<Person> family;

 

        static PersonFactory()

        {

            family = new Collection<Person>();

            family.Add(new Person(0, "Jackie", DateTime.Parse("05/04/1951"), null));

            family.Add(new Person(1, "Tito", DateTime.Parse("10/15/1953"), null));

            family.Add(new Person(2, "Jermaine", DateTime.Parse("12/11/1954"), null));

            family.Add(new Person(3, "Marlon", DateTime.Parse("03/12/1957"), null));

            family.Add(new Person(4, "Michael", DateTime.Parse("08/29/1958"), DateTime.Parse("06/25/2009")));

        }

 

        public static Collection<Person> GetFamily()

        {

            return family;

        }

 

        public static void UpdatePerson(Person person)

        {

            . . .

        }

 

 

        public static void InsertPerson(Person person)

        {

            . . .

        }

 

I then created a simple Web Form to handle the updating using an Object Data Source and a Grid View:

<asp:GridView ID="GridViewFamily" runat="server" AutoGenerateColumns="False"

    DataSourceID="ObjectDataSourceFamily">

    <Columns>

            <EditItemTemplate>

                <asp:TextBox ID="TextBoxPersonId" runat="server" Text=’<%# Bind("PersonId") %>‘></asp:TextBox>

                <cc1:PropertyProxyValidator ID="PropertyProxyValidatorUpdatePersonId" runat="server"

                    ControlToValidate="TextBoxPersonId" PropertyName="PersonId"

                    SourceTypeName="TestWebProject.Person" Display="None"></cc1:PropertyProxyValidator>

                <cc2:ValidatorCalloutExtender ID="PropertyProxyValidator_UpdatePersonId_ValidatorCalloutExtender"

                    runat="server" Enabled="True" TargetControlID="PropertyProxyValidatorUpdatePersonId">

                </cc2:ValidatorCalloutExtender>

            </EditItemTemplate>

            <ItemTemplate>

                <asp:Label ID="Label1" runat="server" Text=’<%# Bind("PersonId") %>‘></asp:Label>

            </ItemTemplate>

        </asp:TemplateField>

        <asp:TemplateField HeaderText="PersonName" SortExpression="PersonName">

      . . .

 

    </Columns>

</asp:GridView>

 

So far, so good

 

 

I then added a Details View for inserting and associated it with the same Object Data Source:

<asp:DetailsView ID="DetailsViewInsertFamily" runat="server" AutoGenerateRows="False"

DataSourceID="ObjectDataSourceFamily" DefaultMode="Insert" Height="50px"

Width="125px">

    <Fields>

            <InsertItemTemplate>

                <asp:TextBox ID="TextBoxInsertPersonId" runat="server"

            Text=’<%# Bind("PersonId") %>‘></asp:TextBox>

                <cc1:PropertyProxyValidator ID="PropertyProxyValidatorInsertPersonId" runat="server"

            ControlToValidate="TextBoxInsertPersonId" PropertyName="PersonId"

            SourceTypeName="TestWebProject.Person" Display="None"></cc1:PropertyProxyValidator>

                <cc2:ValidatorCalloutExtender ID="PropertyProxyValidator_InsertPersonId_ValidatorCalloutExtender"

            runat="server" Enabled="True"

            TargetControlID="PropertyProxyValidatorInsertPersonId">

                </cc2:ValidatorCalloutExtender>

            </InsertItemTemplate>

            <ItemTemplate>

                <asp:Label ID="Label1" runat="server" Text=’<%# Bind("PersonId") %>‘></asp:Label>

            </ItemTemplate>

        </asp:TemplateField>

            . . .

 

 

    </Fields>

</asp:DetailsView>

 

Things go awry.   When I try and run an update on the screen, I get the following error:

Index was outside the bounds of the array.

After a little thought, I realized that the Insert Template is firing off the validation on the update postback.  I handled the error by adding a ValueConvert event handler to the PropertyProxyValidator on the insert:

        protected void PropertyProxyValidatorInsertPersonId_ValueConvert(object sender, Microsoft.Practices.EnterpriseLibrary.Validation.Integration.ValueConvertEventArgs e)

        {

            int result = 0;

            bool ok = Int32.TryParse(e.ValueToConvert.ToString(), out result);

            if (!ok)

            {

                e.ConvertedValue = -1;

            }

        }

However, handling the error then gives me this:

 

Notice how the Validator is firing for Details View – even though the Updating is from the Grid View.

I then put the details view into an Update Panel to see if that will isolate the GridView’s Update – no luck.  I then thought of two programic solutions:

·         On the Updating Event, disable the Insert Validators

·         Adding a RuleSet for updating and inserting

Both solutions seem less than ideal to me – Adding a ruleset uses duplicates code and makes the code base harder to maintain – as well as coupling the DTO to the User Layer – which is never good.  Using some event to enable/disable the validators seems to be better from a coding perspective so I will implement that

 

 

 

WCF – My “Duh!” moment of the week

In preparation for my 70-569 exam, I have been following the Learning Plan for WCF found here
The first step was this webcast found here

I attempted to build a Hello-WCF solution based on what as presented in the webcast and I <thought> I ran into Vista problems.  I set my endpoint like this in the Hosting Service:

serviceHost.AddServiceEndpoint(typeof(IPersonFactory), new NetTcpBinding(), "net.tcp://localhost:9000/PersonFactory");

And I set a reference to that endpoint in the client like this:

IPersonFactory proxy = ChannelFactory<IPersonFactory>.CreateChannel(new

      NetTcpBinding(),new

EndpointAddress("net.tcp://localhost:9000/PersonFactory"));

 

Console.WriteLine(proxy.Echo("Hello World"));

 

I got the following message:


No connection could be made because the target machine actively refused it 127.0.0.1:9000

I tried running VS2008 as an administrator but I got the same message.  I then opened the port in Windows Firewall and then disabling the firewall completely.

I then posted to the MSDN forums here

 

Turns out I was barking up the wrong tree.  By using the “Using” statement, I was closing the port before attempting to have the client make the call.  I quess I was too quick to blame Vista…

MCPD Upgrade

I passed Exam 70-568 on Friday.  I was surprised about how little of the new features of 3.5 were actually in the exam – only a few questions on LINQ, none on entity framework, etc….  I guess the 4.0 exam might have it?  In any event, I am planning to finish the 3.5 upgrade this Decemeber so I am jumping into WCF with both feet.
 

Data Binding CascadingDropDownList

The CascadingDropDownList (CDDL) is a very handy control – I have used it in a couple of different situations.  I recently ran into an interesting implementation of using it.

Consider the basic CDDL that is used as an example on the AJAX homepage (http://www.asp.net/AJAX/AjaxControlToolkit/Samples/CascadingDropDown/CascadingDropDown.aspx)

I duplicated the project and added a twist – the values in the CDDLs are bound to another class.  I called this class CarOrder.  This CarOrder class has the following interface/implementation:

    public class CarOrder

    {

        public int MakeId { get; set; }

        public int ModelId { get; set; }

        public int ColorId { get; set; }

    }

Instanceses of CarOrders are served by a Factory class like so:

        public CarOrder GetCarOrder()

        {

            CarOrder carOrder = new CarOrder();

            carOrder.ColorId = 0;

            carOrder.MakeId = 0;

            carOrder.ModelId = 0;

 

            return carOrder;

        }

 

        public void UpdateCarOrder(CarOrder carOrder)

        {

 

        }

The actual implementation of the factory is not germane for this blog so I left it out.  Once I get better at Mocking, I can provide a more detailed example.

I then added a Default page to my project and constructed it as so:

 

  The FormView’s Update Panel looks like so:

                <asp:DropDownList ID="DropDownListMake" runat="server" Height="16px"

                    Width="211px" SelectedValue=’<%# Bind("MakeId") %>

                    DataSourceID="ObjectDataSourceCarOrder" DataTextField="MakeId"

                    DataValueField="MakeId">

                </asp:DropDownList>

                <cc1:CascadingDropDown ID="DropDownListMake_CascadingDropDown" runat="server"

                    Category="Make" Enabled="True" PromptText="Select Make"

                    ServiceMethod="GetMakes" TargetControlID="DropDownListMake"

                    UseContextKey="True">

                </cc1:CascadingDropDown>

Note that the data binding is with the DropDownList.  When I run the page, I get the following error:

‘DropDownListMake’ has a SelectedValue which is invalid because it does not exist in the list of items.
Parameter name: value

So Then I put the SelectedValue to the CascadingDropDown like so:

                <asp:DropDownList ID="DropDownListMake" runat="server" Height="16px"

                    Width="211px">

                </asp:DropDownList>

                <cc1:CascadingDropDown ID="DropDownListMake_CascadingDropDown" runat="server"

                    Category="Make" Enabled="True" PromptText="Select Make"

                    ServiceMethod="GetMakes" TargetControlID="DropDownListMake"

                    SelectedValue=’<%# Bind("MakeId") %>  UseContextKey="True">

                </cc1:CascadingDropDown>

And things ran like a champ

 

Some things to remember:

  • Disable EventValidation On the page (or master page if you are using one)
  • If you are using a method from the code-behind, make sure your script manager has EnablePageMethod set to true
  • Make sure your method matches the definition AND
  • If it is a code-behind, make sure the method is marked as static and you use both of these attributes:

[WebMethod]

[ScriptMethod]