How to populate two tables in create method [asp.net mvc] - sql

I have two tables in my database: referents and users.
Referents:
FirstName|LastName|Phone|Password|ConfirmPassword|UserName|Email
And
Users:
FirstName|LastName|UserRole|Password|ConfirmPassword|UserName
Currently Create method in my controler looks like:
// GET: Referents/Create
public ActionResult Create()
{
return View();
}
// POST: Referents/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ReferentID,FirstName,LastName,Phone,Email,Password,ConfirmPassword")] Referents referents)
{
if (ModelState.IsValid)
{
db.Referents.Add(referents);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(referents);
}
Obvious, right now I'm populating only Referents.
How to also populate Users table with certain data (UserName, LastName, UserName) in the same time?
In UserRole column I want to write string "referent".
EDIT Added model:
Referents:
namespace StudentService.Models
{
public class Referents
{
[Key]
public int ReferentID { get; set; }
[Required(ErrorMessage = "Морате унети име!")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Презиме је обавезно!")]
public string LastName { get; set; }
[Required(ErrorMessage = "Корисничко име је обавезно!")]
[Index(IsUnique = true)]
public string UserName { get; set; }
[Required(ErrorMessage = "Унесите исправан број телефона.")]
[DataType(DataType.PhoneNumber)]
public string Phone { get; set; }
[Required(ErrorMessage = "Унесите исправну адресу електронске поште.")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "Шифра је обавезна!")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Compare("Password", ErrorMessage = "Морате потврдити лозинку!")]
[DataType(DataType.Password)]
public string ConfirmPassword { get; set; }
}
}

You need to create the item for users and add it your your database.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ReferentID,FirstName,LastName,Phone,Email,Password,ConfirmPassword")] Referents referents)
{
if (ModelState.IsValid)
{
// Create the user data for the current referent.
Users currentUser = new Users(){
FirstName = referents.FirstName,
UserRole = "referent"
// .... Finish initializing fields of your model.
};
db.Referents.Add(referents);
// Save the new user in your database.
db.Users.Add(currentUser);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(referents);
}
Without seeing your model code it is hard to be specific, but you should be able to use that as an example. Just finish initializing the currentUser object to what you need it to be. It is possible this won't work depending on your schema.

Related

EF Core updates fields that doesn't exist in a ViewModel

I am trying to update a record using a view model. I have CreatedBy and DateCreated columns in my model and I don't want them ever changed so I didn't include them in my viewmodel, yet when I update the record, they get updated too. And if they're not nullable, they throw error. What am I doing wrong?
Location.cs
public int LocationId { get; set; }
public string LocationName { get; set; }
public string Address { get; set; }
[ForeignKey("LocationCreator")]
public string? CreatedBy { get; set; }
public AppUser? LocationCreator { get; set; }
[ForeignKey("LocationModifier")]
public string? ModifiedBy { get; set; }
public AppUser? LocationModifier { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
LocationEditVM
public int LocationId { get; set; }
public string LocationName { get; set; }
public string Address { get; set; }
public string? Description { get; set; }
public string? ModifiedBy { get; set; }
public DateTime DateModified { get; set; }
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, LocationEditVM locationEditVM)
{
if (id != locationEditVM.LocationId)
{
return NotFound();
}
var currentUser = await userManager.GetUserAsync(User);
locationEditVM.ModifiedBy = currentUser.Id;
locationEditVM.DateModified = DateTime.Now;
if (ModelState.IsValid)
{
try
{
var location = mapper.Map<Location>(locationEditVM);
_context.Update(location);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!LocationExists(locationEditVM.LocationId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(locationEditVM);
}
If you want to keep existing values, you should get the existing entity from the DB and then map values from the view model to that.
Currently in your EF model it gets the default values for the fields that you didn't have in the view model and end up setting those to DB.
You are made mistake at the time of mapping. As per your model and entity model the entity model get default value of respective column such as CreatedBy and DateCreated is not part of your view model.
At the time of updating you are map viewmodel to entity using mapper, So your entity columns set the default value of respective data types as same will updated to database.
If you want to avoid that then you need to carry both column in viewmodel like other columns or you have to update that manually at the time of updating.

Recommended approach to show a custom user property in a view?

I'm trying to build a simple helpdesk application. In this app, when a ticket is created and displayed, I want to show the first name of the creating user. I am trying to solve how to do this in the best possible way.
I've extended the ApplicationUser class and added FirstName and LastName columns. I also created two foreign keys in my Tickets table, one for the user who created the ticket and one for the agent gets assigned to that ticket. So when the ticket is displayed, I need to show both creators and agents first name + last name's, instead of their UserId's.
This is my ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Ticket> Users { get; set; }
public ICollection<Ticket> Agents { get; set; }
}
This is my model:
public class Ticket
{
public int Id { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public int Status { get; set; }
public string UserId { get; set; }
public string AgentId { get; set; }
public DateTime Created { get; set; }
public DateTime LastUpdated { get; set; }
public DateTime? Completed { get; set; }
public bool Muted { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser TicketUser { get; set; }
[ForeignKey("AgentId")]
public virtual ApplicationUser TicketAgent { get; set; }
}
This is my DbContext:
public DbSet Tickets { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity()
.HasOne(m => m.TicketUser)
.WithMany(t => t.Users)
.HasForeignKey(m => m.UserId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity()
.HasOne(m => m.TicketAgent)
.WithMany(t => t.Agents)
.HasForeignKey(m => m.AgentId)
.OnDelete(DeleteBehavior.Restrict);
}
This is the controller action to display a specific ticket:
[HttpGet]
public ViewResult Tickets(int id)
{
TicketDetailsViewModel ticketDetailsViewModel = new TicketDetailsViewModel()
{
Ticket = _ticketRepo.GetTicket(id)
};
return View(ticketDetailsViewModel);
}
This is my viewmodel:
public class TicketDetailsViewModel
{
public Ticket Ticket { get; set; }
}
Now, I can display the full name in my view if I do this:
#inject UserManager userManager;
#{
var ticketUser = (await userManager.FindByIdAsync(Model.Ticket.UserId)).FirstName + " " + (await userManager.FindByIdAsync(Model.Ticket.UserId)).LastName;
}
But I am not sure if this is a good way to do it. I'd like to learn what is the best way to achive this.
Thank you very much.
You can define a _fullname in your ApplicationUser , and then if firstname and lastname both exist, you can directly call Fullname, like:
public class ApplicationUser : IdentityUser
{
private string _fullName; //new property
public string FirstName { get; set; }
public string LastName { get; set; }
[NotMapped]
public string FullName
{
get
{
return _fullName = this.FirstName + "." + this.LastName;
}
set
{
_fullName = value;
}
}
public ICollection<Ticket> Users { get; set; }
public ICollection<Ticket> Agents { get; set; }
}
In view, just call FullName:
#{
var ticketUser = (await userManager.FindByIdAsync(Model.Ticket.UserId)).FullName;
}
In these scenarios I usually prefer to go with an extension method instead of an additional property like proposed by user Jerry Cai, the model remains lighter and cleaner imho:
public static class ApplicationUsersExtensions
{
public static string GetFullname(this ApplicationUser user)
{
return $"{user.FirstName}.{user.LastName}";
}
}

How to get all user with the associate organization?

Its keep a week that I'm trying to figure this out. I hope to get help from community. Here is a scenario:
I have a entity class called "Company". One company has many users (One-To-Many)
public class User : IdentityUser<int>
{
public User()
{
Company = new Company();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime CreatedDate { get; set; } = DateTime.Now;
public virtual ICollection<UserRole> UserRoles { get; set; }
public virtual Company Company { get; set; }
public int CompanyId { get; set; }
}
I also have a company entity like so.
public class Company
{
public Company()
{
Users = new List<User>();
}
public int CompanyId { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Company Name")]
public string CompanyName { get; set; }
public virtual List<User> Users { get; set; }
}
Now, I can add new company but I cannot add users.
Here is my Controller/Create
[HttpGet]
public IActionResult Create(int companyId)
{
ViewData["UserList"] = new SelectList(_context.Companies, "CompanyId", "CompanyName", companyId);
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(RegisterViewModel viewModel)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(viewModel.UserName);
if (user == null)
{
user = new User
{
UserName = viewModel.UserName,
FirstName = viewModel.FirstName,
LastName = viewModel.LastName,
Email = viewModel.UserName,
PhoneNumber = viewModel.PhoneNumber
};
var result = await _userManager.CreateAsync(user, viewModel.Password);
if (result.Succeeded)
{
RedirectToAction(nameof(Index));
}
else
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
return View();
}
}
return View("Success");
}
ViewData["CompanyId"] = new SelectList(_context.Companies, "Id", "Id", viewModel.CompanyId);
return View();
}
When i run the program and enter data in a POST/Form,
if (ModelState.IsValid)
{
Model.IsValid is always return false. and it ask to enter Company information which i don't want it because i already have company data. I all was trying to do is have Foreign Id linked with user.
Since Identity already have built in function like
var users = await _userManager.GetUserAsync(viewModel.UserName);
How to i also query like Include in GetUserAsync method?

Registering a User to .Net Core Project

I am writing a .Net Core project in which I am trying to implement a user registration process. I've used the "basic" template provided by Microsoft as I am writing in Visual Studio 2017.
I have stumpled into problem with how the user is registered. I have created a pretty substantial form with the information I require to complete a registration:
RegisterViewModel:
public class RegisterViewModel
{
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public bool Admin { get; set; }
public bool Manager { get; set; }
public int CustomerID { get; set; }
public string First_Name { get; set; }
public string Last_Name { get; set; }
public string Mobile { get; set; }
public DateTime? Date_Of_Birth { get; set; }
public string Gender { get; set; }
public string Status { get; set; }
public Customer Customer { get; set; }
}
I have left out some of the unimportant lines from the above, as that is not a part of my problem. The part of displaying my form works as intended but when I try to run my [HttpPost] part I stumble into problems.
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//do something else here if user was created.
}
}
}
My database is a relational database (has foreign keys) which means that when I try to add a User through this process and the user doesn't have a CustomerID (added or it isn't set) the display of the "User" index doesn't work (breaks).
In the register code above a user is created in my database however none of the fields that was filled from my form are input to my database. And worst of all the customer ID (which is a foreign key) doesn't get inserted, even though it resides in the model.
How do I pass these variables that I NEED from this register method?
I figured it out.
It seems that Visual Studio was so kind to supply me with an ApplicationUser.cs class that just required modification.
The following line was found in my code posted above:
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
I found the ApplicationUser class and modified it to contain my model:
public class ApplicationUser : IdentityUser
{
public int CustomerID { get; internal set; }
public string FirstName { get; internal set; }
public string LastName { get; internal set; }
public string FullName { get; internal set; }
public string Gender { get; internal set; }
public string CName { get; internal set; }
public DateTime CDate { get; internal set; }
public string MobileNumber { get; internal set; }
public DateTime? DateOfBirth { get; internal set; }
public string Status { get; internal set; }
public DateTime StartDate { get; internal set; }
public DateTime EndDate { get; internal set; }
public string Contact1 { get; internal set; }
public string Contact2 { get; internal set; }
}
Hope this helps someone else out there!

How to log in my custom user table in MVC 4?

I want to use my custom User table in MVC 4 code first application. I defined advance User table in my database context:
public class MyDatabase : DbContext
{
public DbSet<User> UserSet { get; set; }
public DbSet<News> NewsSet { get; set; }
...
}
Model is like:
public class User
{
[Key]
public int Id{ get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string SurName { get; set; }
...
}
When application start, it calls this:
WebSecurity.InitializeDatabaseConnection("MyDatabase", "Users", "Id", "UserName", autoCreateTables: true);
In controller I use Add(entity) to save user entity. After saving I want to log in user. But it does not work:
[HttpPost]
public ActionResult Register(User user)
{
var result = MyService.SaveUser(user);
WebSecurity.Login(result.UserName, result.Password, true);
return RedirectToAction("Index", "Profile", new { id = result.Id });
}
After saving user, it's data stored in my database, but it can not log in. How should I do?
Edit:
Is it right to save User entity with my business method? Or I must do it only with
WebSecurity.CreateUserAndAccount()?
If I can use my own save method, how to save password in database?
You could just use forms authentication directly.
[HttpPost]
public ActionResult Register(User user)
{
var result = MyService.SaveUser(user);
SignIn(result.Id, "");
return RedirectToAction("Index", "Profile", new { id = result.Id });
}
public void SignIn(string accountId, string roles)
{
var authTicket = new FormsAuthenticationTicket(
1,
accountId,
DateTime.Now,
DateTime.Now.AddMinutes(20),
false,
roles
);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
HttpContext.Current.Response.Cookies.Add(authCookie);
}
Here is a user class that will help you with password issue. It relies on BCrypt
public class UserAccount
{
public string Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string Password
{
get { return PasswordHash; }
set { PasswordHash = HashPassword(value); }
}
public string PasswordHash { get; private set; }
public List<string> Roles { get; set; }
public string AuthenticationRoles
{
get { return Roles == null ? "" : String.Join(",", Roles.Select(x => x.ToString())); }
}
public bool IsActive { get; set; }
public string Name { get; set; }
public bool PasswordIsValid(string password)
{
bool matches = BCrypt.Net.BCrypt.Verify(password, Password);
return matches;
}
private string HashPassword(string value)
{
return BCrypt.Net.BCrypt.HashPassword(value);
}
}