Reputation: 17428
I am overriding a sharp architecture repository method like this:
public class UserRepository : NHibernateRepository<User>, IUserRepository
{
public override User SaveOrUpdate(User entity)
{
ICriteria Criteria = Session.CreateCriteria(typeof(User));
Criteria.Add(Expression.Eq("Email", entity.Email));
User User = Criteria.UniqueResult<User>();
if (User != null)
{
if (User.Id != entity.Id)
{
throw new ArgumentException("Please use a unique email address.");
}
}
return base.SaveOrUpdate(entity);
}
}
The ICriteria always returns an entity so the exception is always thrown even though nothing is in the database.
Can someone see why this is? It shouldn't be the case shouldn't it? I must be overlooking something bleeding obvious ...
Thanks.
Christian
PS:
This is more complete code:
namespace x.Domain.Contracts.Repositories
{
using SharpArch.Domain.PersistenceSupport;
using SharpArch.NHibernate.Contracts.Repositories;
public interface IUserRepository : INHibernateRepository<User>
{
}
}
namespace x.Tasks.Repositories
{
using System;
using System.Collections.Generic;
using System.Linq;
using Domain;
using Domain.Contracts.Tasks;
using Domain.Contracts.Repositories;
using SharpArch.NHibernate;
using NHibernate;
using NHibernate.Criterion;
public class UserRepository : NHibernateRepository<User>, IUserRepository
{
public override User SaveOrUpdate(User entity)
{
ICriteria Criteria = Session.CreateCriteria(typeof(User));
Criteria.Add(Expression.Eq("Email", entity.Email));
User User = Criteria.UniqueResult<User>();
if (User != null)
{
if (User.Id != entity.Id)
{
throw new ArgumentException("Please use a unique email address.");
}
}
return base.SaveOrUpdate(entity);
}
}
}
namespace x.Tasks
{
using System;
using System.Collections.Generic;
using System.Linq;
using Domain;
using Domain.Contracts.Tasks;
using SharpArch.NHibernate.Contracts.Repositories;
using NHibernate.Criterion;
using NHibernate;
using x.Web.Mvc.Controllers.ViewModels;
using SharpArch.NHibernate.Web.Mvc;
using x.Domain.Contracts.Repositories;
public class UserTasks : IUserTasks
{
private readonly IUserRepository UserRepository;
public UserTasks(IUserRepository UserRepository)
{
this.UserRepository = UserRepository;
}
[Transaction]
public void CreateUser(CreateUserViewModel CreateUserViewModel)
{
User User = new User();
User.ForeName = CreateUserViewModel.ForeName;
User.LastName = CreateUserViewModel.LastName;
User.Email = CreateUserViewModel.Email;
User.Password = CreateUserViewModel.getSHA512PW(CreateUserViewModel.Password1);
UserRepository.SaveOrUpdate(User);
}
}
}
Even if I comment the ICriteria bit the entity does not even get persisted (despite the transaction attribute). Any ideas?
PPS:
Some more details plus stack trace:
{"query did not return a unique result: 7"}
at NHibernate.Impl.AbstractQueryImpl.UniqueElement(IList list) at NHibernate.Impl.CriteriaImpl.UniqueResult() at NHibernate.Impl.CriteriaImpl.UniqueResultT at EID2.Tasks.Repositories.UserRepository.SaveOrUpdate(User entity) in C:\Users\csetzkorn\Documents\Visual Studio 2010\Projects\EID2\Solutions\EID2.Tasks\Repositories\UserRepository.cs:line 19 at EID2.Tasks.UserTasks.CreateUser(CreateUserViewModel CreateUserViewModel) in C:\Users\csetzkorn\Documents\Visual Studio 2010\Projects\EID2\Solutions\EID2.Tasks\UserTasks.cs:line 33 at EID2.Web.Mvc.Controllers.UsersController.CreateUser(CreateUserViewModel CreateUserViewModel) in C:\Users\csetzkorn\Documents\Visual Studio 2010\Projects\EID2\Solutions\EID2.Web.Mvc\Controllers\UsersController.cs:line 47
Upvotes: 0
Views: 1336
Reputation: 49301
I think that NHibernate is retrieving the entity instance from its first level cache, which implies that Save has already been called on the entity or it is set to be persisted due to a relationship. Is the user object the same as the entity object? If so, you can check for reference equality:
if (user != entity) throw new ArgumentException("...");
No matter how you get this to work, there's a chance that another transaction has inserted a user with the same email address between your check and the transaction committed. It's a small risk for most applications, but I think the best way to guard against this is to also put a unique constraint on the database column and handle the exception.
Upvotes: 1