.NET Core identity user id comes differently from database - asp.net-mvc

I am trying to get current user's id with identity in .NET Core. Managed to create new user and login with it but whenever logging in with same user identity returns different guid.
I have a product entity which stores userId as foreign key. Whenever I try to add new product it throws error because given userId is not in user table.
But I register a new user and go add new product immediately it works. Lists products. However, logout and login again with same user, products are not listed. When I debug it saw that the _userManager.GetUserId(User) returning a different value.
Why could it be happened?
How can I fix this?
UPDATE
My User Creation Code
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(HomeController.Index), "Home");
}
My User Login Code
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
return RedirectToLocal(returnUrl);
}
Here is My ApplicationUser Model Class
public class ApplicationUser : IdentityUser
{
[Key]
[Column("TABLEID")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override string Id { get; set; }
[Required]
[Column("AD")]
public string Name { get; set; }
[Required]
[Column("SOYAD")]
public string Surname { get; set; }
[Required]
[Column("AKTIF")]
public bool IsActive { get; set; }
[Required]
[Column("SIL")]
public bool IsDeleted { get; set; }
[Required]
[Column("KAYITTARIHI")]
public DateTime RecordDate { get; set; }
public List<Vehicle> Vehicles { get; set; }
public List<VehicleImage> VehicleImages { get; set; }
public List<TransportAdvertise> TansportAdvertises { get; set; }
public List<TransportRequest> TransportRequests { get; set; }
}
Here is My DbContext
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Vehicle> Vehicles { get; set; }
public DbSet<VehicleImage> VehicleImages { get; set; }
public DbSet<TransportAdvertise> TransportAdvertises { get; set; }
public DbSet<TransportRequest> TransportRequests { get; set; }
public DbSet<TransportRoute> TransportRoutes { get; set; }
public DbSet<City> Cities { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
// Identity built-in models.
builder.Entity<IdentityUser>().ToTable("TBLKULLANICI");
builder.Entity<IdentityRole>().ToTable("TBLROL");
builder.Entity<IdentityUserRole<string>>().ToTable("TBLKULLANICIROL");
builder.Entity<IdentityRoleClaim<string>>().ToTable("TBLROLECLAIM");
builder.Entity<IdentityUserClaim<string>>().ToTable("TBLUSERCLAIM");
builder.Entity<IdentityUserLogin<string>>().ToTable("TBLUSERLOGIN");
builder.Entity<IdentityUserToken<string>>().ToTable("TBLUSERTOKEN");
// Custom models.
builder.Entity<Vehicle>().ToTable("TBLARAC");
builder.Entity<VehicleImage>().ToTable("TBLARACRESIM");
builder.Entity<TransportAdvertise>().ToTable("TBLNAKLIYEILAN");
builder.Entity<TransportRequest>().ToTable("TBLNAKLIYEISTEK");
builder.Entity<TransportRoute>().ToTable("TBLROTA");
builder.Entity<City>().ToTable("TBLSEHIR");
// FK mappings..
builder.Entity<Vehicle>()
.HasOne(v => v.User)
.WithMany(u => u.Vehicles)
.HasForeignKey(v => v.UserId)
.HasConstraintName("FK_VEHICLE_USER");
// Vehicle image model fks.
builder.Entity<VehicleImage>()
.HasOne(vi => vi.Vehicle)
.WithMany(v => v.Images)
.HasForeignKey(vi => vi.VehicleId)
.HasConstraintName("FK_VEHICLE_IMAGE_VEHICLE");
builder.Entity<VehicleImage>()
.HasOne(vi => vi.User)
.WithMany(u => u.VehicleImages)
.HasForeignKey(vi => vi.UserId)
.HasConstraintName("FK_VEHICLE_IMAGE_USER");
// TransportAdvertise model fks.
builder.Entity<TransportAdvertise>()
.HasOne(ta => ta.User)
.WithMany(u => u.TansportAdvertises)
.HasForeignKey(ta => ta.UserId)
.HasConstraintName("FK_TRANSPORT_ADS_USER");
builder.Entity<TransportAdvertise>()
.HasOne(ta => ta.Vehicle)
.WithMany(v => v.TransportAdvertises)
.HasForeignKey(ta => ta.VehicleId)
.HasConstraintName("FK_TRANSPORT_ADS_VEHICLE");
// TransportRequest model fks.
builder.Entity<TransportRequest>()
.HasOne(tr => tr.User)
.WithMany(u => u.TransportRequests)
.HasForeignKey(tr => tr.UserId)
.HasConstraintName("FK_TRANSPORT_RQST_USER");
builder.Entity<TransportRequest>()
.HasOne(tr => tr.TransportAdvertise)
.WithMany(ta => ta.TransportRequests)
.HasForeignKey(tr => tr.TransportAdvertiseId)
.HasConstraintName("FK_TRANSPORT_RQST_ADS");
// TransportRoute model fks.
builder.Entity<TransportRoute>()
.HasOne(tr => tr.City)
.WithMany(c => c.TransportRoutes)
.HasForeignKey(tr => tr.CityId)
.HasConstraintName("FK_TRANSPORT_ROUTE_CITY");
builder.Entity<TransportRoute>()
.HasOne(tr => tr.TransportAdvertise)
.WithMany(ta => ta.TransportRoutes)
.HasForeignKey(tr => tr.TransportAdvertiseId)
.HasConstraintName("FK_TRANSPORT_ROUTE_ADS");
}
Vehicle Creation Code
[HttpPost]
public IActionResult AddNewVehicle(VehicleViewModel model)
{
var id = _userManager.GetUserId(User);
// model property check needs to be done before.
// vehicle table does not accept nulls.
var vehicle = new Vehicle()
{
Model = model.Model,
Capacity = model.Capacity,
Description = model.Description,
Year = model.Year,
Active = true,
Delete = false,
RecordDate = DateTime.Now,
UserId = id
};
_vehicleService.AddNew(vehicle);
return RedirectToAction("Index");
}

Do a simple experiment... Create a new user and note its Id... Immediately add a product and check again if the user ID has changed after the save operation. It is possible the user details is being mutated/changed upon save operation. You can also paste the code samples for creating a user, creating a product and retrieving a user here. The problem is most likely where u are saving the vehicles. At that point the user details is either being mutated, or the vehicles is being stored with a different user. The vehicles has a 1 to 1 relationship with the user. So when creating a vehicle be careful not to be doing vehicle.user = new User; Ensure you are doing the following.
1- Get the user from the Db :
var user = await UserManager.FindByIdAsync("Id of currently logged in
user");
2- set vehicle.user to the user retrieved above:
vehicle.user = user.
You can share code where u are saving vehicle for people to be able to help. Also you can prevent the user Id from being automatically generated in the db and add a [Required] attribute on the Id field. You can the do
user.Id = Guid.Guid.NewGuid()
. This can help prevent issues where a new user is automatically added to the db when u create a vehicle

Related

CRUD operation on many-to-many relationship in ASP.NET MVC

I have 2 tables in my ASP.NET MVC project: store and product.
The relationship between these tables is many-to-many, so I also have a table StoreProduct.
I want to do CRUD operations on table product, but I need column StoreID from table StoreProduct.
How can I get the storeid from StoreProduct to do CRUD operation on product table?
here is how you'd do it if you want a many-to-many relationship with enums i.e. you want many user roles and work with enums moreover, you can't store it as a flag because you need to know about the roles/privileges without source code(on db side).
TLDR ;) You'd have to create a join table which contains about about who has what privilege(or roles if you want).
There is a Users table which has a list of privileges, a privilege table which has privilege definition i.e. Id, name. And a Join table which will have User and Privilege as it's key. If an entry against this user/privilege combination is present that means this user has this privilege/role.
The code:
//for enum
public enum UserPrivilegeId : int
{
AddProject = 0,
ModifyProject = 1,
DeleteProject = 2,
AddUser = 3,
ModifyUser = 4,
DeleteUser = 5
}
//User class
public record User
{
public User()
{
Privileges = new HashSet<Privilege>();
}
public int Id { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public virtual ICollection<Privilege> Privileges { get; set; }
public virtual List<UserPrivilege> UserPrivileges { get; set; }
}
//Privilege Class
public record Privilege //note record is IMPORTANT here, because this forces it to compare by value, if you want to *use a class*, then make sure to override GetHashCode and Equals
{
public Privilege()
{
Users = new HashSet<User>();
}
public Privilege(UserPrivilegeId privilegeId, string privilegeName)
{
PrivilegeId = privilegeId;
PrivilegeName = privilegeName;
Users = new HashSet<User>();
}
[Key]
public UserPrivilegeId PrivilegeId { get; set; }
public string PrivilegeName { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual List<UserPrivilege> UserPrivileges { get; set; }
}
//and finally the UserPrivilege join class
public record UserPrivilege
{
public UserPrivilegeId PrivilageId { get; set; }
public Privilege Privilage { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
//The set-up in dbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Privilege>()
.HasKey(p => p.PrivilegeId);
modelBuilder.Entity<Privilege>()
.Property(p => p.PrivilegeId)
.HasConversion<int>();
modelBuilder.Entity<User>()
.HasMany(user => user.Privileges)
.WithMany(privilege => privilege.Users)
.UsingEntity<UserPrivilege>(
j => j
.HasOne(up => up.Privilage)
.WithMany(u => u.UserPrivileges)
.HasForeignKey(up => up.PrivilageId),
j => j
.HasOne(up => up.User)
.WithMany(p => p.UserPrivileges)
.HasForeignKey(up => up.UserId),
j =>
{
j.Property(u => u.PrivilageId).HasConversion<int>();
j.HasKey(u => new { u.PrivilageId, u.UserId });
});
//this adds definitions of privileges to the table
modelBuilder.Entity<Privilege>()
.HasData(
Enum.GetValues(typeof(UserPrivilegeId))
.Cast<UserPrivilegeId>()
.Select(p => new Privilege(p, p.ToString())));
base.OnModelCreating(modelBuilder);
}
Use it by creating a wrapper around it with a boolean on IsActive like this:
public class UserPrivelegesDTO
{
public UserPrivelegesDTO(UserPrivilegeId privilege, bool isActive)
{
this.PrivilegeId = privilege;
this.PrivilegeName = privilege.ToString();
this.IsActive = isActive;
}
public UserPrivilegeId PrivilegeId { get; set; }
public string PrivilegeName { get; set; }
public bool IsActive { get; set; }
}
If you want to convert from List<Privileges> to List<UserPrivilegeDTO>, you can
return await _context.Privileges.OrderBy(x => x.PrivilegeId).ToListAsync(cancellationToken);
To Convert back to List<Privileges>, simply
var privileges = _userPrivilegesViewModel.Privileges.Where(x => x.IsActive).Select(x => new Privilege(x.PrivilegeId, x.PrivilegeName));
If you want to check if the user has privilege
var user = _context.Users.Include(x => x.Privileges).FirstAsync(x => x.Id == 1);
if (request.Editor.Privileges.Any(p => p.PrivilegeId == UserPrivilegeId.ModifyUser))
return true;
When you want to update privileges
var PrivilegeChangeUser = await
_context.Users
.Include(user => user.Privileges)
.Include(user => user.UserPrivileges)
.FirstOrDefaultAsync(user => user.Id == request.UserId);
//**NOTE**you *need* to include the join table i.e. UserPrivileges in order replace the privileges, if you do not include it EF will try to add the privileges which already exist :(
//To update the privileges from an IEnumerable<UserPrivilegeIdEnum>
//first get the privileges objects and add that to users
var AllPrivileges =
await _context.Privileges
.Include(x => x.UserPrivileges)
.Include(x => x.Users)
.Where(x =>
request.Privileges
.Contains(x.PrivilegeId)
).ToListAsync(cancellationToken);
PrivilegeChangeUser.Privileges = AllPrivileges;
I have used an enum as the primary key, but you can of course just use a standard int too. For more information read this about how to configure a many-to-many relationship in efcore

I want to get item from a foreign key inside a foreign key (if that makes any sense...)

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();

How to create a record with the current user

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.

how to properly configure using fluent api as data annotation not working properly

This is what i was trying to achieve but entity framework didn't let me do this:
modelBuilder.Entity<ApplicationUser>()
.HasMany(u => u.Following)
.WithMany(u => u.Followers)
.Map(m =>
{
m.ToTable("FollowTables");
m.MapLeftKey("UserId");
m.MapRightKey("FollowId");
});
But if i use this code then in database, table is created but with no primary key in it.only two column got created.
If i use this code then proper table is created with primary key too in it.
public class FollowTable
{
[Key]
public int autoId { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public ApplicationUser User { get; set; }
[ForeignKey("Follow")]
public int? FollowId { get; set; }
public ApplicationUser Follow { get; set; }
}
here, autoId is primary key and UserId and FollowId both are foreign key to ApplicationUser class where UserId is user's own id and FollowId are the ids which user is following.
autoId UserId FollowId
1 4 11
2 4 12
3 4 13
but my ApplicationUser class structure is something like this :
public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public string Address { get; set; }
public ApplicationUser()
{
this.Followers = new HashSet<ApplicationUser>();
this.Following = new HashSet<ApplicationUser>();
}
// calculated properties
public ICollection<ApplicationUser> Following { get; set; }
public ICollection<ApplicationUser> Followers { get; set; }
i want to use these collections badly.
But this code i think is creating a new table in database named dbo.ApplicationUserApplicationUsers with columns ApplicationUser_Id and ApplicationUser_Id1.
i dont want this table.also my code is too entering data into this table like this follow function:
public virtual bool Follows(int userId, int accountId)
{
var targetUser = _context.Users
.Where(u => u.Id == accountId && u.Id != userId)
.Select(u => new
{
User = u,
CanFollow = u.Followers.All(f => f.Id != userId)
})
.FirstOrDefault();
if (targetUser == null || !targetUser.CanFollow) return false;
var currentUser = _context.Users
.Include(u => u.Following)
.FirstOrDefault(u => u.Id == userId);
if (currentUser == null) return false;
if (currentUser.Following.All(u => u.Id != accountId))
{
currentUser.Following.Add(targetUser.User);
_context.SaveChanges();
return true;
}
return false;
}
Now, suggest me how to configure mapping with or without fluent api.i want to enter data in follow table not in table which was auto generated.
You're conflating two separate relationships. If you were dealing with something like "Friends", then that's a straight M2M: by virtue of me being your friend, you are also my friend. However, a user could follow many people and be followed by none, that means you need two separate relationships to to track that.
public class ApplicationUser
{
...
public virtual ICollection<ApplicationUser> Following { get; set; }
public virtual ICollection<ApplicationUser> Followers { get; set; }
public class Mapping : EntityTypeConfiguration<ApplicationUser>
{
HasMany(m => m.Following).WithMany();
HasMany(m => m.Followers).WithMany();
}
}
Then in your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ApplicationUser.Mapping());
}
Entity Framework will handle the join tables. You don't need to worry about that, unless you need an M2M with a payload, like the date someone started following or the user started following someone else.

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.

Resources