Проблема связывания WPF (нарушение UNIQUE CONSTRAINT при обновлении), как отклонить изменения? - PullRequest
1 голос
/ 20 февраля 2010

хорошо, пожалуйста, будьте осторожны, я новичок в WPF и LINQ - у меня странная проблема здесь. У меня есть экран поиска и экран добавления / редактирования. У меня есть экран добавления / редактирования, привязанный к объекту «CompanyContact», и экран поиска, привязанный к коллекции (CompanyContacts).

У меня есть уникальное ограничение из 3 столбцов (FirstName, LastName, CompanyId) в таблице db CompanyContact, поэтому нельзя использовать одно и то же имя дважды для одной и той же компании.

Я должен также упомянуть, что у меня есть триггер «AFTER UPDATE» в столбце «ModifiedDate» таблицы CompanyContact, который обновляет ModifiedDate, потому что я не люблю позволять клиентскому ПК определять измененную дату / время ... (я хочу база данных, чтобы отслеживать, когда запись была изменена). Я позволил DEFAULT CONSTRAINT поместить GetDate () в этот столбец на INSERT.

скажем, есть «Стив Смит» на CompanyId 123 и «Стив Смит2» на CompanyId 123

Если я попытаюсь изменить существующий контакт компании (Steve Smith2 @ CompanyId = 123) и изменить фамилию с «Smith2» на «Smith», чтобы сработало ограничение «Unique» (столкновение со Steve Smith @ CompanyId = 123), все, кажется, работает нормально (то есть я сделал так, чтобы экран Edit перехватывал SqlException и «сбрасывал» свойства обратно к их исходным значениям, сбрасывая base.DataContext, и пользователь получает уведомление - «эй, вы не можете сделать это ... это приведет к дублированию записи ") , но когда я закрываю экран редактирования (нажмите кнопку ОТМЕНА) и возвращаюсь к экрану поиска, оскорбительные данные отображаются в результатах поиска ... (то есть теперь есть 2 записи, показывающие Стива Смита @ CompanyId 123)

Я пробовал много вещей, включая написание кода в LINQ для проверки на наличие дубликатов перед попыткой ОБНОВИТЬ ... но кажется, что есть более простое решение, чем это? Я убежден в том, что нужно вносить правила в базу данных, поэтому правила применяются последовательно, т. Е. Правила применяются одинаково для всех, включая тех, кто работает непосредственно с базой данных (на бэкэнде)

вот фрагмент экрана Add / Edit (экран поиска может вызывать эту функцию) ...

    public CompanyContact EditCompanyContact(int companyContactId)
    {
        CompanyContact myCompanyContact;

        try
        {
            _screenMode = ScreenMode.Edit;

            myCompanyContact = new CompanyContactRepository().GetById(companyContactId);

            //experimental code -- use this to reset base DataContext if unique constraint violated...
            _originalCompanyContact = (CompanyContact)myCompanyContact.Clone();

            //make sure to clone the object so we can discard changes if user cancels
            base.DataContext = (CompanyContact)myCompanyContact.Clone();

            SetupScreen();
            this.ShowDialog();

            //if user cancels Edit this is 'reset' to originalCompanyContact 
            return ((CompanyContact)base.DataContext); 

        }
        finally
        {
        }
    }

а вот код от кнопки «Отмена»

    private void btnCancel_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            //HACK: this allows us to discard changes to the object passed in (when in EDIT mode)
            //TODO: research a truer WPF approach? (RoutedEvents?)
            _userCancelled = true;

            base.DataContext = _originalCompanyContact;

            this.Close();
        }
        finally
        {
        }
    }

вот код, который выполняется при попытке сохранить на экране добавления / редактирования:

try
{
  if (base.DataContext != null)
  {
   CompanyContactRepository ccr = new CompanyContactRepository();
   cc = ((CompanyContact)base.DataContext);
   ccr.Save(cc);
  }

  //dismiss the form after saving CompanyContact
  this.Close();
}
catch (SqlException sx)
{
  if (sx.Message.IndexOf("Violation of UNIQUE KEY constraint 'UN_CompanyContact_Value'.") == 0)
  {
    MessageBox.Show(String.Format("a CompanyContact with the name ({1} {0}) already exists for {2}", cc.FirstName, cc.LastName, cc.Company.Name), "Duplicate Record", MessageBoxButton.OK, MessageBoxImage.Exclamation);
  }
  else
  {
    //yes - catch and rethrow is probably a bad practice, but trying to ISOLATE UNIQUE constraint violation
    throw sx;
  }
}
finally
{
}

и вот некоторый LINQ-код для сохранения (извините, это FUGLY! - я целый день хакал с ним)

