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

时间:2012-04-17 13:01:47

标签: c# asp.net objectdatasource formview

我尝试更新FormView

时出现此错误
  

无法在类型上找到名为“MainContact.FirstName”的属性   由ObjectDataSource中的DataObjectTypeName属性指定   'odsForm'。

我认为这是因为我在EditTemplate中使用了像这样的文本框

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

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

这是FormView的数据源

<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>

这是EntryItem类

 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

我该如何解决这个问题?

3 个答案:

答案 0 :(得分:4)

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

    <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,FormViews等以存储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>

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

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

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将只读取/写入他们需要的字段。

答案 1 :(得分:1)

两种可能的方法。

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

但这可能无济于事,因为Bind/Eval可能无法关注引用(Bind("MainContact.FirstName"))。

相反,将其重写为

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

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

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 );
}

答案 2 :(得分:0)

根据一些消息来源,实际上不可能使用嵌套属性进行双向绑定。

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

此外,there's a blog post描述了这个问题:

  

现在,在大多数情况下使用ObjectDataSource仍然不会导致任何问题,当你绑定的是一个简单的属性,例如客户端的名称,即:你有一组绑定到的客户端一个GridView,其中一列显示客户端的名称。为此,您可以使用Bind("Name")之类的内容。当您需要绑定到子属性(如in)时会出现问题   Bind("Address.StreetName")。这不起作用