EF SaveChanges not saving and not throwing exception - c#

I have a method that creates a new user and then insert a row in the User Permission table, but nothing is happening. Here is my code:
// before: creates user
var permission = new UserPermission()
{
UserId = user.Id,
UserName = user.UserName,
Assets = createUserModel.Assets
};
AccountDb.UserPermissions.Add(permission);
var saveChangesResult = AccountDb.SaveChanges();
if (saveChangesResult == 0) // the result is always 0
{
AppUserManager.Delete(user);
return BadRequest("User permission could not be saved");
}
// then: add user to role and return ok
SaveChanges always returns 0 and doesn't update the database, I've already googled for it and tried the following actions:
// no success
AccountDb.UserPermissions.Attach(permission);
// no success either
AccountDb.Entry(permission).State = EntityState.Added;
And I tried in async method too, but no luck.
Here is my "UserPermission" model:
public class UserPermission
{
public int Id { get; set; }
public string UserId { get; set; }
public string UserName { get; set; }
public string _Assets { get; set; }
[NotMapped]
public List<int> Assets
{
get
{
if (_Assets == null) return null;
return Array.ConvertAll(_Assets.Split(';'), Int32.Parse).ToList();
}
set
{
_Assets = String.Join(";", value.Select(x => x.ToString()).ToArray());
}
}
}
It's curious because this method was working lately, but after some changes in "AccountContext" and some rolebacks, I notice that the method wasn't working anymore.
-- edit --
here is the full create method
[HttpPost]
[Route("create")]
public IHttpActionResult CreateUser(CreateUserBindingModel createUserModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (createUserModel.RoleName is null)
{
return BadRequest("There is no role assigned to user");
}
var user = new UserModel()
{
UserName = createUserModel.UserName,
Email = createUserModel.Email,
FirstName = createUserModel.FirstName,
LastName = createUserModel.LastName
};
var addUserResult = AppUserManager.Create(user, createUserModel.Password);
if (!addUserResult.Succeeded)
{
return GetErrorResult(addUserResult);
}
var permission = new UserPermission()
{
UserId = user.Id,
UserName = user.UserName,
Assets = createUserModel.Assets
};
AccountDb.UserPermissions.Add(permission);
var saveChangesResult = AccountDb.SaveChanges();
if (saveChangesResult == 0)
{
AppUserManager.Delete(user);
return BadRequest("User permission could not be saved");
}
var addRoleResult = AppUserManager.AddToRole(user.Id, createUserModel.RoleName);
if (!addRoleResult.Succeeded)
{
AppUserManager.Delete(user);
return GetErrorResult(addUserResult);
}
return Ok(TheModelFactory.Create(user));
}
-- edit 2 --
I was using an Azure db string and I changed to a local db string, but the problem still is the same, UserPermissions Table does not update and no error is emiting.

You are creating a new user, but you never use the result. Your user.Id is always null (or the respective default).
I don't know why your database fails silently (constraints or foreign keys should throw an exception), but you should fix that first. It will probably result in real records being written.

I found a solution!
For some reason, my local instance of AccountContext was the problem, it gets all UserPermissions but doesn't update. So I created a new instance and tried to add the permission:
using (var db = new AccountContext())
{
db.UserPermissions.Add(permission);
var saveChangesResult = db.SaveChanges();
if (saveChangesResult == 0)
{
AppUserManager.Delete(user);
return BadRequest("User permission could not be saved");
}
}
now it's working well. But I don't know why the local AccountDb is not working now if it was working in the past

Related

writing update function to check before adding data in C#

