CslaDataSource_UpdateObject() e.Values has null values

CslaDataSource_UpdateObject() e.Values has null values

Old forum URL: forums.lhotka.net/forums/t/1256.aspx


Bahadir posted on Monday, September 18, 2006

Hi,

I am new to Csla. I have a root collection object, EmployeeList, and its children, Employee objects. I have a basic website where a GridView pulls data from EmployeeList by calling:

    protected void EmployeesListDataSource_SelectObject(
        object sender, Csla.Web.SelectObjectArgs e)
        {
            // Sql query: Select * From Employees
            e.BusinessObject = GetEmployeesList();
        }

The whole employees table displays just fine in the GridView. Next, I click on edit, on one of the rows, edit the row and press update. In this case, I expect to update this row with the following:

   protected void EmployeesListDataSource_UpdateObject(
    object sender, Csla.Web.UpdateObjectArgs e)
    {
        EmployeesList obj = GetEmployeesList(); // From Session
        try
        {
            Employee emp = obj.getItem(
                        new Guid(e.Keys["Id"].ToString()));
            // emp is valid here.
            Csla.Data.DataMapper.Map(e.Values, emp);
            emp.Save();
            e.RowsAffected = 1;
        }
        catch
        ...
    }

The DataMapper call fails with: Property copy failed (Name)
If I try to manually try copying fields "Name", "Surname", and "JobTitle" from e.Keys[], these fields are null, but not the "Id" field. Essentially e.Values just contains "Id" of Employee. Do you see any mistakes? Shouldn't the GridView pass all columns' values to UpdateObject?

Thanks,
Bahadir

Bahadir replied on Monday, September 18, 2006

Hi,

I've just seen on this post http://forums.lhotka.net/forums/thread/6370.aspx

that UpdateObject returns null on fields that are left blank. In my case however fields were not blank.

Thanks,
Bahadir

sydneyos replied on Tuesday, September 19, 2006

Bahadir, Do you have ViewState enabled on the grid?  Not sure if this would make a difference . . .

Bahadir replied on Tuesday, September 19, 2006

Hi,

No I haven't had luck with this. I will seek help in an asp.net forum. At least you have non-template fields working. By the way, perhaps you've seen it already, but each column has a property: "ConvertEmptyStringToNull" this might help with eliminating null keys for empty fields.

Bahadir

sydneyos replied on Wednesday, September 20, 2006

Could you post the code you have for the grid and the code-behind that handles the grid?

Bahadir replied on Wednesday, September 20, 2006

Hi,

Thanks, for helping me out. I didn't post thinking it would be off-topic. Here's what I posted at: http://forums.asp.net/thread/1404722.aspx

Note I was using csla2.1 beta originally, then down to csla2.0.3 and still the same.

---

Hi,

I have a basic GridView control that is bound to a custom object data source as below:

 

 <form id="form1" runat="server"><br />
        &nbsp;<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" BackColor="White"
            BorderColor="#999999" BorderStyle='Solid' BorderWidth='1px' CellPadding='3' DataKeyNames='Id'
            DataSourceID="EmployeesListDataSource" ForeColor="Black" GridLines="Vertical">

            <FooterStyle BackColor="#CCCCCC" />
            <Columns>
                <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" ShowSelectButton="True" />
                <asp:BoundField DataField="Id" HeaderText="Id" InsertVisible="False" ReadOnly="True" SortExpression="Id" />
                <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
                <asp:BoundField DataField="Surname" HeaderText="Surname" SortExpression="Surname" />
                <asp:BoundField DataField="JobTitle" HeaderText="Job Title" SortExpression="JobTitle" />
            </Columns>
            <SelectedRowStyle BackColor="#000099" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#999999" ForeColor="Black" HorizontalAlign="Center" />
            <HeaderStyle BackColor="Black" Font-Bold="True" ForeColor="White" />
            <AlternatingRowStyle BackColor="#CCCCCC" />
        </asp:GridView>
    </form>

    <csla:CslaDataSource ID="EmployeesListDataSource" runat="server"
      TypeName="classlib_cslatest.EmployeesList"
      TypeAssemblyName="classlib_cslatest"
      OnDeleteObject="EmployeesListDataSource_DeleteObject"
      OnUpdateObject="EmployeesListDataSource_UpdateObject"
      OnSelectObject="EmployeesListDataSource_SelectObject">
    </csla:CslaDataSource>
 
As you can see, there are Id, Name, Surname and JobTitle fields, and Id is the only key field (see DataKeyNames). The GridView is successfully populated with rows and columns via the EmployeesListDataSource_SelectObject() call. In other words, the column names are correct, and all the rows of my Employee table is displayed correctly. So my assumption is that data is bound correctly. Then when I want to update the GridView, I do the following:
 
