views:

16

answers:

1

I've got an order details form in Silverlight that has a listbox of order payments. The order payments are bound to their own domain datasource outside of the surrounding order. I have a button that pops up a ChildWindow control to add new order payments. An order payment has an amount and a payment method associated with it.

The form loads fine, displays all payment methods in a dataform in a dropdown along with the textbox for the amount. When I save the order payment and try to attach it back to the order details Order Payments datasource, I get the dreaded 'Entity cannot be attached because it is already attached to another entity' error.

Here's the XAML for the Add Order Payment child window:

<controls:ChildWindow.Resources>
    <riaControls:DomainDataSource x:Key="paymentMethodsSource" QueryName="GetPaymentMethods" AutoLoad="True">
        <riaControls:DomainDataSource.DomainContext>
            <ds:CPSDomainContext />
        </riaControls:DomainDataSource.DomainContext>
    </riaControls:DomainDataSource>
</controls:ChildWindow.Resources>

<dataForm:DataForm x:Name="addOrderPaymentDataForm"  AutoGenerateFields="False" AutoCommit="True" AutoEdit="True" CommandButtonsVisibility="None">
        <dataForm:DataForm.EditTemplate>
            <DataTemplate>
                <StackPanel>


                    <dataForm:DataField Label="Payment Method">
                        <ListBox ItemsSource="{Binding Data, Source={StaticResource paymentMethodsSource}}" 
                                 DisplayMemberPath="Name"
                                 SelectedItem="{Binding PaymentMethod, Mode=TwoWay}"
                                 SelectionMode="Single"/>
                    </dataForm:DataField>

                    <dataForm:DataField Label="Amount">
                        <TextBox Text="{Binding Amount, Mode=TwoWay}" />
                    </dataForm:DataField>

                </StackPanel>
            </DataTemplate>
        </dataForm:DataForm.EditTemplate>
    </dataForm:DataForm>

Here's the code behind for the Add Order Payment screen:

public AddOrderPaymentWindow() {
        InitializeComponent();
        NewOrderPayment = new OrderPayment();
        addOrderPaymentDataForm.CurrentItem = NewOrderPayment;
        addOrderPaymentDataForm.BeginEdit();
    }

    public OrderPayment NewOrderPayment { get; set; }

    private void OKButton_Click(object sender, RoutedEventArgs e) {
        NewOrderPayment.CreatedBy = "jkandiko";
        NewOrderPayment.CreatedOn = DateTime.Now;
        NewOrderPayment.ModifiedBy = "jkandiko";
        NewOrderPayment.ModifiedOn = DateTime.Now;
        var result = addOrderPaymentDataForm.CommitEdit();
        this.DialogResult = true;
    }

    private void CancelButton_Click(object sender, RoutedEventArgs e) {
        NewOrderPayment = null;
        addOrderPaymentDataForm.CancelEdit();
        this.DialogResult = false;
    }

Finally, here is the code that tries to reattach the new order payment to the order details screen:

 void add_Closed(object sender, EventArgs e) {
        AddOrderPaymentWindow pay = (AddOrderPaymentWindow)sender;
        if (pay.NewOrderPayment != null) {
            Administration.Web.Services.CPSDomainContext context = (CPSDomainContext)orderPaymentDataGridSource.DomainContext;

            context.OrderPayments.Add(pay.NewOrderPayment);
            context.SubmitChanges();
        }
    }

Am I running into this problem because the Order Payment has a property on it that is bound to a different data context? Can I even add a child object in this way, given that the child object has to load objects from RIA services in order for it to be saved? Should I somehow pass the domaindatasource from the order details page to the child control to have one datasource? Or do I have to revert to doing a clone in code to get a new object?

+1  A: 

I'd recommend you pass the DomainContext from the parent window (the one you use in add_Closed) to the child window and then use that DomainContext in your DomainDataSource (paymentMethodsSource). That way all the entities are being loaded into the same context. An alternate approach would be to do the query (GetPaymentMethods) in the parent window, pass the context to the child window, and just bind the ListBox.ItemsSource to CPSDomainContext.PaymentMethods in your child window.

Kyle McClellan