Comment gérer la mise à jour des entités. NHibernate + ASP.NET MVC
Je ne peux pas mettre à jour précédemment créé entité. Je suis un StaleObjectException
exception avec le message:
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Project.DomainLayer.Entities.Employee#00000000-0000-0000-0000-000000000000]
Je ne partage pas le processus de mise à jour avec quelqu'un. Quel est le problème?
D'Accès aux données /DI
public class DataAccessModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
this.Bind<ISessionFactory>()
.ToMethod(c => new Configuration().Configure().BuildSessionFactory())
.InSingletonScope();
this.Bind<ISession>()
.ToMethod(ctx => ctx.Kernel.TryGet<ISessionFactory>().OpenSession())
.InRequestScope();
this.Bind(typeof(IRepository<>)).To(typeof(Repository<>))
.InRequestScope();
}
}
D'Accès Aux Données /Mappages
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Project.DomainLayer" namespace="Project.DomainLayer.Entities">
<class name="Employee" optimistic-lock="version">
<id name="ID" column="EmployeeID" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb" />
</id>
<version name="Version" type="Int32" column="Version" />
<!-- properties -->
<property name="EmployeeNumber" />
<!-- ... -->
<property name="PassportRegistredOn" not-null="true" />
<!-- sets -->
<set name="AttachedInformation" cascade="all">
<key column="EmployeeID" />
<element column="Attachment" />
</set>
<set name="TravelVouchers" cascade="all">
<key column="EmployeeID" />
<one-to-many class="TravelVoucher" />
</set>
</class>
</hibernate-mapping>
D'Accès Aux Données /Référentiel
public class Repository<T> : IRepository<T> where T : AbstractEntity<T>, IAggregateRoot
{
private ISession session;
public Repository(ISession session)
{
this.session = session;
}
//other methods are omitted
public void Update(T entity)
{
using(var transaction = this.session.BeginTransaction())
{
this.session.Update(entity);
transaction.Commit();
}
}
public void Update(Guid id)
{
using(var transaction = this.session.BeginTransaction())
{
this.session.Update(this.session.Load<T>(id));
transaction.Commit();
}
}
}
À l'intérieur d'un Contrôleur de
public class EmployeeController : Controller
{
private IRepository<Employee> repository;
public EmployeeController(IRepository<Employee> repository)
{
this.repository = repository;
}
public ActionResult Edit(Guid id)
{
var e = repository.Load(id);
return View(e);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Employee employee)
{
if(ModelState.IsValid)
{
repository.Update(employee);
return RedirectToAction("Deatils", "Employee", new { id = employee.ID });
}
else
{
return View(employee);
}
}
}
Comment puis-je mettre à jour mon entités?
Merci!
MODIFIER
J'ai donc ajouté unsaved-value="{Guid.Empty goes here}"
au balisage. De plus, j'ai essayé de faire la chose suivante: l'
public void Update(T entity)
{
using(var transaction = this.session.BeginTransaction())
{
try
{
this.session.Update(entity);
transaction.Commit();
}
catch(StaleObjectStateException ex)
{
try
{
session.Merge(entity);
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
Et cela me donne le même effet.. je veux dire transaction.Commit();
après Merge
donne la même exception.
Aussi je me demande dois-je exposer, à l'aide caché d'entrée, l'entité ID
sur le Edit
vue?
MODIFIER
Si l'entité se détache vraiment. Quand il passe sur le contrôleur de la ID
est égal à Guid.Empty
. Comment dois-je traiter, Merge
ou Reattach
?
OriginalL'auteur lexeme | 2012-03-14
Vous devez vous connecter pour publier un commentaire.
Il y a deux scénarios que vous pouvez exécuter dans votre modèle de code.
Vous pouvez récupérer l'objet à partir de la db à l'aide de
ISession.Get()
qui peut être suivie par une modification/mise à jour de l'objet récupéré. Pour ce changement, pour être efficace, tout ce que vous devez faire est de rincer la session ou de valider la transaction en tant que Nhibernate permettra de suivre tous les changements pour vous automatiquement.Vous avez un transitoire exemple, un objet qui n'est pas associé avec le
ISession
en contexte, à partir de laquelle vous voulez mettre à jour. Dans ce cas, de mon expérience, la meilleure pratique consiste àISession.Get()
l'objet et de faire les modifications correspondantes de l'objet que vous venez de récupérer. (habituellement, votre modèle de vue est différent de votre modèle de domaine ainsi, ne pas mélanger les deux) Ce modèle est illustré ci-dessous. Il travaille tout le temps. Assurez-vous également utiliserISession.SaveOrUpdate()
.Aussi, remarquez que votre contrôleur est probablement instancié par demande, par conséquent, la durée de vie de votre
ISession
ne couvrent pas les appels multiples à différentes méthodes que vous avez dans votre contrôleur. En d'autres termes, chaque méthode est presque toujours de travail dans le contexte d'une nouvelleISession
(unité de travail).employee
avecid == Guid.Empty
?Lorsque vous mettez à jour l'objet, l'ID ne doit pas être Guid.Vide. Seulement quand vous êtes dans le processus de création/enregistrement du nouvel objet, il sera. L'ID peut être générée par votre application ou à l'aide de la base de données. Voir nhibernate de documentation
Regarde ma réponse ci-dessous. C'est la façon dont il fonctionne. Des conseils sont appréciés.
Super réponse, selon shevchyk réponse, je pense que vous voulez dire "Vous avez un
detached
exemple" stackoverflow.com/questions/161224/...OriginalL'auteur Newbie
Votre logique n'est pas bonne, parce que vous utilisez le modèle de domaine comme Employé comme ViewModel. La meilleure pratique est d'utiliser CreateEmploeeViewModel et EditEmployeeViewModel et de séparer la Logique de Domaine et le Modèle de Vue de la logique.
Par Exemple:
Pour convertir d'un Employé à un ViewModel je préfère yo utiliser Automapper.
De sorte que les Actions du contrôleur de devenir l'ressemble:
Dans ce cas, NHibernate sait que vous la mise à jour de l'Employé, et vous ne pouvez pas supprimer certaines propriétés qui n'existent pas dans votre Vue.
bien que vous avez raison, domainmodel vs viewmodel est bon. Mais il n'a pas de rapport avec la question.
Si vous regardez la réponse, vous pouvez voir quelle est la solution. L'Option 2.
OriginalL'auteur Ivan Korytin
Je crois que votre objet Employé a devenir ce que NHibernate appelle "détaché" entre GET et POST de votre Modifier les méthodes d'action. Voir la NHibernate documentation sur ce sujet pour plus de détails et quelques solutions. En fait, le lien décrit exactement GET-POST scénario vous semble à l'aide d'.
Vous mai besoin de le remettre à votre Salarié de l'objet et/ou de préciser les "non enregistrées valeur" que le Service proposé, de sorte que NHibernate connaît un Employé avec un ID de Guid.Vide n'a pas été conservée dans la base de données. Sinon, comme le Service proposé, NHibernate voit Guid.Vide comme une carte d'identité valide, et pense que l'objet a déjà été enregistré dans la base de données, mais la session dans laquelle il a été récupéré, a été éliminée (par conséquent, l'objet devient "détaché").
Espère que cette aide.
OriginalL'auteur Jason Iwinski
Vous demander,
Oui, vous devriez le faire. Vous devriez également exposer le Version caché dans un entrée de son entreprise, c'est pour aider à prévenir les modifications simultanées de la même entité. Le StaleObjectException conseils que vous avez versioning allumé, et dans ce cas, la mise à jour ne fonctionnera que si la valeur de la version (Int32) que vous renvoyez est identique à celui dans la base de données.
Vous pouvez toujours obtenir autour de lui par le rechargement de l'entité et de la cartographie, en s'assurant que la valeur de la Version est susceptible de correspondre, mais qui semble renverser son but.
À mon humble avis, je mets l'ID de l'entité et la Version cachée de l'entrée, et sur la publication, de rechargement de l'entité et la carte de données. De cette façon, comme Ivan Korytin suggère ci-dessus, vous n'auriez pas à transporter des propriétés qui ne sont pas nécessaires dans votre point de vue. Vous pouvez également gérer l'insipidité au niveau du contrôleur et ajouter une erreur de validation plutôt que d'avoir NHibernate vous dire que votre objet est vicié.
Ivan Korytin décrit la procédure standard pour la manipulation d'une simple modification d'une entité. La seule question avec sa réponse est qu'il ne traite pas de la Version de la propriété. À mon humble avis, la base de données ne doit pas être spécifique à la version ou la Version de propriété devrait avoir de l'importance.
Automapper
correspondant viewmodel résultat dans des entités détachement de tout modifier/mettre à jour? En utilisant un autre 3d partie sowtware rend l'application plus complexe. Ce que je veux savoir, c'est comment faire de modifier/mettre à jour avecnhibernate
sans se détacher des entités.Le processus qui provoque le détachement en fin de compte, c'est que vous êtes dans un environnement web, pas un environnement de bureau. Disons que vous êtes en utilisant ASP.Net MVC, et votre utilisateur fait un changement dans un formulaire d'édition et clique sur le bouton envoyer. Le modèle de liaison va se lier à un "modèle" (dans ce cas, votre nom de domaine objet) qui a une valeur null constructeur et faire de son mieux pour affecter des valeurs à ce "modèle objet" fonction de ce qui a été posté en arrière.
Si vous étiez absolument insister sur la recherche d'une solution dans laquelle le modèle objet est entièrement attaché entité, je suis sûr qu'il y est un moyen, mais il serait intéressant que dans un sens académique. En termes de création de logiciels et de fournir des logiciels de manière efficace et de maintenir une base de code qui est facile pour les autres développeurs qui viennent plus tard, il est plus sage de suivre les approches standard. Il n'est pas nécessaire pour une 3ème partie automapper: vous pouvez mapper les propriétés manuellement, et il n'y a rien non valide au sujet de cette approche.
OriginalL'auteur brightgarden
"non enregistrées valeur" est manquant. donc NH pense que Guid.Vide est une pièce d'identité valide
OriginalL'auteur Firo
Si vous voulez mettre à jour une entité de champs que vous n'avez pas besoin d'utiliser de session.Mise à jour(),
session d'utilisation.Flush() avant la clôture de la transaction.
session.Mise à jour() -> mise à Jour de l'instance persistante avec l'identifiant de l'instance éphémère.
OriginalL'auteur Anton
Si vous êtes l'un de nous qui ne répondent pas ici aidé, essayez de rechercher ce qui, pour un "ID" dans votre entité est l'envoi.
J'ai le même problème mais à la fin, j'ai vu que j'étais en train de changer l'ID d'un autre nombre (dans NHibernate l'id auto-généré, si vous le configurer de cette façon!).
Donc, en bas de la ligne, de vérifier si la structure des données que vous envoyez et les valeurs correspondent à ce que vous vous attendez à envoyer.
Espère que je peux aider quelqu'un! 🙂
OriginalL'auteur C3PO
Après tout cela aide, mais je pense que c'est horrible:
Même si j'ai mis
HiddenFor
champs surEdit
vue pourID
(etVersion
) ID passée ordinaire unGuid.Empty
qui stipule queemployee
est transitoire.Je suis vraiment apprécié pour votre aide les gars!
Questions
I know what viewmodels are, but quite not understood how does it help with detaching
.Why if I put TextBoxFor(e => e.ID) on Edit view it binds employee like a transient entity without saving the ID value?
permet d'essayer de garder tout le contenu de votre question dans la question elle-même. C'est la partie de l'Étiquette autour d'ici, et il est plus facile de comprendre votre question. De toute façon, comment allez-vous générer vos Identifiants (Id) ?
OriginalL'auteur lexeme