public void Save(CompanyContact entityToSave)
{
  try
  {

    var saveEntity = (from cc in db.CompanyContacts
            where cc.CompanyContactId == entityToSave.CompanyContactId
            select cc).SingleOrDefault();

    if (saveEntity == null)
    {
      //INSERT logic                    
      entityToSave.CreatedById = new CompanyPersonRepository().GetCompanyPerson(DataContext.Default.LoginUsername).CompanyPersonId;
      entityToSave.ModifiedById = entityToSave.CreatedById;

      db.CompanyContacts.InsertOnSubmit(entityToSave);
      db.CompanyContacts.Context.SubmitChanges();
    }
    else
    {
      //UPDATE logic            
      saveEntity.ModifiedById = new CompanyPersonRepository().GetCompanyPerson(DataContext.Default.LoginUsername).CompanyPersonId;
      saveEntity.CompanyId = entityToSave.Company.CompanyId;
      saveEntity.FirstName = entityToSave.FirstName;
      saveEntity.LastName = entityToSave.LastName;
      saveEntity.CompanyContactTypeId = entityToSave.CompanyContactTypeId;

      db.CompanyContacts.Context.SubmitChanges();

    }
...

1 Ответ

0 голосов
/ 24 февраля 2010

ОК. Я нашел решение, но кажется, что это скорее взлом, чем настоящее решение ... наверняка есть лучший способ исправить это?

Я поместил блок catch в свой код LINQ (в функции Save ()) следующим образом:

if (sx.Message.IndexOf("Violation of UNIQUE KEY constraint 'UN_CompanyContact_Value'.") == 0)
  {
    //this will refresh the cache... so you won't see things that violate constraints
    //showing on the SearchCompanyContacts if you return from Edit CC screen...
    db.Refresh(RefreshMode.OverwriteCurrentValues, db.CompanyContacts);
    throw sx;
  }
  else
  {
    throw sx;
  }

это кажется очень пустым и неудовлетворительным .. Мне действительно не нравится тот факт, что я ловлю и перебрасываю исключение, но это единственный способ получить некоторые детали, объясняющие, почему не удалось сохранить ... это не будет было бы необходимо, если бы мне не нужно было делать вызов db.Refresh () ... (я мог бы просто позволить SqlException пузыриться в стеке вызовов, и я мог бы обработать это с помощью класса ExceptionHandler) ...

Я также изменил логику, так что я возвращаю NULL с экрана Edit, если пользователь отменяет, затем на экране поиска, у меня тоже есть это небольшое изменение ...

selectedCompanyContact = editCompanyContact.EditCompanyContact(selectedCompanyContact.CompanyContactId);

if (selectedCompanyContact == null)
{
  //refresh from db..
  ExecuteSearch();
}

, чтобы на экране поиска обновлялись результаты поиска и отображалось значение «сброс» (т.е. исходные данные).

но, конечно, это не может быть правильным способом написания LINQ? Я ожидал бы, что есть какой-то параметр, который я могу определить, который говорит, что если база данных отклоняет его (нарушен UNIQUE CONSTRAINT, нарушен FOREIGN KEY и т. Д.), То выбрасывает материал, кэшированный в коллекции, который нарушает любые правила базы данных ... сейчас интересно то, что экран поиска и экран редактирования не сразу стирают то, что набрал пользователь, но как только я нажимаю кнопку «Отмена», экран «обновляется», и вы видите исходное значение ...

одна вещь, которая мне нравится в этом решении, это то, что по крайней мере экран (Редактировать) не закрывается, если вы нарушаете правило (вы получаете сообщение «дублирующаяся запись существует»), и вы все равно можете видеть ваши данные «без изменений» в поля, которые привязаны к объекту «для сохранения», так что вы можете просмотреть данные и сказать «ага ... я понимаю, почему эти данные были отклонены», тогда вы можете изменить свои данные, и кнопка «ОК» будет продолжать отклонять ваши неверные данные до тех пор, пока ваши данные не будут соответствовать всем правилам, определенным в базе данных, поэтому, если вы хотите, вы можете нажать кнопку ОТМЕНА, тогда она отменит ваши изменения, и экран результатов поиска обновится до «старых данных».

Я был заинтригован этой функцией:

db.CompanyContacts.Context.GetChangeSet().Updates.Clear();

но, к сожалению, это список только для чтения, поэтому вызов этого не удаётся ... но мне интересно, есть ли способ использовать объект из списка (например, db.CompanyContacts.Context.GetChangeSet (). Updates [0 ]) чтобы иметь возможность вывести нарушающее существо или отсоединить / прикрепить - не уверен, что я чего-то упускаю или нет, но это так.

Я серьезно ... и не называй меня Ширли ...; -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...