I'm building an N-tier application which has to send JSON data, which is read from SQL Server 2012 through Enity Framework.
When I try to request a collection of users I get an "An error has occurred" page. It works with hardcoded data.
This is my code:
public IEnumerable<User> Get()
{
IUserManager userManager = new UserManager();
return userManager.GetUsers();
}
public IEnumerable<User> GetUsers()
{
return repo.ReadUsers();
}
public IEnumerable<User> ReadUsers()
{
IEnumerable<User> users = ctx.Users.ToList();
return users;
}
"ctx" is a reference to a DbContext-object.
EDIT: This works:
public IEnumerable<User> Get()
{
IList<User> users = new List<User>();
users.Add(new User() { FirstName = "TestPerson1" });
users.Add(new User() { FirstName = "TestPerson2" });
return users;
}
Browser screenshot: http://i.imgur.com/zqG0qe0.png
EDIT: Full error (screenshot): http://i.imgur.com/dt48tRG.png
Thanks in advance.
If your website returns internal error and no call stack you are not seeing the full exception(which makes it kind of hard to exactly point out your problem).
So first of all to get to the actual exception with call stack you have 2 methods.
Debug the website : start the website locally or attach your debugger to a locally running website. While stepping through the code the debugger will stop when it hits an exception and you'll see the exception details then.
Disable custom errors : IIS wants to protect your internal workings so standard behavior is not to show full exceptions. To disable this behavior edit your web.config and add the xml node under
After you get the actual exception please update your question with call stack & the real internal server error. My guess is that you have a serialization issue, maybe a circular reference of some sort. You're fix would be to either make a simpel viewModel(and not return the entity directly) or add serialization settings(json.net support circular references for example).
Edit
As suspected the serialization is giving you a hard time. The cause is the proxy creation used by the lazy loading.
You can disable the proxy creation with the following code(make note that this also disables lazy loading).
public IEnumerable<User> ReadUsers()
{
ctx.Configuration.ProxyCreationEnabled = false;
IEnumerable<User> users = ctx.Users.ToList();
return users;
}
if this works you might consider disabling proxy creation during the context initialization.
Try to return plain model, smth like
var logins = ctx.Users.Select(user => new FlatUserModel
{
Id = user.Id,
Name = user.UserName,
Email = user.Email
})
.ToArray();
return logins;
Also look in browser what do you get in response.
As it works with hard coded data there is possibility of lazyloading enabled on context.
If so disable lazy loading in the context or like this so that serialization to JSON does not load the entities from database.
public IEnumerable<User> ReadUsers()
{
ctx.Configuration.LazyLoadingEnabled = false;
IEnumerable<User> users = ctx.Users.ToList();
return users;
}
Related
Part of my application requires the importing of users at a large scale, this information is supplied via an excel document which is uploaded via a View.
Due to the size of this batch and several other processes which need to be kicked off for each user, including welcome emails that take quite a long time to complete I have opted to use threads to do this to avoid any long loading screens to the user.
In order to use threads I have needed to make several variables accessible to the threaded function, including ApplicationUserManager, HttpRequestBase and UrlHelper which all get populated in the ActionResult function that starts this Thread
This is the controller function that populates this information:
public ActionResult importExternalUsers()
{
BulkUserImportProcessHelperModel helperModel = new BulkUserImportProcessHelperModel();
helperModel.userManager = this.HttpContext.ApplicationInstance.Context.GetOwinContext().GetUserManager<ApplicationUserManager>(); ;
helperModel.urlContext = this.Url;
helperModel.httprequestBase = this.Request;
helperModel.filePath = #"C:\Users\tiaan\Desktop\MultipleUserCreation.xlsx";
Thread thread = new Thread(new ParameterizedThreadStart(bulkUserImportProcess));
thread.Start(helperModel);
return View();
}
As you can see the models get populated then a thread is created. I made use of a "Helper model" in that case just to pass the parameters to the function I started in a Thread as I could not find an easy way, if any at all, to pass multiple parameters to a function designed for threaded use.
This is the threaded function which imports the Excel document and starts the loop that registers the users:
public static void bulkUserImport(object objectParams)
{
BulkUserImportProcessHelperModel helperModel = (BulkUserImportProcessHelperModel)objectParams;
List<MedicalProfessionalRegistration> usersToReg = IO.InputOutput.ImportExcelDocument(helperModel.filePath);
foreach (var user in usersToReg)
{
AccountController.registerUser(user, helperModel.userManager, helperModel.urlContext, helperModel.httprequestBase);
}
}
So far, although probably a little unconventional, all is working. Adding a breakpoint in this function after the creation of the helperModel variable reveals that all the fields were populated as expected.
My problems start at the static AccountController function responsible for the creation of the users.
public static bool registerUser(MedicalProfessionalRegistration model, ApplicationUserManager userManager, UrlHelper urlHelper, HttpRequestBase request)
{
string password = membershipPasswordGenerator(10, 10, 5, 1);
model.Password = password;
model.ConfirmPassword = password;
var user = new ApplicationUser { UserName = model.UserName, Email = model.Email };
var result = userManager.Create(user, model.Password);
if (result.Succeeded)
{
IO.Identities.registerUser(user.Id, IO.IdentityRank.User);
IO.Identities.storeUserIdentityMeta(user.Id, model);
string code = userManager.GenerateEmailConfirmationToken(user.Id);
string callbackUrl = urlHelper.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code, rst = "T" }, protocol: request.Url.Scheme);
IO.Email.confirmEmailAccountRegister(user.Email, callbackUrl);
return true;
}
else
{
return false;
}
}
Currently, I am getting an exception on
var result = userManager.Create(user, model.Password);
that reads:
This exception was originally thrown at this call stack:
System.Data.Entity.Internal.LazyInternalContext.Connection.get()
What's weird to me is that sometimes it actually populates the database with a single user from this list then seems to fail on the next iteration of the loop in bulkUserImport. While other times it fails on the first one almost immediately.
I believe it has something to do with UserManager being disposed as the stack trace says, as well as the userManager being null
This thread could run upwards of an hour (and the IIS server has been configured appropriately for this)
So ultimately what I think this comes down to, is there any way that I can prevent these fields from being disposed, or is there something else that's wrong with my logic here that I have become oblivious to after spending the last day tinkering with it?
Thanks in advance
I wish to test/deal with specific error conditions returned from UserManager, eg: registration failed due to username already on file, etc
var user = new SiteUser() { UserName = username, Email = RegisterViewModel.Email };
var result = await _userManager.CreateAsync(user, RegisterViewModel.Password);
if (!result.Succeeded)
{
// here I want to test for specific error conditions
// eg: username already on file, etc
// how can I do this?
}
IdentityResult contains an Errors property, which is of type IEnumerable<IdentityError>. IdentityError itself contains both a Code property and a Description property. This means that your result variable in your OP has an Errors property that describes the specific errors that occurred.
IdentityErrorDescriber is used for generating instances of IdentityError. Here's an example from the source:
public virtual IdentityError DuplicateUserName(string userName)
{
return new IdentityError
{
Code = nameof(DuplicateUserName),
Description = Resources.FormatDuplicateUserName(userName)
};
}
IdentityErrorDescriber is injected into the DI system in the same way that UserManager is. This means you can take it as a dependency in your controller's construtor (for example) and use it later, like so (assuming _errorDescriber has been created and set in your constructor):
if (!result.Succeeded)
{
// DuplicateUserName(...) requires the UserName itself so it can add it in the
// Description. We don't care about that so just provide null.
var duplicateUserNameCode = _errorDescriber.DuplicateUserName(null).Code;
// Here's another option that's less flexible but removes the need for _errorDescriber.
// var duplicateUserNameCode = nameof(IdentityErrorDescriber.DuplicateUserName);
if (result.Errors.Any(x => x.Code == duplicateUserNameCode))
{
// Your code for handling DuplicateUserName.
}
}
There are a number of different ways to get the Code value you want to test against and to do the check itself - this is just one example that's fairly safe against customisations you might want to make to the codes and errors themselves.
If you're interested, here's a link to the source for where the DuplicateUserName error is added to the IdentityResult you get back.
I've only talked about DuplicateUserName here, but there are other IdentityErrorDescriber values for e.g. InvalidEmail that you might also want to check for.
Users of my site have experienced some strange behaviour yesterday (first time I've seen this issue), and unfortunately I don't have much in the way of error logs to try to figure out what's going on. The site had a higher-than-normal number of people online at once, albeit not a large number in the grand scheme of things (maybe 50 to 100 users all trying to perform similar functions). I can't recreate the issue in my development environment, haven't seen it before, and don't really know why it is happening.
The crux of the problem is that users can register or log on successfully, but a small number of them could see other users' data.
The site is ASP.NET MVC 3.
Users are logging on and I set an authentication cookie - here's the LogOn action:
[HttpPost]
public ActionResult LogOn(AccountLogOnViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (!Membership.ValidateUser(model.UserName, model.Password))
{
ModelState.AddModelError("login-message", "Incorrect username or password");
}
}
if (ModelState.IsValid)
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
Session.Remove("MenuItems");
return Redirect(returnUrl ?? Url.Action("Index", "Home"));
}
else
{
model.ReturnUrl = returnUrl;
return View(model);
}
}
AccountLogOnViewModel is a simple object with two string properties, UserName and Password.
From what I can gather, this is fine - if you log in as NickW then doing something like User.Identity.Name correctly gives you "NickW" (when users were seeing other users' data, they reported that that "Welcome, NickW" text on screen was showing them the correct value - this is written out using User.Identity.Name)
The site also uses a custom membership provider. It overrides the ValidateLogin method, and the GetUser method. ValidateLogin appears to be working just fine so I'm not concerned about it.
The overridden GetUser method is as follows:
public override MembershipUser GetUser(string username, bool userIsOnline)
{
User user = _userRepository.Users.FirstOrDefault(u => u.UserName == username);
MembershipUser membershipUser = null;
if (user == null)
return membershipUser;
membershipUser = new MembershipUser(this.Name,
user.UserName,
user.Id,
user.Email,
null,
user.Comments,
user.IsActivated,
user.IsLockedOut,
user.CreatedDate,
user.LastLoginDate,
user.LastLoginDate,
user.LastModifiedDate,
Convert.ToDateTime(user.LastLockedOutDate));
return membershipUser;
}
So I'm attempting to retrieve a User object from my database, and using that to create a new MembershipUser object. My database User table has additional columns on top of those required by the membership provider - e.g. name, address, phone number etc.
At various points in the rest of the website (for example if you go to the Profile page), I retrieve a user object from the database and use it to populate the screen. The line I use to retrieve the User object is:
User user = userRepository.Users.FirstOrDefault(u => u.UserName == Membership.GetUser().UserName);
Here is a cut down version of the userRepository (i.e. just removing unrelated code).
public class SqlUserRepository : IUserRepository
{
private Table<User> usersTable;
private string _connectionString;
public SqlUserRepository(string connectionString)
{
_connectionString = connectionString;
usersTable = (new DataContext(connectionString)).GetTable<User>();
}
public IQueryable<User> Users
{
get { return usersTable; }
}
public void CreateUser(AccountRegisterViewModel user)
{
User newUser = new User();
newUser.UserName = user.UserName;
newUser.Salutation = user.Salutation;
newUser.PhoneNumber = user.PhoneNumber;
newUser.SecondaryPhoneNumber = user.SecondaryPhoneNumber;
newUser.FirstName = user.FirstName;
newUser.LastName = user.LastName;
newUser.PasswordSalt = CreateSalt();
newUser.Password = CreatePasswordHash(user.Password, newUser.PasswordSalt);
newUser.Email = user.Email;
newUser.CreatedDate = DateTime.UtcNow;
newUser.Comments = "Created from web registration";
newUser.LastModifiedDate = DateTime.UtcNow;
newUser.LastLoginDate = DateTime.UtcNow;
newUser.IsActivated = true;
newUser.IsLockedOut = false;
newUser.MayContact = user.MayContact;
usersTable.InsertOnSubmit(newUser);
usersTable.Context.SubmitChanges();
}
}
So it appears to me as if the auth cookie I'm setting is fine, but either:
When I first go in to the membership provider's GetUser() method, it retrieves the wrong record from the database and therefore sets up a MembershipUser object with the wrong username; subsequently when I look in the database for "this" user I'm actually looking for the wrong username.
Or: Intermittently when I do userRepository.FirstOrDefault(x => x.UserName == Membership.GetUser().Name) it retrieves the wrong record.
Or: something else is going wrong that I haven't thought of.
As I say, this seems to be a problem when the site was under load, so I'm wondering if it's some sort of caching issue somewhere? But I really don't know.
One thought I had was to change the way I retrieve the user in case the problem lies with the membership provider, and use this instead:
userRepository.FirstOrDefault(x => x.UserName == User.Identity.Name)
// or HttpContext.Current.User.Identity.Name if not within a controller
But really I'm not even sure what's going on so have no idea whether this will resolve the issue. Could it be a caching problem somewhere? It appears (but I can't be 100% certain) that when user A could see user B's details, it was always the case that user B was also active in the system (or had been within the previous 20 minutes).
I know it's a long shot, but does anyone have any idea how this could happen? Obviously it's a major concern and needs to be fixed urgently, but without knowing why it's happening I can't fix it!
Thanks in advance for any help,
Nick
Some things to consider:
Instead of using FirstOrDefault, use SingleOrDefault. FirstOrDefault assumes there will be more than 1 record of data matching your query. Since you are querying by username, there should only be 1 matching row, correct? In that case, use SingleOrDefault instead. When there are multiple rows that match the query, SingleOrDefault will throw an exception.
To get the username, instead of invoking Membership.GetUser().UserName, use User.Identity.Name. The User property on an MVC controller references an IPrincipal that should match the user's forms authentication cookie value. Since you have a custom membership provider, this should help eliminate its methods as a source of the problem.
There could be a caching issue if you have caching set up for the MVC project. Do you use the OutputCacheAttribute ([OutputCache]) on any controllers or action methods? Do you have it set up as a global filter in the global.asax file? Or do you think there may be some kind of SQL-based caching going on?
Looking at your overridden GetUser method, I see it should take 2 parameters: string username and bool isOnline. However, when you invoke it with Membership.GetUser().UserName, you are passing no parameters. Do you have another overridden overload of this method that also takes no parameters? What does it look like? Does it use System.Threading.CurrentPrincipal.Identity.Name to sniff out the current username when none is passed?
My code seems straightforward:
bool rv = false;
var results = from user in Users
where user.userName.Equals(newUser.userName)
select user;
if (results.Count() == 0)
{
this.context.Users.Add(newUser);
this.context.SaveChanges();
rv = true;
}
return rv;
But this causes a DbEntityValidationException with an inner exception value that says:
OriginalValues cannot be used for entities in the Added state.
...What does that even mean? The only thing I can think of is that newUser shows a value of 0 for userID even though this has a private setter and userID, as a primary key, is supposed to be database-generated. But if that were the problem, it would not be possible to simply use Add() to insert an object into any EF database ever. So I'm confused.
Thanks in advance to anyone who can help shed some light on this.
ETA: newUser is created by the following code in my Controller:
[HttpPost]
public ActionResult CreateRegistration(FormVMRegistration newRegistration)
{
//draw info from form and add a new user to repository
User newUser = new User();
newUser.userName = newRegistration.userName;
newUser.screenName = newRegistration.screenName;
newUser.password = newRegistration.usersPW;
bool addSuccess = userRep.Add(newUser);
...
}
FormVMRegistration is just a ViewModel with nothing but string properties, to convey data from the Regiostration form to the Controller.
userRep is my User Repository.
I think it comes from the fact that you're using the newUser object before it is saved. Try to change this line
bool addSuccess = userRep.Add(newUser);
to
bool addSuccess = userRep.Add(newUser, newRegistration.userName);
and then (given passedUserName is a passed name from above) change your linq query to:
var results = from user in Users
where user.userName.Equals(passedUserName)
select user;
Good luck!
As near as can be determined, the problem seems to have stemmed from "deep" references to objects, or references to references. IOW, class Foo made a reference to class Bar which made a reference to class Baz which made a reference to class Foo... and apparently EF doesn't like this so much, because it's not easy to validate.
I have an exception which is fired when I test some code of mine using the library Linq to AD.
I am using a repository with this function :
public UserAD GetUser(string username)
{
UserAD user = null;
using (Root = CreateDirectoryEntry())
{
var users = new DirectorySource<UserAD>(Root, SearchScope.Subtree);
user = users.Where(x => x.AccountName == username)
.AsEnumerable()
.SingleOrDefault(); //not supported in LDAP; alternative in-memory SingleOrDefault
}
return user;
}
This works fine when I call it directly:
[TestMethod]
public void RepositoryUtenteAD_GetUser()
{
UserAD user = repositoryAD.GetUser("TestAD_OK");
Assert.AreEqual("blablabla", user.DistinguishedName);
Assert.IsFalse(user.AccountDisabled);
}
But I may use another method which will call GetUser:
[TestMethod]
public void RepositoryUtenteAD_AutenticazioneUtente_Authentication()
{
IAutenticazione auth = repositoryAD.AutenticazioneUtente("TestAD_OK", "TestAD_OK");
Assert.IsTrue(auth.IsAuthenticated);
}
and the Authentication method is as follows (cleared of meaning and details, the error remaining there) :
public IAutenticazione AutenticazioneUtente(string username, string password)
{
bool IsWhyNotAuthentifiedFound = false;
IAutenticazione authenticazione = new Autenticazione();
UserAD user = GetUser(username);
return authenticazione;
}
The test is running fine, the assert is giving me the good value, but after the Cleanup of my test I have a disconnectedcontext exception fired. I assume it comes from the Interop.Adsi dll I use.
In GetUser(username) Should I copy everything I have in my UserAD to make a clone and so be clear off context...? Or is there a more clever way to approach?
Thanks for your reading!
Found the solution even if it is a strange one. The bug only happens when running the test in debug mode, if i choose run instead of debug, everything is fine.
That close the chapter. Thanks for your reading.