How to create a record with the current user - asp.net-mvc

I am trying to add a create controller method for a child table of application user. I can't figure out how to populate the user id. I'm in the process of learning mvc and this seems like such a basic concept, but I can't get it to work. Here is my class.
public class Ticket
{
[Key]
public long Id { get; set; }
[StringLength(128), MinLength(3)]
[ForeignKey("AspNetUser")]
public virtual string AspNetUserId { get; set; }
public virtual ApplicationUser AspNetUser { get; set; }
public DateTime Date { get; set; }
public string Request { get; set; }
}
Here is my index - hopefully pulling only records associated to the current user. I don't have seed data setup, so I have to get create working in order to test this, but the view comes up.
public ActionResult Index()
{
//var userId = User.Identity.GetUserId();
var userId = UserManager.FindById(User.Identity.GetUserId());
var tickets = db.Tickets.Where(m => m.AspNetUser == userId); ;
return View(tickets.ToList());
}
My create get which also comes up, but doesn't seem to be linked up.
public ActionResult Create()
{
return View();
}
And here is my troublesome create post method. When I click submit nothing happens.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,AspNetUserId,Date,Request")] Ticket ticket)
{
if (ModelState.IsValid)
{
ticket.AspNetUserId = User.Identity.GetUserId();
db.Tickets.Add(ticket);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ticket);
}
Within my view I have a #Html.HiddenFor(model => model.Id) for the Id, but since it's not posting I assume my ModelState is not valid.
So frustrating.

Related

Querying database but getting null back

I'm trying to list the items from my database into my view but I'm getting null back.
I know the connection must be working to a certain extent because in my database the tables didn't exist but once I ran my program it did create the tables. However when I add content into my table my view still returns NULL.
Also, haven't touched the Review table yet, just worried about getting Restaurants working.
Restaurant.cs
namespace OdeToFood.Models
{
public class Restaurant
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string Country { get; set; }
public ICollection<RestaurantReview> Reviews { get; set; }
}
}
OdeToFood.cs
namespace OdeToFood.Models
{
public class OdeToFoodDb : DbContext
{
public DbSet<Restaurant> Restaurants { get; set; }
public DbSet<RestaurantReview> Reviews { get; set; }
}
}
Controller
OdeToFoodDb _db = new OdeToFoodDb();
public ActionResult Index()
{
var model = _db.Restaurants.ToList();
return View();
}
Index.cshtml
#model IEnumerable<OdeToFood.Models.Restaurant>
#{
ViewBag.Title = "Home Page";
}
#{
if (Model != null)
{
foreach (var item in Model)
{
<div>
<h4>#item.Name</h4>
<div>#item.City, #item.Country</div>
<hr />
</div>
}
}
else
{
<h1>Null</h1>
}
}
You need to pass to model back to the view.
OdeToFoodDb _db = new OdeToFoodDb();
public ActionResult Index()
{
var model = _db.Restaurants.ToList();
return View(model);
}
You never actually send the model to the view. Pass it as an argument:
OdeToFoodDb _db = new OdeToFoodDb();
public ActionResult Index()
{
var model = _db.Restaurants.ToList();
return View(model);
}
Additionally, it's generally a good idea not to create database contexts in a shared scope. Keep the context as close to where it's used as possible and only expand its scope when you really need to. Something like this:
public ActionResult Index()
{
using (var _db = new OdeToFoodDb())
{
var model = _db.Restaurants.ToList();
return View(model);
}
}
Database contexts/connections in a shared scope is just asking for problems unless you pay close attention to what you're doing. As the code gets more complex, it becomes more likely that other methods will try to use it and it may be in an unknown state at that time.

How to create different data with each different user?