hi I want to write an update function to check the data in my database and if there was a similar column don't add the filed update that filed
here in the code below, I deserialize JSON file
public class CustomerDeserializer
{
public static List<DtoCustomer> Deserialize()
{
List<DtoCustomer> result =
JsonConvert.DeserializeObject<List<DtoCustomer>>(
File.ReadAllText(#"Information.json"));
return result;
}
}
in this part, I read the data and build it
public static Customer CustomerBuild(DtoCustomer dto)
{
return new Customer()
{
FirstName = dto.FirstName,
LastName = dto.LastName,
Address = dto.Address,
Email = dto.Email,
ComapnyName = dto.CompanyName,
PhoneNumber = dto.Phone
};
and then I process data and save them into database
////update function
public static void Process(Customer customer)
{
using (var context = new PracticeEntities1())
{
context.Customers.Add(customer);
context.SaveChanges();
}
}
I want to write an update function before saving to check the data and update it what should I do?
You may do something like this:
using (var context = new PracticeEntities1())
{
var existingCustomer = context.Customers.FirstOrDefault(c => c.Email == customer.Email);
if (existingCustomer != null) {
existingCustomer.FirstName = customer.FirstName;
existingCustomer.LastName = customer.LastName;
existingCustomer.Address = customer.Address;
existingCustomer.CompanyName = customer.CompanyName;
existingCustomer.Phone = customer.Phone;
}
else
{
context.Customers.Add(customer);
}
context.SaveChanges();
}

How to get the username of a logged in user and compare it to get the id

I want to be able to get the username of the logged in user in my C# ASP.net application so I can get the ID of that user. I then want to perform an edit so they can apply to be part of an appointment.
With my current code, I click the link and I just get a HTTP 400 error. I have debugged this and the ID is coming through as the correct value. Is there any reason that its not being attached to the url?
// GET:
public ActionResult VolunteerCeremony(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
string userName = string.Empty;
//if (System.Web.HttpContext.Current != null &&
// System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
//{
// System.Web.Security.MembershipUser usr = System.Web.Security.Membership.GetUser();
// if (usr != null)
// {
// userName = usr.UserName;
// }
//}
var getVolunteerId = (from u in db.Volunteers
where WebSecurity.CurrentUserName == u.Username
select u.VolunteerId).FirstOrDefault();
Volunteer volunteer = db.Volunteers
.Include(p => p.Appointments)
.Where(i => getVolunteerId == id)
.FirstOrDefault();
if (volunteer == null)
{
return HttpNotFound();
}
PopulateAssignedCeremonyData(volunteer);
return View(volunteer);
}
// POST: /Player/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult VolunteerCeremony(int? id, string[] selectedOptions)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var getVolunteerId = (from u in db.Volunteers
where WebSecurity.CurrentUserName == u.Username
select u.VolunteerId).FirstOrDefault();
var updateVolunteerWithCeremony = db.Volunteers
.Include(p => p.Appointments)
.Where(i => getVolunteerId == id)
.Single();
try
{
UpdateVolunteerCeremonies(selectedOptions, updateVolunteerWithCeremony);
db.Entry(updateVolunteerWithCeremony).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
PopulateAssignedCeremonyData(updateVolunteerWithCeremony);
return View(updateVolunteerWithCeremony);
}
EDIT This is where I'm calling the method
else if(Request.IsAuthenticated && HttpContext.Current.User.IsInRole("Volunteer"))
{
<li>#Html.ActionLink("Appointments", "Create", "Appointments")</li>
<li>#Html.ActionLink("Join Ceremony", "VolunteerCeremony", "Volunteers")</li>
}
EDIT 2 Here's my ViewModel that I used for the many to many relationship I have:
public class VolunteerCeremonyVM
{
public int AppointmentId { get; set; }
public string DetailsOfAppointment { get; set; }
public bool Assigned { get; set; }
public VolunteerCeremonyVM()
{
this.AppointmentId = AppointmentId;
this.DetailsOfAppointment = DetailsOfAppointment;
this.Assigned = Assigned;
}
}
Based on further clarification, you will want to update your ViewModel to include a VolunteerId. So it should look something like:
public class VolunteerCeremonyVM
{
public int AppointmentId { get; set; }
public string DetailsOfAppointment { get; set; }
public bool Assigned { get; set; }
public int VolunteerId { get; set; }
}
Notice that I removed the default constructor as it wasn't doing anything. When you construct the object you can use code like the following:
var model = new VolunteerCeremonyVM {
AppointmentId = 10,
VolunteerId = 50,
//etc
};
As this will set all of the properties after the basic object is constructed.
Then, you will want to pass this view model into the view that is generating your action links like so: return View(model); in whatever controller action you're using.
Inside the view, you will make use of this new information. Specifically, the Html.ActionLink method requires that any extra parameters are passed to it in a route dictionary. It uses this route dictionary to build the URL that is generated for the anchor tag. You can call Html.ActionLink with an anonymous object for its fourth parameter to specify the route dictionary. The id will be the name of the parameter and Model.VolunteerId will be the value of the current volunteer.
Change the Html.ActionLink call to the following to pass an id:
#Html.ActionLink("Join Ceremony",
"VolunteerCeremony",
"Volunteers",
new { id = Model.VolunteerId },
null)
This should fill in the id parameter of the ActionLink with the currently logged in user's userId which should generate the following link: /Volunteers/VolunteerCeremony/3 or whatever the ID actually is.
For the form side, you will also need a hidden field on the form so the correct id is sent along with the request. You will need to add a VolunteerId to the form ViewModel that you create when generating the form page. Once you've done that, inside the form itself, you will need to add the following:
Html.Hidden("id", Model.VolunteerId)
This creates an <input type="hidden" name="id" value="3" /> tag that will be sent with the form on post back to the server. Since the tag's name is id this will match with the id that's defined in your controller action.

Method to get user details from LDAP/AD

I'm using the code below to get various information about the logged in user and it works, but I want to turn it into a method where I can pass the attribute I want (DisplayName, EmailAddress, etc) and it returns that value for whoever is logged in. I couldn't figure out how to get this to work though.
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, User.Identity.Name))
{
if (user != null)
{
loggedInUserfullName = user.DisplayName;
loggedInUserEmail = user.EmailAddress;
}
}
}
While the above works, I can't use any of those variables outside of the scope of the method, which isn't helpful.
I tried the method below to just get the DisplayName but got errors due to FindByIdentity expecting a string (I don't have a string with the user's name - that's what I'm trying to find out!)
public string getUserFullName(object user)
{
user = UserPrincipal.FindByIdentity(ContextType.Domain, user);
string loggedInUserDisplayName = user.DisplayName;
return loggedInUserDisplayName ;
}
UserPrincipal loggedInUser = new UserPrincipal(ContextType.Domain);
// line above throws cannot convert from 'System.DirectoryServices.AccountManagement.ContextType' to 'System.DirectoryServices.AccountManagement.PrincipalContext'
getUserDetails(loggedInUser);
Is there a way to do this? Am I using the wrong library to achieve what I want?
This is a web application using windows authentication, if that makes any difference.
Thanks
I haven't used the DirectoryServices.AccountManagement stuff in a long time, but I'll give this a shot.
This line in your code is throwing an exception:
UserPrincipal loggedInUser = new UserPrincipal(ContextType.Domain);
The exception is telling you that the constructor for UserPrincipal expects a System.DirectoryServices.AccountManagement.PrincipalContext, but you're giving it a System.DirectoryServices.AccountManagement.ContextType. These lines in your working code are correct:
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, User.Identity.Name))
{
I'm not sure I fully understand what your intention is, but if you are looking for a reusable way to get info about the logged in user, try something like this:
public static class UserManager
{
public static string GetDisplayName(string name)
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain), name))
{
if (user != null)
{
return user.DisplayName;
}
throw new Exception("error");
}
}
}
You can call it by doing this:
var dn = UserManager.GetDisplayName(User.Identity.Name);
Obviously, you'll want to handle the error better. If I'm missing something, let me know and I'll try to update my answer.
Hope this helps!
Edit
To return an object containing multiple fields, you could do something like:
public static UserInfo GetDisplayName(string name)
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain), name))
{
if (user != null)
{
return new UserInfo
{
FullName = user.DisplayName,
Email = user.EmailAddress,
GivenName = user.GivenName,
SamAccountName = user.SamAccountName,
Surname = user.Surname
//any other things you may need somewhere else
};
}
throw new Exception("error");
}
}
Here is the UserInfo class:
public class UserInfo
{
public string FullName { get; set; }
public string Email { get; set; }
public string Surname { get; set; }
public string GivenName { get; set; }
public string SamAccountName { get; set; }
}

