Question

Je voudrais gérer le cas où un utilisateur est l'édition d'un objet à partir d'une base de données dans une application Windows Forms, fait une modification qui serait en violation d'une contrainte de base de données (c'est à direla colonne valeur unique), permet d'économiser l'entité la base de données, et NHibernate déclenche une exception donc bousiller la session.

Je suis en utilisant le document d'orientation publié dans l'article MSDN La construction d'un Bureau À Faire de l'Application avec NHibernate et à l'aide d'une session-par-présentateur/approche de forme.Lors de la création de l'animateur, je garde une référence à la SessionFactory, créer une nouvelle session, de récupérer l'objet à partir de la base de données via la session et de stocker une référence à elle [l'objet].Lorsque le formulaire est affiché, ses champs sont renseignés à partir de l'objet.

Lorsque des modifications sont apportées à la forme et à l'utilisateur souhaite enregistrer les données, l'objet est mis à jour à partir des valeurs de champ, et l'objet est enregistré dans la base de données.

J'ai poignée rassis-l'état d'exception où l'objet est modifiée entre le moment où il a été extrait de la base de données et quand il est enregistré:Une nouvelle session est créée, l'objet d'origine est à nouveau chargé, l'utilisateur est informé qu'un conflit a eu lieu et il est présenté avec ses changements et de ce qui est actuellement dans la base de données.Il peut choisir d'enregistrer ses modifications ou sur annuler (et accepter ce qui est maintenant stocké dans la base de données).

Dans le cas d'une violation de contrainte, il semblerait que l'une des deux choses peuvent arriver:

  1. L'objet n'a pas changé dans la base de données où elle peut être rechargée dans la nouvelle session.
  2. L'objet a également été modifié dans la base de données et ne correspond plus à ce qui avait été initialement chargé.

Cependant, je ne pense pas que je peut effectivement détecter les cas survenus depuis un état à l'état d'exception n'est jamais levée, car la contrainte exception s'est produite (j'ai testé).

La manipulation de cas 1 est trivial puisque je ne peux tout simplement afficher un message d'erreur qui dit "CHAMP-X a une valeur qui est déjà dans la base de données" et de faire semblant que rien n'a vraiment mal tourné.L'utilisateur peut modifier les champs-X à une valeur unique, et l'enregistrer de nouveau sans avoir à ressaisir ses changements.

La manipulation de cas 2 serait comme de la manipulation régulière vicié à l'état d'exception.

Je sais que je peux "force brute" en gardant une copie de l'original (ou des valeurs), puis de comparer les deux champs. Cependant, je soupçonne qu'il y a une meilleure façon de gérer cela en tirant parti de NHibernate. Comment voulez-vous gérer cela?

Dans le cas où il est utile:

  • NHibernate Version:3.2
  • Optimiste de verrouillage à l'aide de "sale" de la stratégie
  • .NET Framework 2.0 SP2
  • La base de données SQLite/pilote

EDIT 23 Fév - j'ai ajouté un exemple de code pour mieux illustrer ce que je suis en train de faire.

/**
 * Save handler called when user initiates save in UI.
 * 
 * Returns true when save was successful (essentially, tells the presenter
 * that the UI can be closed.
 */
private bool SaveData()
{
    try
    {
        if (this.creatingNewUserAccount)
        {
            // Do whatever is necessary to instantiate a new object.
            this.userAccount = new UserAccount();
            // and copy values from the UI into the new object.
            this.userAccount.Name = this.form.Name;
            // etc.
        }
        else
        {
            // Just copy values from the UI into the existing object
            // from the database.
            this.userAccount.Name = this.form.Name;
            // etc.
        }

        using (ITransaction tx = this.session.BeginTransaction())
        {
            this.accountRepository.store(this.userAccount);
            tx.Commit();
        }

        return true;
    }
    catch (StaleObjectStateException)
    {
        HandleStaleStateException();
        return false;
    }
    catch (ArgumentException e)
    {
        this.m_View.ShowOtherDialog(e.Message);
        return false;
    }
    catch (GenericADOException e)
    {
        HandleConstraintViolationException();
        return false;
    }
}

private void HandleStaleStateException()
{
    // The session was trashed when the exception was thrown,
    // so close it and create a new one.
    this.session.Dispose();
    this.session = this.sessionFactory.OpenSession();
    CurrentSessionContext.Bind(this.session);

    // Reload the object from the database.
    this.userAccount = LoadData();

    // Do a bunch of things that deal with informing the user
    // of the stale-state and displaying a form to merge changes.
    HandleEditConflict();
}

private void HandleConstraintViolationException()
{
    // The session was trashed when the exception was thrown,
    // so close it and create a new one.
    this.session.Dispose();
    this.session = this.sessionFactory.OpenSession();
    CurrentSessionContext.Bind(this.session);

    // Determine if trying to save a new entity or editing an existing one.
    if (this.creatingNewUserAccount)
    {
        // If saving a new entity, we don't care about the old object
        // we created and tried to save.
        this.userAccount = null;
    }
    else
    {
        // ????
    }
}
Était-ce utile?

La solution

L' ISession.Refresh(Object obj) la méthode a été ce qui a fini par travailler pour moi.Le code de ma question reste la même, sauf pour la finale de la méthode:

private void HandleConstraintViolationException()
{
    // The session was trashed when the exception was thrown,
    // so close it and create a new one.
    this.session.Dispose();
    this.session = this.sessionFactory.OpenSession();
    CurrentSessionContext.Bind(this.session);

    // Determine if trying to save a new entity or editing an existing one.
    if (this.creatingNewUserAccount)
    {
        // If saving a new entity, we don't care about the old object
        // we created and tried to save.
        this.userAccount = null;
    }
    else
    {
        this.session.Refresh(this.userAccount);
    }
    this.form.ShowDialog("... Describe the constraint violation ...");
}

Autres conseils

vous pourriez

// after ConstraintException with new session

session.Lock(loadedObject, LockMode.None);  // attach object with session
// copy back from UI
session.Flush();
catch()
{
    if (ConstraintException)
        // repeat
    else if (Stale)
        // handle like you have
    else
        // all ok
}

Si vous n'êtes pas intéressé par ce qui est dans dB

// loads from the database, copy state from object into it and returns the loaded object (attached to session), changes will be updated on next flush
obj = session.Merge(obj);

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top