Член "X" был изменен, чтобы не соответствовать члену ассоциации "Y",

Я получаю эту ошибку, если я выполняю следующие действия:

  • Измените свойство Y объекта (связанное свойство объекта)
  • Попытка представить изменения
  • В этот момент значение Y и значение X (базовый ключ) не согласуются - LINQ to SQL, по-видимому, не синхронизирует их до тех пор, пока не будет вызван GetChangeSet.
  • Ожидаемая ошибка возникает из-за некоторых ограничений бизнес-логики или уровня базы данных во время операции обновления.
  • В этот момент значение Y согласуется с X, потому что был вызван GetChangeSet.
  • Измените значение Y на Nothing (aka null).
  • Вызвать GetChangeSet.

Ошибка возникает на последнем шаге, потому что значение X и исходное значение X (возвращено GetOriginalEntityState) различны, а новое значение не согласуется с Y? Вот почему? Это ошибка в LINQ to SQL. Должно быть потому, что я не вижу такого же поведения, если вместо Y вместо Y вместо другого изменить значение (не нулевое). Каков правильный путь? Я вижу несколько способов:

  • Отбросить DataContext при возникновении ошибки и оставить UI as-is. Мне это не нравится, потому что оптимистические конфликты с изменением коллаборации не могут быть обнаружены. Новый контекст не имеет исходных значений, которые были заполнены в то же время, когда пользовательский интерфейс был заполнен, поэтому, если в пользовательском интерфейсе есть какие-то устаревшие значения, они приведут к возврату данных в базе данных.
  • Обновите datacontext (OverwriteCurrent) и оставьте пользовательский интерфейс как есть. Мне не нравится это по той же причине, что и # 1.
  • Обновите datacontext (OverwriteCurrent) и повторно заполните пользовательский интерфейс. Мне это не нравится, потому что сообщение об ошибке, только что представленное пользователю, не показывает пользователю ошибку, которую они сделали, и позволяет им исправить ее. Он также отбрасывает все другие изменения, которые пользователь мог сделать.
  • При возникновении ошибки явным образом извлекаю ключ для Y, который соответствует исходному значению X и reset Y, затем вызывает GetChangeSet для повторной синхронизации X (X доступен только для чтения или закрыт, поэтому я не могу reset он напрямую). Это похоже на работу, но похоже на хак и может потребовать много кода для других подобных ошибок.

Есть ли лучшее решение. Это что-то, о чем следует сообщить?

Ответы

Ответ 1

Похоже, эта ошибка была исправлена ​​в последней версии .NET и/или Visual Studio 2010. Код, сгенерированный для файла DBML, теперь содержит код для обновления базового значения внешнего ключа, даже если значение свойства ассоциации равно null:

    [global::System.Data.Linq.Mapping.AssociationAttribute(Name="Customer_Order", Storage="_Customer", ThisKey="fkCustomer", OtherKey="id", IsForeignKey=true)]
    public Customer Customer
    {
        get
        {
            return this._Customer.Entity;
        }
        set
        {
            Customer previousValue = this._Customer.Entity;
            if (((previousValue != value) 
                        || (this._Customer.HasLoadedOrAssignedValue == false)))
            {
                this.SendPropertyChanging();
                if ((previousValue != null))
                {
                    this._Customer.Entity = null;
                    previousValue.Orders.Remove(this);
                }
                this._Customer.Entity = value;
                if ((value != null))
                {
                    value.Orders.Add(this);
                    this._fkCustomer = value.id;
                }
                else
                {
                    this._fkCustomer = default(Nullable<int>);
                }
                this.SendPropertyChanged("Customer");
            }
        }
    }

Я думаю, что this._fkCustomer = default(Nullable<int>);, должно быть, не было в коде, который я тестировал, когда я изначально разместил этот вопрос. Так что либо я неправильно настроил свой DBML, либо эта проблема была исправлена.

Ответ 2

В MSDN:

Для обновлений отношений ссылка на дочерние элементы родителя (то есть ссылка, соответствующая внешнему ключу) считается авторитетом. Ссылка в обратном направлении (то есть от родителя к дочерней) является необязательной. Классы отношений (EntitySet и EntityRef) гарантируют согласованность двунаправленных ссылок для отношений "один ко многим" и "один к одному". Если объектная модель не использует EntitySet или EntityRef, и если обратная ссылка присутствует, вы несете ответственность за ее соответствие с прямой ссылкой при обновлении отношений.

Если вы обновите требуемую ссылку и соответствующий внешний ключ, вы должны убедиться, что они согласны. Исключение InvalidOperationException генерируется, если они не синхронизируются в то время, когда вы вызываете SubmitChanges. Хотя изменения значения внешнего ключа достаточны для влияния на обновление базовой строки, вы должны изменить ссылку, чтобы поддерживать связь графика объекта и двунаправленную согласованность отношений.

http://msdn.microsoft.com/en-us/library/Bb386982(v=VS.90).aspx

Ответ 3

Недавно я столкнулся с этой проблемой, используя linq для sql, мои классы, которые были сгенерированы с помощью sqlmetal, и отображались с использованием привязок WPF.

При отображении combox значений из таблицы у меня была ссылка с внешним ключом, я устанавливал SelectedValuePath в ID. Однако, когда идентификатор был обновлен в моей записи, связанный элемент не был обновлен, чтобы соответствовать значению ИД.

(Address.provID будет равен 1 и Address.Province будет null)

Я обновил привязку, чтобы обновить SelectedItem, а не SelectedValuePath.

<ComboBox Name="provList" DisplayMemberPath="Code" ItemsSource="{Binding Source={x:Static list:GlobalList.ProvinceList}}" SelectedItem="{Binding Path=Provinces}" Width="75" Height="23" HorizontalAlignment="Left" Margin="5,0,0,0" VerticalAlignment="Top"/>