Can I check modelstate without modelbinding?

I'm getting my feet wet with the Entity Framework and am wondering if there is a way for me to check model state without model binding happening.
Say I create a user primarily from code, is there a way for me to check to make sure it is valid according to my predefined data annotations before I update?
public ActionResult Index()
{
User u = new User();
u.Username = "test";
u.Password = "test";
u.Email = "test";
DefaultContext db = new DefaultContext();
if (ModelState.IsValid)
{
db.Users.Add(u);
db.SaveChanges();
Response.Write(u.Id);
}
else
// model is not valid
return View();
}
The above code does not work because there is no binding happening. Or maybe I am confused of the process.
Thanks.
ModelState won't be available because it's set up by the model binder. The ModelBinder didn't bind a model so it didn't run validation thus ModelState will be empty.
But that doesn't stop you from using attributes and performing validation.
Assuming you are using the DataAnnotation attributes, you can run the validation without model binding.
Given this class:
public class Widget
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public decimal Price { get; set; }
}
You can do this:
var widget = new Widget
{
Id = 12,
Price = 15.57M
};
var context = new ValidationContext(widget, null, null);
var results = new List<ValidationResult>();
if( Validator.TryValidateObject( widget, context, results, true ) )
{
//Validation Successful
}
else
{
//Validation Failed
}
The Name property was not set and the TryValidateObject() will fail. The results collection will have the actual error that occurred.
you can use TryValidateModel or ValidateModel
User u = new User();
u.Username = "test";
u.Password = "test";
u.Email = "test";
if (TryValidateModel(u))
{
}else{
}

