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.
Related
I'm using ASP.NET Core Identity. The user ID will be as FK in the Invite model and I'm trying to display all the users that are in the invite with the desired information.
I want to display the GameName within the GameID that is assigned to the user.
So it would be something like in invite show GameName (FK in user) GameTag (user) instead of GameID with a number.
Model classes:
public class Invite
{
public int ID { get; set; }
[ForeignKey("UserId")] // ICollection<Invite> in User
[Display(Name = "Users")]
public virtual ApplicationUser User { get; set; }
}
public class ApplicationUser : IdentityUser
{
public string Description { get; set; }
[ForeignKey("GameID")]
public int? GameID { get; set; }
public string GameTag { get; set; }
public virtual ICollection<Invite> Invite { get; set; }
}
public class Game
{
public int ID { get; set; }
[Display(Name = "Game")]
public string GameName { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUser { get; set; }//Allow Users to get Games FKID (Foreign key ID)
}
Getting the list of invites in the invite controller index and putting them inside viewbag for invite razor index page. it only shows GameID which is the FK inside User and I don't know how to get the information inside the Game FK from Invite that is assigned to user as FK
// GET: Invites
public async Task<IActionResult> Index()
{
ViewBag.InviteList = new List<String>();
var invite = _context.Invites;
var theuser = _context.ApplicationUser;
foreach (Invite i in invite)
{
foreach (ApplicationUser tu in theuser)
{
if (i.User.Id == tu.Id)
{
ViewBag.InviteList.Add(tu.GameID + " " +tu.GameTag);
}
}
}
return View(await _context.Invites.ToListAsync());
}
If anyone understands what I'm trying to say welcome to suggest a better title
Your code is not implemented correctly (besides the main requirement of showing GameName). Actually the info from Game is not referenced directly from ApplicationUser. Not sure why you don't include that in the class (together with GameID). Suppose you do that (including the property Game), the code can be simple like this:
var invites = await _context.Invites.AsNoTracking()
.Include(e => e.User.Game).ToListAsync();
//NOTE: this can contain duplicate strings added to `InviteList`, unless you
//include more data field in each item.
foreach (Invite i in invites)
{
ViewBag.InviteList.Add(i.User.Game.GameName + " " + i.User.GameTag);
}
If you don't want to include the property Game in the ApplicationUser, you need to join the entities, like this:
var games = await (from invite in _context.Invites
join game in _context.Games on invite.User.GameID equals game.ID
select new { game.GameName, invite.User.GameTag }).AsNoTracking().ToListAsync();
//to avoid duplicate items (as noted in the previous code)
ViewBag.InviteList = games.GroupBy(e => e.GameName)
.Select(g => g.First())
.Select(e => $"{e.GameName} {e.GameTag}").ToList();
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.
Im using the default Web Application with MVC + Identity for managing users, and im trying to make every registered user to make his movie list and to be able to see only his list when logs in.
So, for my example i have made a movie class
public class Movie
{
public int MovieId { get; set; }
public string MovieName { get; set; }
public string Year { get; set; }
public string Genre { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
and in ApplicationUser i have put
public virtual ICollection<Movie> Movies { get; set; }
to have a list of movies.
I also made a Entity FrameWork MovieController, used Movie as model and ApplicationDbContext as context.
So i have problems querying the db in order to get each user to view only his movie list, with the default scaffolding i get this in index action of MoviesController
public ActionResult Index()
{
var movies = db.Movies.ToList();
return View(movies);
}
If anyone has experience with my problem please help, any input is welcomed and much appreciated.
You need to add one more property in your movie class of ApplicationUserID for creating proper relationship between them like this:
public class Movie
{
public int MovieId { get; set; }
public string MovieName { get; set; }
public string Year { get; set; }
public string Genre { get; set; }
public string ApplicationUserID { get;set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
Now you can get all movies for a user like this:
public ActionResult Index()
{
ApplicationDbContext context = new ApplicationDbContext();
string userid = User.Identity.GetUserId();
IEnumerable<Movie> movies = new List<Movie>();
if (!string.IsNullOrEmpty(userid))
{
movies = context.Movies.Where(x => x.ApplicationUserID == userid);
}
return View(movies);
}
First you should change your link to applicationuser (from Movie) to be a collection (many to many).
public virtual ICollection<Movie> Movies { get; set; }
If you want to fetch a list of movies for the logged in user you can fetch the user and movie list like this:
var user = UserManager.FindById(User.Identity.GetUserId());
Debug.WriteLine(user.Email);
var movies = user.Movies.ToList();
foreach (var movie in movies)
{
Debug.WriteLine(movie.MovieName);
}
Instead of writing to the debug window you could return a view with movies like you have done in your own code.
-
I would also suggest that you make use of viewmodels for sending data to/from your views. The way you are doing it now could potentially send the entire database to your view (depending on your query ofc). You will also get a more granulated control of what you display to the user. (you could read this: what-is-viewmodel-in-mvc to get you started)
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.
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