1) Click edit on the row that I want to modify.
2) Change the fields in a row that are enabled for modification.
3) Click update.
 
Then I expect to get updated fields in "UpdateObjectArgs e" argument to EmployeesListDataSource_UpdateObject() (See below).
But there are no keys defined in the UpdateObjectArgs argument for the modified fields. I would expect to get e.Keys["Name"], e.Keys["Surname"] and e.Keys["JobTitle"] to obtain new values, but there are no such keys defined. The only defined dictionary key is "Id" and that contains the original Id value, since it is defined in DataKeyNames.
 
Could you think of a reason why Update wouldn't work? Could you point me at the first point in the ASP.NET calls where the DataGrid values are captured before being passed to UpdateObjectArgs? Maybe I can debug it from there?
 
Thanks,
Bahadir 
 
The function to update the database is as follows:
 
    protected void EmployeesListDataSource_UpdateObject(
    object sender, Csla.Web.UpdateObjectArgs e)
    {
        EmployeesList obj = GetEmployeesList();

        try
        {
            // e.Keys["Id"] exists and keeps its original value.
            Employee emp = obj.getItem(new Guid(e.Keys["Id"].ToString()));
            String total = string.Empty;
            // Neither of the below keys exist:
            MessageLabel.Text = "Name: " + e.Keys["Name"] +
                "Surname: " + e.Keys["Surname"] + "Jobtitle: " + e.Keys["JobTitle"];
            //Csla.Data.DataMapper.Map(e.Values, emp);
            MessageLabel.Text += "Total: " + total;
            emp.Save();

            e.RowsAffected = 1;
        }
        catch (Csla.DataPortalException ex)
        {
            this.ErrorLabel.Text = ex.BusinessException.Message;
            e.RowsAffected = 0;
        }
        catch (Exception ex)
        {
            this.ErrorLabel.Text = ex.Message ;
            e.RowsAffected = 0;
        }
    }
 
// Function I use to fetch data. Uses SQL Query: Select * From Employee. Works just fine. 
    protected void EmployeesListDataSource_SelectObject(
        object sender, Csla.Web.SelectObjectArgs e)
        {
            e.BusinessObject = GetEmployeesList();
        }
 
 


sydneyos replied on Wednesday, September 20, 2006

Hmm, I am using e.Values["mykey"] to get the values back - that seems to work (except for my template columns that are being bound in the RowDataBound code-behind).  Have you tried that instead of e.Keys["mykey"]?

Bahadir replied on Wednesday, September 20, 2006

Grrr.. I knew it was a simple error. It works now.

Keys I believe returns what you define in DataKeyNames. I got confused by the fact that "mykey" is a key and there's a Keys dictionary.

Thanks a lot sydneyos.

Bahadir

sydneyos replied on Monday, September 18, 2006

I am having perhaps a similar problem - I can't tell if you are saying there is a Dictionary entry for "Name" but the value is null, or there is just no entry.  My problem is that I don't even have a dictionary entry for any fielsds where I am using a Template column, and I don't see any way for my Template columns to communicate the field to which they are bound.  Bound columns work as expected (but do have null values rather than empty string) if left blank.

RockfordLhotka replied on Monday, September 18, 2006

You have to understand how ASP.NET data binding works. CslaDataSource doesn't create e.Values - that is created and populated purely by ASP.NET data binding - the data control merely passes the dictionary through from data binding to your code.

You'd have the same basic results using the ObjectDataSource or any other data source - but you'd have a harder time debugging. The reason is that those other data controls use reflection (or other means) to copy the values from the dictionary into your object. The fact that you have access to e.Values itself allows you the option of using reflection (with DataMapper.Map) or using any other technique of your choice to copy the values, and allows for more flexible debugging.

What it sounds like is that you need to set up your template controls differently to support data binding from the template. That's just a matter of using the right ASP.NET tag syntax or control property dialogs in VS 2005, and you should be able to get the correct answers for that sort of thing from a good ASP.NET book.

What I've done in the past (because I'm allergic to books Big Smile [:D]), is create a databound column, and then convert it to a template column so I can look at the tag structure. From that you can typically infer how you need to set up your template controls to get them to be properly data bound.

Remember that the data binding name in those tags defines the name of the value that ends up in the dictionary. If you want to use DataMapper.Map, the name in the control must match the name of the property on your object.

sydneyos replied on Tuesday, September 19, 2006

That's a good trick, but it doesn't seem to translate when the inner control of the template column needs to be a DropDownList - or I'm missing something.  Anyway, my workaround is to not use e.Values - instead I get the GridViewRow, do FindControl and get the value from that.

Copyright (c) Marimer LLC