Need a refresher course on property access

I need help with accessing class properties within a given class.
For example, take the below class:
public partial class Account
{
private Profile _profile;
private Email _email;
private HostInfo _hostInfo;
public Profile Profile
{
get { return _profile; }
set { _profile = value; }
}
public Email Email
{
get { return _email; }
set { _email = value; }
}
public HostInfo HostInfo
{
get { return _hostInfo; }
set { _hostInfo = value; }
}
In the class "Account" exists a bunch of class properties such as Email or Profile.
Now, when I want to access those properties at run-time, I do something like this
(for Email):
_accountRepository = ObjectFactory.GetInstance<IAccountRepository>();
string username = Cryptography.Decrypt(_webContext.UserNameToVerify, "verify");
Account account = _accountRepository.GetAccountByUserName(username);
if(account != null)
{
account.Email.IsConfirmed = true;
But, I get "Object reference not set..." for account.Email... Why is that?
How do I access Account such that account.Email, account.Profile, and so on
returns the correct data for a given AccountId or UserName.
Here is a method that returns Account:
public Account GetAccountByUserName(string userName)
{
Account account = null;
using (MyDataContext dc = _conn.GetContext())
{
try
{
account = (from a in dc.Accounts
where a.UserName == userName
select a).FirstOrDefault();
}
catch
{
//oops
}
}
return account;
}
The above works but when I try:
account = (from a in dc.Accounts
join em in dc.Emails on a.AccountId equals em.AccountId
join p in dc.Profiles on em.AccountId equals p.AccountId
where a.UserName == userName
select a).FirstOrDefault();
I am still getting object reference exceptions for my Email and Profile
properties. Is this simply a SQL problem or is there something else I need to be
doing to be able to fully access all the properties within my Account class?
Thanks!
Your getting this because Email is another class which has not been assigned yet. What you can do is in your constructor default the properties that link to other classes as new items. For example in your ctor:
public Account()
{
// Set Defaults
Email = new Email();
Profile = new Profile();
HostInfo = new HostInfo();
}
Then you can set their values as desired.
This looks like a case of handling null values on your properties. You should initialize the Email property to something other than null if you expect to store or query against it, or alter the queries so that they can expect to deal with null values. Also if you get a null value from the database, and your property cannot be set to null, the reverse problem occurs.
Are you declaring these properties yourself, or are you trying to indicate something like auto-generated code from like Linq-to-SQL?
If this is auto-generated where the Account table references the Email table, etc., then you probably just need to specify that you want those objects to load as well in the load options:
using (MyDataContext dc = _conn.GetContext())
{
var options = new DataLoadOptions();
options.LoadWith<Account>(a => a.Email);
options.LoadWith<Account>(a => a.Profile);
options.LoadWith<Account>(a => a.HostInfo);
dc.LoadOptions = options;
try
{
account = (from a in dc.Accounts
where a.UserName == userName
select a).FirstOrDefault();
}
catch
{
//oops
}
}
Just wanted to add: there is now a shorter form for declaring trivial properties:
public Profile Profile { get; set; }
public Email Email { get; set; }
public HostInfo HostInfo { get; set; }

Categories