在 FormView 中找不到名为“xxx.yyy”的属性(嵌套属性的双向绑定)

本文关键字:属性 嵌套 绑定 yyy 找不到 FormView xxx | 更新日期: 2023-09-27 18:31:27

当我尝试更新 FormView 时遇到此错误

在类型上找不到名为"MainContact.FirstName"的属性 由对象数据源中的 DataObjectTypeName 属性指定 "odsForm"。

我认为这是因为我在编辑模板中使用了这样的文本框

<asp:TextBox Text='<%# Bind("MainContact.FirstName") %>' ID="txtFirstName" runat="server" />
它在

文本框中显示正确的文本,但显然它在更新时不起作用。

这是窗体视图的数据源

<asp:ObjectDataSource ID="odsForm" runat="server" DataObjectTypeName="Helpers.BusinessObjects.EntryItem"
    SelectMethod="GetEntryByEmail" TypeName="Helpers.DataAccessers.EntryHelper"
    UpdateMethod="UpdateEntry">
    <SelectParameters>
        <asp:SessionParameter SessionField="email" Name="email" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>

这是条目项类

 public class EntryItem
    {
        public int Id { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public Person MainContact { get; set; } 
        ...
    }

和人类

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    ...
}

调试器进入 FormView ItemUpdating事件处理程序,但永远不会进入Helpers.DataAccessers.EntryHelper.UpdateEntry

我该如何解决这个问题?

在 FormView 中找不到名为“xxx.yyy”的属性(嵌套属性的双向绑定)

您可以编写自己的控件,以便根据需要执行绑定,以这种方式使用(我制作了其中之一):

    <ItemTemplate>
      <%# Eval("MainContact.FirstName")%>
    </ItemTemplate>
    <EditItemTemplate>
      <xx:BinderHelper runat="server" DataSource='<%# Bind("MainContact") %>'>
        <ItemTemplate>
          <asp:TextBox Text='<%# Bind("FirstName") %>' ID="txtFirstName" 
             runat="server" />
        </ItemTemplate>
      </xx:BinderHelper>
    </EditItemTemplate>

无论如何,我建议你不要直接在页面中使用域对象,总的来说不要使用 ObjectDataSource 编写它们。问题是,当您更改域(例如添加字段)时:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    // just added
    public DateTime? BirthDate { get; set; }
}

然后,您需要更改所有 GridViews、FormView 等来存储 BirthDate,否则框架将调用 ObjectDataSource Update 方法,将null放入 BirthDate 中。例如:

<asp:GridView runat="server" DataSourceID="odsForm" AutoGenerateColumns="False">
    <Columns>
      <asp:CommandField runat="server" ShowEditButton="True" />
      <asp:BoundField DataField="FirstName" />
      <asp:BoundField DataField="LastName" />
 </Columns>
  </asp:GridView>

它将从数据库中读取您的人员。每个人都会设定一个出生日期。保存时,此人将使用"出生日期"更新为 null ,因为 GridView 不存储新字段。

我认为最好的解决方案是为数据绑定编写 DTO(并将它们保留在表示层中)和数据对象。在您的情况下:

public class EntryItemView
{
    public int Id { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public string MainContactFirstName { get; set; } 
}
[DataObject]
public class EntryItemViewDataObject {
   [DataObjectMethod(DataObjectMethodType.Select)]
   public EntryItemView GetItem(...) {
       // TODO: read from the database, convert to DTO
   }
   [DataObjectMethod(DataObjectMethodType.Update)]
   public void Update( EntryItemView entry) {
      EntryItem domainObject = getById(entry.Id);
      // TODO: use EmitMapper or AutoMapper
      domainObject.MainContact.FirstName = entry.MainContactFirstName;
      // TODO: save
   }
}
这样,任何

添加到域的内容对于您的视图都是安全的,并且 DataObjects 将仅读取/写入它们所需的字段。

两种可能的方法。

首先,您可以从ObjectDataSource的定义中删除DataObjectTypeName="Helpers.BusinessObjects.EntryItem"。我从来没有用过它,绑定总是有效的。

但这可能不会有所帮助,因为Bind/Eval可能无法遵循参考文献(Bind("MainContact.FirstName"))。

相反,将其重写为

<%# ((EntryItem)Container.DataItem).MainContract.FirstName #>

缺点是你松散了自动双向绑定,所以你必须帮助粘合剂一点。只需将Inserting/Updating处理程序添加到 ObjectDataSource 和内部处理程序:

protected void TheObjectDataSource_Updating( object sender, BlahBlahEventArgs e )
{
    // find the control in the data bound parent
    TextBox txt = (TextBox)YourFormView.FindControl( "txtFirstName" );
    // read the value and add it to parameters
    e.Parameters.Add( "nameofyourparameter", txt.Text );
}

根据几个来源,实际上不可能对嵌套属性进行双向绑定。

以下是SO上类似问题的一个答案:https://stackoverflow.com/a/1195119/370671。

此外,还有一篇描述该问题的博客文章:

现在,在大多数情况下,当 [you] 绑定的是一个简单的属性(例如客户端的名称)时,仍然使用 ObjectDataSource 不会给您带来任何问题,即:您有一个绑定到GridView的客户端集合,其中一列显示客户端的名称。为此,您可以使用类似 Bind("Name") .当您需要绑定到子属性(例如 Bind("Address.StreetName") .这行不通