Im using asp.net entity framework and asp.net identity, and what im trying to do is to register with some account and to make some data with user#1(agentmi6) like:
and then log in with user#2(john) and make some data with this user:
so my main problem is how to make users view only their created data.
In my movie model i added a relation with application user
public class Movie
{
[Key]
public int MovieId { get; set; }
public string Name { get; set; }
public string Year { get; set; }
public string Genre{ get; set; }
public string ApplicationUserID { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
then in MoviesController added this query to get all movies created by the current registered user:
public ActionResult Index()
{
var currentUser = User.Identity.GetUserId();
var movies = db.Movies.Where(x => x.ApplicationUserID == currentUser);
return View(movies);
}
and in the Create action of the controller when i create a movie i add the current user to that movie
public ActionResult Create(Movie movie)
{
if (ModelState.IsValid)
{
movie.ApplicationUserID = User.Identity.GetUserId();
db.Movies.Add(movie);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
in all Views of MoviesController i removed the application user html since i dont want to show in index and when creating movies.
and in the end i got what i was looking for.
Login with user a#a.com and made a movie which is only visible to him,
and second user v#v.com that can see only his movie.

Selected Items in MVC select list are null

I have the following model -
public class RoleModel
{
public int Id { get; set; }
public string RoleName { get; set; }
public virtual IEnumerable<UserModel> Users { get; set; }
public virtual IEnumerable<UserModel> SelectedUsers { get; set; }
public RoleModel()
{
}
}
The users IEnumerable is a list of users who are currently assigned to the selected role. When the View loads, this is populated correctly.
When I press Save in the view, the SelectedUsers IEnumerable and the Users IEnumerable is null, meaning I cannot unassign all users and reassign the selected users to the role.
Any ideas why the IEnumerables are null when pressing Save?
View
#Html.ListBoxFor(model => model.SelectedUsers, new SelectList(Model.Users, "Id", "SurnameFirstName"))
Controller
[Authorize]
[HttpGet]
public ActionResult Details(int id = 0)
{
RoleModel role = _roleService.GetById(id);
if (role == null)
{
return HttpNotFound();
}
return View(role);
}
[HttpPost]
public ActionResult Details(RoleModel model)
{
if (ModelState.IsValid)
{
_roleService.ReassignRights(model);
_roleService.ReassignUsers(model);
_roleService.Update(model);
return RedirectToAction("Index");
}
return View(model);
}
Reassign Users method in RoleService.cs
public void ReassignUsers(RoleModel role)
{
var roleDal = _roleRepository.FindById(role.Id);
//_roleRepository.ClearUsersForRole(role.Id);
foreach (var user in role.SelectedUsers)
{
}
}
Try replacing the type of UserModel into type of of UserModel's PK. If UserModel::Id is int then change the SelectedUsers as below.
public virtual IEnumerable<int> SelectedUsers { get; set; }
While the Users property will still be null because there is no input that is related to Users, it's just being used to render the html options in the ListBoxFor. You need to set the value again when it's submitted.
DEMO

Can user hack values in action parameter?

Example:
I have table Orders and table OrderPositions.
public partial class Orders
{
public Orders()
{
this.OrderPositions = new HashSet<OrderPositions>();
}
public int OrderId { get; set; }
public string Title { get; set; }
public virtual ICollection<OrderPositions> OrderPositions { get; set; }
}
public partial class OrderPositions
{
public int OrderPositionId { get; set; }
public int OrderId { get; set; }
public string Name { get; set; }
public virtual Orders Orders { get; set; }
}
On the view user can modify single record from OrderPositions table.
In controller:
[HttpPost]
public ActionResult Edit(OrderPositions orderPosition)
{
// save orderPosition
}
So parameter orderPosition.Orders should be = null because on the form in view user can modify only order position. But can user hack it? I mean that in parameter orderPosition.Orders won't be null and I update record not only in table OrderPositions but also in table Orders? Or ASP.NET MVC prevent from that situation?
It really depends on what you do here
[HttpPost]
public ActionResult Edit(OrderPositions orderPosition)
{
// save orderPosition
}
If you're saving the whole entity then yes there is nothing stopping a user passing over addition entity properties. There are a few ways to prevent this though, here are a couple...
1.Create a new entity at the point of saving
[HttpPost]
public ActionResult Edit(OrderPositions orderPosition)
{
if(ModelState.IsValid)
{
var order = new OrderPositions
{
OrderPositionId = orderPosition.OrderPositionId,
OrderId = orderPosition.OrderId,
Name = orderPosition.Name
};
//Then save this new entity
}
}
2.Create a Model specific to the entity's action
public class EditOrderPosition
{
[Required]
public int PositionId { get; set; }
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
[HttpPost]
public ActionResult Edit(EditOrderPosition model)
{
if(ModelState.IsValid)
{
var order = new OrderPositions
{
OrderPositionId = model.PositionId,
OrderId = model.Id,
Name = model.Name
};
//Then save this new entity
}
}
I generally go with the 2nd method as it stops direct user involvement with my entities. As a rule of thumb I never use entity objects as parameters in controller actions.
Hope this helps
Yes they can. This is one reason I do not expose my entities as a parameter to action methods, instead I use DTOs that only have the properties that I expect.
This is an example of the Mass Assignment Vulnerability.
Yes, there is nothing preventing a rogue app calling your endpoint with arbitrary data. Always validate everything serverside.

Entity Framework Create and Update in List

I have an entity that I am updating and the method in the controller has these lines in it;
db.Entry(userdetails).State = EntityState.Modified;
try {
db.SaveChanges();
}
I think this is fine, however the entity has a collection in it and these records needs to be created, not updated. I am therefore getting this error;
"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
I have read that this may be because my AppUserInfo objects have an id of 0 because they need to be added.
The class for the outer entity looks like this;
public class User {
public Guid UserId { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public List<AppUserInfo> InfoList { get; set; }
}
Now in this situation for example, the phone number could have been modified, and the 'InfoList' collection has new items in that need to be created. Some may need to be updated as well. The 'AppUserInfo' class looks like this;
public class AppUserInfo
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AppUserInfoId { get; set; }
public string info { get; set; }
}
How do I handle this in EF? Am I allowed to save collections in this way? I do not know how to say that there are modifications and additions and possible deletions in the list to EF. Do I simply just remove the line;
db.Entry(userdetails).State = EntityState.Modified;
Any info on the correct way to do this would be very helpful.
Thanks,
db.Entry(userdetails).State = EntityState.Modified;
This line of code is used for editing your objects, if you have no trouble creating your User then all you need is another controller to handle AppUserInfo
Lets imagine that you have a AppUserInfo controller, and you have a AppUserInfo viewModel somewhere. Then you can somthing like the following to accomplish what you want to do.
Your view model like this:
puclic class AppUserInfoCreateViewModels
{
public Guid UserId { get; set; }
public string Info { get; set; }
}
Then your Controller like this:
public class AppUserInfoController : Controller
{
private readonly IYourDataSource _db;
public AppUserInfoController(IYourDataSource db)
{
_db = db;
}
[HttpGet]
public ActionResult Create(int userId)
{
var model = new AppUserInfoCreateViewModels();
model.UserId = userId;
return View(model);
}
[HttpPost]
public ActionResult Create(AppUserInfoCreateViewModels viewModel)
{
if(ModelState.IsValid)
{
var user = _db.Users.Single(d => d.UserId == viewModel.UserId);
var appUserInfo= new AppUserInfo();
appUserInfo.Info= viewModel.Infor;
user.AppUserInfos.Add(appUserInfo);
_db.Save();
return RedirectToAction("detail", "user", new {id = viewModel.UserId});
}
return View(viewModel);
}
}
I hope this helps, ask any questions you may have

Resources