I'm still very new to C# and would appreciate any help with my code.
I'm creating a user profile page and am getting the error "Nullable object must have a value" on "photo = (byte)user.Photo;" in the following code. I assume it's because I declared "photo = 0;" How do I add a value to it?
Update:
Here's the entire method
public static bool UserProfile(string username, out string userID, out string email, out byte photo)
{
using (MyDBContainer db = new MyDBContainer())
{
userID = "";
photo = 0;
email = "";
User user = (from u in db.Users
where u.UserID.Equals(username)
select u).FirstOrDefault();
if (user != null)
{
photo = (byte)user.Photo;
email = user.Email;
userID = user.UserID;
return true; // success!
}
else
{
return false;
}
}
}
I am assuming that you are getting error for this one...
if (user != null)
{
photo = (byte)user.Photo;
email = user.Email;
userID = user.UserID;
return true; // success!
}
else
{
return false;
}
If yes then just replace it with...
if (user != null)
{
photo = user.Photo== null ? null : (byte)user.Photo;
email = user.Email;
userID = user.UserID;
return true; // success!
}
else
{
return false;
}
Related
I have a form on a mvc view that contains Name, Username, email address, drop down list for branch and department. There are 2 stored procedures that is checking for duplicate Username and Email address. An error will be shown if the user clicks the update button.
Scenario 1
If the user deletes the username and enters a unique one the form will not submit because email address is still existent in the database.
How can I allow the update to continue if the user puts back the original Username
[HttpPost]
public async Task<IActionResult> Update(ApplicationUserModel model)
{
var appmodel = new ApplicationUserModel();
appmodel.UserDetails = await ClaimsService.GetApplicationUserByID(model.Users.UserID);
appmodel.Users = await ClaimsService.GetUserNameByID(model.Users.UserName);
appmodel.User = await ClaimsService.GetEmailAddressIfExists(model.Users.EmailAddress);
if (!appmodel.UserDetails.Name.Equals(model.Users.Name) || appmodel.UserDetails.BranchID != model.Users.BranchID || appmodel.UserDetails.DepartmentID != model.Users.DepartmentID )
{
if(!appmodel.UserDetails.UserName.Equals(model.Users.UserName) || !appmodel.UserDetails.EmailAddress.Equals(model.Users.EmailAddress))
{
model.UserNameErrorMessage = "Username already exists";
model.EmailAddressErrorMessage = "Email address already exists";
model.Username = appmodel.Users.UserName;
model.EmailAddress = appmodel.User.EmailAddress;
model.Users = new Model.Applications.Tables.tblUsers() { Archive_User = "0", StatusID = 1 };
model.BranchSelectList = new SelectList(await BranchServices.GetBranchByCompanyID(1), "BranchID", "BranchFullName");
model.DepartmentSelectList = new SelectList(await DepartmentService.GetAllActiveDepartments(1), "DepartmentID", "Department");
return View(model);
}
}
await ClaimsService.UpdateUserAsync(model.Users);
string redirectUrl = string.Format("/ApplicationUsers/Users");
return RedirectToAction("Message", "Home", new { type = Service.Utils.StringHelper.Types.UpdateSuccess, url = redirectUrl });
}
Compare the old value (of username alone then email) with the new value if they are the same then don't check if it exists before. if they are different then check if the new is unique
If email was 'user1#example.com' , username = 'user1'
and the he entered email = 'user1#eample.com' but username = 'user1updated'
You first check old email and new email (both are 'user1#example') then it is ok.
then check the old username with the new username (here are different) so I check the existence of new username ('user1updated') in the database if it not there I update otherwise don't update
[HttpPost]
public async Task<IActionResult> Update(ApplicationUserModel model)
{
string redirectUrl;
var appmodel = new ApplicationUserModel();
appmodel.UserDetails = await ClaimsService.GetApplicationUserByID(model.Users.UserID);
appmodel.Users = await ClaimsService.GetUserNameByID(model.Users.UserName);
appmodel.User = await ClaimsService.GetEmailAddressIfExists(model.Users.EmailAddress);
if(appmodel.UserDetails.EmailAddress == model.Users.EmailAddress || appmodel.UserDetails.UserName == model.Users.UserName)
{
if (!appmodel.UserDetails.Name.Equals(model.Users.Name) || appmodel.UserDetails.BranchID != model.Users.BranchID || appmodel.UserDetails.DepartmentID != model.Users.DepartmentID)
{
await ClaimsService.UpdateUserAsync(model.Users);
redirectUrl = string.Format("/ApplicationUsers/Users");
return RedirectToAction("Message", "Home", new { type = Service.Utils.StringHelper.Types.UpdateSuccess, url = redirectUrl });
}
else if (appmodel.UserDetails.EmailAddress != model.Users.EmailAddress || appmodel.UserDetails.UserName != model.Users.UserName)
{
if (!appmodel.UserDetails.Name.Equals(model.Users.Name) || appmodel.UserDetails.BranchID != model.Users.BranchID || appmodel.UserDetails.DepartmentID != model.Users.DepartmentID)
{
await ClaimsService.UpdateUserAsync(model.Users);
redirectUrl = string.Format("/ApplicationUsers/Users");
return RedirectToAction("Message", "Home", new { type = Service.Utils.StringHelper.Types.UpdateSuccess, url = redirectUrl });
}
if(appmodel.UserDetails.EmailAddress == model.Users.EmailAddress)
{
model.EmailAddressErrorMessage = "";
}
if(appmodel.UserDetails.EmailAddress != model.Users.EmailAddress)
{
model.EmailAddressErrorMessage = "Email address already exists";
}
if(appmodel.UserDetails.UserName == model.Users.UserName)
{
model.UserNameErrorMessage = "";
}
if (appmodel.UserDetails.UserName != model.Users.UserName)
{
model.UserNameErrorMessage = "Username already exists";
}
model.Username = appmodel.Users.UserName;
model.EmailAddress = appmodel.User.EmailAddress;
model.Users = new Model.Applications.Tables.tblUsers() { Archive_User = "0", StatusID = 1 };
model.BranchSelectList = new SelectList(await BranchServices.GetBranchByCompanyID(1), "BranchID", "BranchFullName");
model.DepartmentSelectList = new SelectList(await DepartmentService.GetAllActiveDepartments(1), "DepartmentID", "Department");
return View(model);
}
await ClaimsService.UpdateUserAsync(model.Users);
redirectUrl = string.Format("/ApplicationUsers/Users");
return RedirectToAction("Message", "Home", new { type = Service.Utils.StringHelper.Types.UpdateSuccess, url = redirectUrl });
}
if (appmodel.UserDetails.EmailAddress == model.Users.EmailAddress)
{
model.EmailAddressErrorMessage = "";
}
if (appmodel.UserDetails.EmailAddress != model.Users.EmailAddress)
{
model.EmailAddressErrorMessage = "Email address already exists";
}
if (appmodel.UserDetails.UserName == model.Users.UserName)
{
model.UserNameErrorMessage = "";
}
if (appmodel.UserDetails.UserName != model.Users.UserName)
{
model.UserNameErrorMessage = "Username already exists";
}
model.Username = appmodel.Users.UserName;
model.EmailAddress = appmodel.User.EmailAddress;
model.Users = new Model.Applications.Tables.tblUsers() { Archive_User = "0", StatusID = 1 };
model.BranchSelectList = new SelectList(await BranchServices.GetBranchByCompanyID(1), "BranchID", "BranchFullName");
model.DepartmentSelectList = new SelectList(await DepartmentService.GetAllActiveDepartments(1), "DepartmentID", "Department");
return View(model);
}
I am using this method for Reset Password
public ResetTokenResult DoPasswordResetTokenForChange(string userId, string token)
{
switch (UserManager.FindById(userId))
{
case null:
return ResetTokenResult.UnknownUserId;
case CatalystUser user when ! (user.PasswordInvalidatedByReset ?? false):
return ResetTokenResult.TokenIsExpired;
case CatalystUser user when ! ((user.PasswordResetTokenExpiration ?? DateTime.MinValue) > DateTime.UtcNow):
return ResetTokenResult.TokenIsExpired;
case CatalystUser user when UserManager.VerifyUserToken(user.Id, "ResetPassword", token):
user.PasswordResetTokenExpiration = DateTime.UtcNow.AddDays(-1); // 1-time use. Invalidate now.UserManager.Update(user);
return ResetTokenResult.Success;
default:
return ResetTokenResult.InvalidToken;
}
}
Controller which I am using this method
[RequireHttpsWhenConfigured]
public async Task<ActionResult> Index(PasswordChangePage currentPage,
string userId, string token, string returnUrl = "")
{
var model = new PasswordChangePageViewModel(currentPage);
var isResetPasswordRequest = !string.IsNullOrEmpty(userId) && !string.IsNullOrEmpty(token);
if (!isResetPasswordRequest)
{
if (!RequestContext.IsCurrentUserAuthorized())
return Redirect(NavigationService.GetLoginLink());
model.PasswordChangeModel = new PasswordChangeViewModel {ReturnUrl = returnUrl};
model.ReturnUrl = returnUrl;
return View("Index", model);
}
if (RequestContext.IsCurrentUserAuthorized())
{
SignInManager.AuthenticationManager.SignOut();
return Redirect(Request.Url?.AbsoluteUri ?? "~/");
}
var loginLink = NavigationService.GetLoginLink();
var result = UserAccountService.DoPasswordResetTokenForChange(userId,Base64ForUrlDecode(token));
if ((result & ResetTokenResult.Failure) != ResetTokenResult.None)
{
model.ChangeCanProceed = false;
model.ErrorMessage = GetMessageForTokenResult(result);
model.LoginLink = loginLink;
}
else
{
model.PasswordChangeModel = new PasswordChangeViewModel { CurrentPassword = "null", IsResetPassword = true, UserId = userId, ResetPasswordToken = token };
model.ReturnUrl = loginLink;
}
return View("Index", model);
}
When users want to reset their password, they receive an email with a token link and everything works fine. As I know default ASPNET Identity token burns after 1 clicking to link.
My question is what is the best way to implement logic, the token link will burn after 5 clickings to link which is sent to email.
I developed function to delete users (soft deletion), ie stays in the database and does not show in the list. When I enter the login and password of the deleted account, it have to show a message as this login is deleted.
how to achieve this and thanks
user.controller.cs:
[Route("api/Users/Login")]
[ResponseType(typeof(object))]
public async Task<object> accountLogin(string name, string password)
{
string pwd = Encrypt(password);
Users account = new Users();
var query = from c in db.Users
where c.userlogin == name && c.password == pwd
select c;
account = query.FirstOrDefault();
string message = "";
if (account == null)
{
message += "404";
}
else if (account .IsActive == 1)
{
message += "400";
}
else if ((account .IsActive != 0) && (account .IsActive != 1)) // this condition of account deleted -- or account .IsActive == 2
{
message += "500";
}
else
{
message = JsonConvert.SerializeObject(account);
}
return new { a = message };
}
and this login.controller.js:
$scope.login = function () {
$scope.loading = true;
authService.login($scope.userlogin)
.then(function done(response) {
console.log(response);
if (response.a == '400') {
$scope.error = 'account disabled';
$scope.loading = false;
}
else if (response.a == '500') {
$scope.error = 'this login is deleted';
$scope.loading = false;
}
else if (response != '404') {
console.log("bbb");
$location.url('Dashboard');
}
else {
console.log("login or password incorrect");
$scope.error = 'login or password incorrect';
$scope.loading = false;
}
},
function fail(err) {
$scope.error = 'Problem in connextion ! contact administrateur';
console.log(err);
}
);
}
In my C# application I use 2 connection strings (application_cs, users_cs). To change these connection strings I use:
private static void SetProviderConnectionString(string connectionString)
{
var connectionStringFieldM =
Membership.Provider.GetType().GetField("_sqlConnectionString",
BindingFlags.Instance | BindingFlags.NonPublic);
var connectionStringFieldR = Roles.Provider.GetType().GetField("_sqlConnectionString",
BindingFlags.Instance | BindingFlags.NonPublic);
var connectionStringFieldP = ProfileManager.Provider.GetType().GetField("_sqlConnectionString",
BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringFieldM.SetValue(Membership.Provider, connectionString);
connectionStringFieldR.SetValue(Roles.Provider, connectionString);
connectionStringFieldP.SetValue(ProfileManager.Provider, connectionString);
}
public static void SetProviderUsers()
{
SetProviderConnectionString(ConfigurationManager.ConnectionStrings["users_cs"].ConnectionString);
}
public static void SetProviderApp()
{
SetProviderConnectionString(ConfigurationManager.ConnectionStrings["application_cs"].ConnectionString);
}
So in my code whenever I want to add a user I do this:
public int CreateUser(int stid, int cid, int usrId, string email, string tel, string mob, string username,
bool create, bool prime)
{
int result = 0;
Guid userid = new Guid();
DALUsers.UserDBDataContext dc = new DALUsers.UserDBDataContext();
DAL.AppDataContext d = new DAL.AppDataContext();
BLL.Security.SetProviderUsers();
if (create) //create the user first
{
string question = "1";
string answer = "1";
bool isAproved = true;
string password = System.Web.Security.Membership.GeneratePassword(8, 2);
MembershipCreateStatus cs = new MembershipCreateStatus();
MembershipUser newUser = Membership.CreateUser(username, password, email, question, answer, isAproved, out cs);
Membership.UpdateUser(newUser);
Roles.AddUserToRole(username, "User_x");
if (cs == MembershipCreateStatus.Success)
{
result = 1;
}
else
X.MessageBox.Info("Error", "Cannot create user due to :" + cs.ToString(), UI.Danger).Show();
}
//at this point we have the user created either way.
// return userid;
var id = (from i in dc.aspnet_Users where i.UserName.CompareTo(username) == 0 select i.UserId);
if (id.Count() == 1)
{
userid = id.First();
bool contin = true;
var fulname = (from i in dc.Clients where i.id == usrId select i).First();
if (String.IsNullOrEmpty(fulname.Mobile)) fulname.Mobile = mob;
fulname.Email = email;
fulname.ModifiedBy = HttpContext.Current.User.Identity.Name;
fulname.ModifiedDate = DateTime.Now;
dc.SubmitChanges();
DateTime dt = DateTime.Now;
DALUsers.CIUser usr = new DALUsers.CIUser();
var existing = (from i in dc.CIUsers where i.UserName.CompareTo(username) == 0 && i.cid == cid select i);
if (existing.Count() > 0)
{
X.MessageBox.Info("Warning", "UserName already exists . Please try another!", UI.Warning).Show();
contin = false;
}
else
{
dc.CIUsers.InsertOnSubmit(usr);
dc.SubmitChanges();
}
if (contin)
{
DALUsers.CIUser usrNew = new DALUsers.CIUser();
var approved = (from k in dc.aspnet_Memberships //if user is not approved
where k.UserId == userid
select k).FirstOrDefault();
if (approved.IsApproved == false)
{
approved.IsApproved = true;
}
ProfileBase profile = ProfileBase.Create(username);
profile.SetPropertyValue("Mobile", mob);
profile.SetPropertyValue("Email", email);
profile.Save();
usrNew.UserId = usrId;
usrNew.cid = cid;
usrNew.FullName = fulname.LastName + " " + fulname.FirstName;
usrNew.Role = "User_x";
usrNew.SignRights = prime;
usrNew.IsPrime = prime;
usrNew.stid = stid;
usrNew.UserName = username;
usrNew.UserId = userid;
usrNew.CreatedDate = DateTime.Now;
usrNew.CreatedBy = HttpContext.Current.User.Identity.Name;
dc.CIUsers.InsertOnSubmit(usrNew);
dc.SubmitChanges();
result = 1;
X.MessageBox.Info("Success", "The user has been successfully added", UI.Success).Show();
}
}
else
X.MessageBox.Info("Error", "Could not find the user", UI.Danger).Show();
BLL.Security.SetProviderApp();
return result;
}
EDIT
I just saw that in my code there is this line:
DALUsers.aspnet_User user = new DALUsers.aspnet_User();
But the variable user is not used anywhere else in the code. Probably it has been left there... And its the only variable named user in my code. Is that causing the issue? But then why only on the production server?
EDIT
The weird part is that when I run my application from visual studio locally it works as a charm. But when I am adding a user in the application running on the production server when I am trying to add the second user it fails and I receive this error:
Value cannot be null. Parameter name: user
And if I try to login to my application after that it fails. I have to restart my website from iis to be able to login again.
Any ideas?
Well I cant find the error in your code but if you say that this error occurs only in server and that you are sure that your files are synched between server and your local machine, then probably the error lies in your web.config. Take a look
I'm building a simple application where a user can edit their profile including adding/deleting a brand image. This seems to be working fine and is updating the database no problem, however when refreshing the page and retrieving the user details via Membership.GetUser() the result includes the old results and not those from the updated database.
Here is my MembershipUser GetUser override:
public override MembershipUser GetUser(string query, bool userIsOnline)
{
if (string.IsNullOrEmpty(query))
return null;
var db = (AccountUser)null;
// ...get data from db
if (query.Contains("#")){
db = _repository.GetByQuery(x => x.Email == query).FirstOrDefault();
}
else
{
string firstName = query;
string lastName = null;
if (query.Contains(" "))
{
string[] names = query.Split(null);
firstName = names[0];
lastName = names[1];
}
// ...get data from db
db = _repository.GetByQuery(x => x.FirstName == firstName && x.LastName == lastName).FirstOrDefault();
}
if (db == null)
return null;
ToMembershipUser user = new ToMembershipUser(
"AccountUserMembershipProvider",
db.FirstName + " " + db.LastName,
db.ID,
db.Email,
"",
"",
true,
false,
TimeStamp.ConvertToDateTime(db.CreatedAt),
DateTime.MinValue,
DateTime.MinValue,
DateTime.MinValue,
DateTime.MinValue);
// Fill additional properties
user.ID = db.ID;
user.Email = db.Email;
user.FirstName = db.FirstName;
user.LastName = db.LastName;
user.Password = db.Password;
user.MediaID = db.MediaID;
user.Media = db.Media;
user.Identity = db.Identity;
user.CreatedAt = db.CreatedAt;
user.UpdatedAt = db.UpdatedAt;
return user;
}
Note I am using a custom MembershipProvider and MembershipUser. Here is where I am calling that method:
public ActionResult Edit()
{
ToMembershipUser toUser = Membership.GetUser(User.Identity.Name, true) as ToMembershipUser;
Now when I do a separate query just under this line of code straight to the database, not invoking MembershipUser, I get the updated result which in turn updates the MembershipUser result?!
It seems it may be caching the results? Anyway around this? I hope this is clear enough. Thanks
Edit:
It appears that when I set a breakpoint just after :
// ...get data from db
db = _repository.GetByQuery(x => x.FirstName == firstName && x.LastName == lastName).FirstOrDefault();
'db' retrieves the outdated results though surely this is talking to the database? If need be I'll update with my repository pattern
I managed to find a workaround though I'm not happy with this solution, so if anyone can improve upon this please post.
I decided to manually update the MembershipUser instance manually each time I update the image. My controller now looks like this:
private static ToMembershipUser MembershipUser { get; set; }
// GET: Dashboard/AccountUsers/Edit
public ActionResult Edit()
{
if(MembershipUser == null)
MembershipUser = Membership.GetUser(User.Identity.Name, true) as ToMembershipUser;
}
[HttpPost]
[ValidateJsonAntiForgeryToken]
public JsonResult UploadMedia(IEnumerable<HttpPostedFileBase> files, int id)
{
var images = new MediaController().Upload(files);
if (images == null)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json("File failed to upload.");
}
AccountUser accountUser = db.AccountUsers.Find(id);
db.Entry(accountUser).State = EntityState.Modified;
accountUser.UpdatedAt = TimeStamp.Now();
accountUser.MediaID = images[0];
db.SaveChanges();
MembershipUser.Media = accountUser.Media;
MembershipUser.MediaID = accountUser.MediaID;
return Json(new { result = images[0] });
}
[HttpPost]
[ValidateJsonAntiForgeryToken]
public JsonResult DeleteMedia(int id)
{
bool delete = new MediaController().Delete(id, 1);
if (!delete)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json("Error. Could not delete file.");
}
MembershipUser.Media = null;
MembershipUser.MediaID = null;
return Json("Success");
}