I want to seed the default DB with an admin user before I start the project on .NET Core Default MVC application. The code is as below:
public void SeedDb(ApplicationDbContext Context, IServiceProvider ServiceProvider, IConfiguration Configuration)
{
if (Context.Users.Count() > 0) return;
var UserManager = ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var ApplicationUser = new ApplicationUser()
{
Email = Configuration["Email"],
NormalizedEmail = Configuration["Email"],
LockoutEnabled = false,
NormalizedUserName = Configuration["Email"],
SecurityStamp = "579355dd - a64c - 498d - a0b5 - 9e55754c9109",
EmailConfirmed = true,
ConcurrencyStamp = null,
Id = "977ec1a5-1ae7-4658-952a-6b5dccd75a85",
PasswordHash ="",
PhoneNumber = "333333333333",
LockoutEnd = null,
AccessFailedCount = 1,
PhoneNumberConfirmed = true,
TwoFactorEnabled = false,
UserName = Configuration["Email"]
};
var Password = HashPassword(ApplicationUser, Configuration["Password"]);
if (VerifyHashedPassword(ApplicationUser, Password, Configuration["Password"]) == PasswordVerificationResult.Success)
{
ApplicationUser.PasswordHash = Password;
}
Context.Users.Add(ApplicationUser);
Context.SaveChanges();
var RoleManager = ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
string[] Roles = { "Admin", "Manager", "User" };
foreach (string RoleName in Roles) {
RoleManager.CreateAsync(new IdentityRole(RoleName));
}
var Admin = Context.Users.SingleOrDefault(m => m.Email == Configuration["Email"]);
var Role = Context.Roles.SingleOrDefault(m => m.Name == Configuration["Role"]);
IdentityUserRole<string> UserRole = new IdentityUserRole<string>() { UserId = Admin.Id, RoleId = Role.Id };
Context.UserRoles.Add(UserRole);
Context.SaveChanges();
}
Everything runs perfect except I can't seed the UserRole DB with Data. From DBContext I add IdentityUserRole entity and save the changes to DB. Although nothing passed under the DB. Any suggestion?
Create a class named StartupDbInitializer:
using System;
using System.Collections.Generic;
using System.Linq;
using Core.Entities;
using Microsoft.AspNetCore.Identity;
namespace Core.Startups
{
public class StartupDbInitializer
{
private const string AdminEmail = "admin#admin.com";
private const string AdminPassword = "StrongPasswordAdmin123!";
private static readonly List<IdentityRole> Roles = new List<IdentityRole>()
{
new IdentityRole {Name = "Admin", NormalizedName = "ADMIN", ConcurrencyStamp = Guid.NewGuid().ToString()}
};
public static void SeedData(ApplicationDbContext dbContext, UserManager<User> userManager)
{
dbContext.Database.EnsureCreated();
AddRoles(dbContext);
AddUser(dbContext, userManager);
AddUserRoles(dbContext, userManager);
}
private static void AddRoles(ApplicationDbContext dbContext)
{
if (!dbContext.Roles.Any())
{
foreach (var role in Roles)
{
dbContext.Roles.Add(role);
dbContext.SaveChanges();
}
}
}
private static async void AddUser(ApplicationDbContext dbContext, UserManager<User> userManager)
{
if (!dbContext.Users.Any())
{
var user = new User {
UserName = AdminEmail,
Email = AdminEmail,
IsEnabled = true,
EmailConfirmed = true,
};
await userManager.CreateAsync(user, AdminPassword);
}
}
private static void AddUserRoles(ApplicationDbContext dbContext, UserManager<User> userManager)
{
if (!dbContext.UserRoles.Any())
{
var userRole = new IdentityUserRole<string>
{
UserId = dbContext.Users.Single(r => r.Email == AdminEmail).Id,
RoleId = dbContext.Roles.Single(r => r.Name == "Admin").Id
};
dbContext.UserRoles.Add(userRole);
dbContext.SaveChanges();
}
}
}
}
Then call it in your Startup's Configure method:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ApplicationDbContext dbContext,
UserManager<User> userManager,
)
{
// rest of code...
StartupDbInitializer.SeedData(dbContext, userManager);
}
Above, I inject my DbContext and UserManager<T>.
Try this line... it must work.
ApplicationUser user = await _usermanager.FindByEmailAsync("your.email#mymail.com");
if (!await _usermanager.IsInRoleAsync(user, "Admin"))
{
await _usermanager.AddToRoleAsync(user, "Admin");
}
When you tried it and it works, change it to your config parameters if you prefer them. It's not that hard to get it to work, you have everything you need in UserManager and RoleManager classes.
I still say you have to check if the role exist in table before you insert it, I got all my roles populated every time I run the application before I added the check.
if ((await _roleManager.FindByNameAsync("Admin")) == null)
{
await _roleManager.CreateAsync(new IdentityRole { Name = "Admin" });
}
Related
I am trying to implement Npgsql in our DAL and running into issues under heavy load. the following sample application is a decent representation of just a simple query that under heavy load, throws a 'A command is already in progress' exception. I am assuming this is due to the lack of MARS support so I also tried creating a connection each time with a using statement around each command only to have the performance become unusable. I checked that the username is indexed so that shouldn't be an issue.
Not sure what I am doing wrong here but I need some advice on how to get this performing well.
OS: Docker Container: microsoft/dotnet:2.1.301-sdk
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
namespace npgsqlTest
{
class Program
{
static async Task Main(string[] args)
{
DAL dal = new DAL();
dal.Prepare();
var tasks = dal.Users.Select(async user =>
{
Console.WriteLine(await dal.RunTest(user));
});
await Task.WhenAll(tasks);
}
}
public class DAL
{
private static string _ConnectionString;
private NpgsqlConnection _Connection;
public List<string> Users { get; set; } = new List<string>();
public DAL()
{
_ConnectionString = $"Host=192.168.1.1;Username=admin;Port=5432;Password=password;Database=BigDB;";
_Connection = new NpgsqlConnection(_ConnectionString);
_Connection.Open();
}
public void Prepare()
{
string query = "SELECT username FROM usertable;";
using (var cmd = new NpgsqlCommand(query, _Connection))
{
var reader = cmd.ExecuteReader();
using (reader)
{
while (reader.Read())
{
Users.Add(reader[0].ToString());
}
}
}
}
public async Task<string> RunTest(string user)
{
var parameters = new Dictionary<string, Object> { { "username", user } };
var query = $"SELECT name FROM usertable WHERE username = (#username);";
var reader = await QueryAsync(query, parameters);
using (reader)
{
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
var name = reader["name"];
if (!(hash is DBNull))
return (string)name;
}
}
}
return String.Empty;
}
public async Task<DbDataReader> QueryAsync(string query, Dictionary<string, Object> parameters)
{
using (var cmd = new NpgsqlCommand(query, _Connection))
{
foreach (var parameter in parameters)
{
cmd.Parameters.AddWithValue(parameter.Key, parameter.Value == null ? DBNull.Value : parameter.Value);
}
cmd.Prepare();
return await cmd.ExecuteReaderAsync();
}
}
}
}
I am unsure how to go about searching for the "Code" stored in my Database in order to return the "OriginalUrl".
I know I can search for the ObjectId but I want to be able to search by the "Code" assigned to that ObjectId.
Currently I have a working program that takes a Url as well as a "title" and sends it to the database:
It is assigned an Objectid _id and a randomly generated 12 character "Code":
If it helps this is my Controller class:
namespace ShortenUrls.Controllers
{
[Route("api/codes")]
public class ShortUrlsController : Controller
{
private readonly ShortUrlRepository _repo;
public ShortUrlsController(ShortUrlRepository repo)
{
_repo = repo;
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(string id)
{
var su = await _repo.GetAsync(id);
if (su == null)
return NotFound();
return Ok(su);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] ShortUrl su)
{
await _repo.CreateAsync(su);
return Ok(su);
}
}
And Repository class:
namespace ShortenUrls.Models.Repository
{
public class ShortUrlRepository
{
private const string alphabet = "23456789bcdfghjkmnpqrstvwxyz-_";
private static readonly Random rand = new Random();
private readonly Database _db;
public ShortUrlRepository(Database db)
{
_db = db;
}
private static string GenerateCode()
{
const int codeLength = 12;
var chars = new char[codeLength];
for (var i = 0; i < codeLength; i++)
{
chars[i] = alphabet[rand.Next(0, alphabet.Length)];
}
return new string(chars);
}
public Task<ShortUrl> GetAsync(string id)
{
var objId = ObjectId.Parse(id);
return _db.Urls.Find(x => x.Id == objId).FirstOrDefaultAsync();
}
public Task CreateAsync(ShortUrl su)
{
su.Code = GenerateCode();
return _db.Urls.InsertOneAsync(su);
}
}
Just use a filter. Doing it this way let's you create a query specifically for the "code".
public async Task<ShortUrl> GetAsync(string code)
{
var filterBuilder = new FilterDefinitionBuilder<ShortUrl>();
var filter = filterBuilder.Eq(s => s.Code, code);
var cursor = await _db.Urls.FindAsync(filter);
return await cursor.FirstOrDefaultAsync();
}
Assuming you already know the code when calling this and that ObjectId is created on InsertOneAsync call. First change your repository to take Code as searchable input.
public Task<ShortUrl> GetAsync(string code)
{
return await _db.Urls.FirstOrDefaultAsync(x => x.Code == code);
}
Then change your controller Get to this:
[HttpGet("{code}")]
public async Task<IActionResult> Get(string code)
{
var su = await _repo.GetAsync(code);
if (su == null)
return NotFound();
return Ok(su);
}
In your controller you can access su.OriginalUrl if you need to only return that after getting the object.
Then in postman you can just call http://localhost:51767/api/codes?code=cmg3fjjr_gtv
Remember only Id works for default url parameters as setup by your default routes in Startup.cs.
app.UseMvc(routes => { /*...*/ })
So this wont work: /api/codes/cmg3fjjr_gtv unless you specifically set up routing or change {code} back to {id}. Readability of your code suffers though.
I am working in Asp.net core Application.I want to use role based claims in my application, i have tried following code, but its not working.Its not creating aspnetrolesclaim table in db. Did i do any think wrong?
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
CreateRolesAndAdminUser(app);
}
private async Task CreateRolesAndAdminUser(IApplicationBuilder app)
{
var _roleManager = app.ApplicationServices.GetRequiredService<RoleManager<ApplicationRole>>();
var roleExists = await _roleManager.RoleExistsAsync("Admin");
if (!roleExists)
{
var role = new ApplicationRole()
{
Name = RoleName.SuperUser,
NormalizedName = RoleName.SuperUser.ToUpper(),
};
await _roleManager.CreateAsync(role);
await _roleManager.AddClaimAsync(role, new Claim("User","Create"));
await _roleManager.AddClaimAsync(role, new Claim("User","Edit"));
await _roleManager.AddClaimAsync(role, new Claim("User", "Delete"));
}
string adminUserEmail = "admin#adminstore.com";
string adminPwd = "admin#123";
var _userManager = app.ApplicationServices.GetRequiredService<UserManager<ApplicationUser>>();
var checkAppUser = await _userManager.FindByEmailAsync(adminUserEmail);
if (checkAppUser == null)
{
ApplicationUser newAppUser = new ApplicationUser
{
Email = adminUserEmail,
UserName = adminUserEmail,
NormalizedEmail = adminUserEmail.ToUpper(),
NormalizedUserName = adminUserEmail.ToUpper(),
};
await _userManager.CreateAsync(newAppUser, adminPwd);
await _userManager.AddToRoleAsync(newAppUser,"Admin");
}
}
I'm trying to find a way that would allow me to update a UserProfile entity along with a list of Roles that the user is assigned to. I've written the code below, but it doesn't work.
public void UpdateUserProfile(UserProfile userProfile)
{
context.Entry(userProfile).State = EntityState.Added;
var databaseRoleIds = context.Roles
.Where(r => r.UserProfiles
.Any(u => u.UserId == userProfile.UserId))
.Select(r => r.RoleId).ToList();
var clientRoleIds = userProfile.Roles.Select(r => r.RoleId).ToList();
var removedRoleIds = databaseRoleIds.Except(clientRoleIds).ToList();
var addedRoleIds = clientRoleIds.Except(databaseRoleIds).ToList();
var unchangedRoleIds = removedRoleIds.Union(addedRoleIds).ToList();
foreach (var roleId in unchangedRoleIds)
{
var role = context.Roles.Find(roleId);
context.Entry(role).State = EntityState.Unchanged;
}
foreach (var roleId in removedRoleIds)
{
userProfile.RemoveRole(context.Roles.Find(roleId));
}
foreach (var roleId in addedRoleIds)
{
userProfile.AddRole(context.Roles.Find(roleId));
}
context.Entry(userProfile).State = EntityState.Modified;
}
Here is the unitOfWork
namespace MvcWebsite.WorkUnits
{
public class WorkUnit : IWorkUnit, IDisposable
{
private MvcContext context = new MvcContext();
private RoleRepository roleRepository;
private UserProfileRepository userProfileRepository;
public IRoleRepository RoleRepository
{
get
{
if (this.roleRepository == null)
{
roleRepository = new RoleRepository(context);
}
return roleRepository;
}
}
public IUserProfileRepository UserProfileRepository
{
get
{
if (this.userProfileRepository == null)
{
userProfileRepository = new UserProfileRepository(context);
}
return userProfileRepository;
}
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
... and here is the HttpPost Edit method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(UserProfileEditViewModel model)
{
try
{
if (ModelState.IsValid)
{
var clientUserProfile = new UserProfile();
clientUserProfile.UserId = model.UserId;
clientUserProfile.UserName = model.UserName;
clientUserProfile.FirstName = model.FirstName;
clientUserProfile.LastName = model.LastName;
clientUserProfile.Email = model.Email;
clientUserProfile.RowVersion = model.RowVersion;
clientUserProfile.Roles = new List<Role>();
foreach(var role in model.Roles)
{
if (role.Assigned)
{
clientUserProfile.Roles.Add(new Role
{
RoleId = role.RoleId,
RoleName = role.RoleName,
RowVersion = role.RowVersion,
});
}
}
unitOfWork.UserProfileRepository.UpdateUserProfile(clientUserProfile);
unitOfWork.Save();
return RedirectToAction("Details", new { id = clientUserProfile.UserId });
}
}
catch (DataException ex)
{
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
return View(model);
}
Does anyone have any idea why this isn't working? Or could suggest a tutorial somewhere that actually works. Any help, as always, is greatly appreciated.
Instead of creating a new UserProfile in your controller, get the UserProfile from the repository, modify its fields, then send it back to UpdateUserProfile and call Save.
Finally found that I had it totally wrong in the first place. I wasn't changing the relationships at all. I've included the code below, which allows me to attach the parent entity as modified and then mark the relationships as added and deleted as required
public void UpdateUserProfile(UserProfile userProfile)
{
context.Entry(userProfile).State = EntityState.Modified;
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
foreach (var role in userProfile.Roles)
{
context.Entry(role).State = EntityState.Unchanged;
}
var databaseRoleIds = context.Roles
.Where(r => r.UserProfiles
.Any(u => u.UserId == userProfile.UserId))
.Select(r => r.RoleId)
.ToList();
var clientRoleIds = userProfile.Roles.Select(r => r.RoleId).ToList();
var removedRoleIds = databaseRoleIds.Except(clientRoleIds).ToList();
var addedRoleIds = clientRoleIds.Except(databaseRoleIds).ToList();
foreach (var roleId in removedRoleIds)
{
var role = context.Roles.Find(roleId);
objectContext
.ObjectStateManager
.ChangeRelationshipState(userProfile, role, u => u.Roles, EntityState.Deleted);
}
foreach (var roleId in addedRoleIds)
{
var role = context.Roles.Find(roleId);
objectContext
.ObjectStateManager
.ChangeRelationshipState(userProfile, role, u => u.Roles, EntityState.Added);
}
}
I have created a database in Microsoft sql server express. I need to be able to login on Mvc 2 app, using my database ( not the one existing on AcountController meaning MembershipService )
I just need to replace MemeberAhipService with my database. How can I do that ( i'm using entity framework code first ) . I don't need to create a model in visual studio. I have the usermodel, userContext: Db . I think i need repository also. Can anyone show me an example, of tell me the steps ?
You can create your own MembershipService.
Example:
New MembershipService.cs (or whatever you want)
public class MembershipService
{
public bool IsUserValid(string username, string password)
{
var db = new DatabaseContext();
var user = db.GetUser(username, password);
// Or however you want to get your data, via Context or Repository
return (user != null);
}
}
New FormsClass.cs
public class FormService
{
public void SignIn(string username, List<string> roles)
{
FormsAuthenticationTicket authTicket = new
FormsAuthenticationTicket(1, // Version
username, // Username
DateTime.Now, // Creation
DateTime.Now.AddMinutes(30), // Expiration
false, // Persistent
string.Join(",", roles.ToArray())); // Roles
string encTicket = FormsAuthentication.Encrypt(authTicket);
HttpContext.Current.Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
GenericIdentity id = new GenericIdentity(username);
HttpContext.Current.User = new GenericPrincipal(id, roles.ToArray());
}
}
In Global.asax:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
string encTicket = authCookie.Value;
if (!String.IsNullOrEmpty(encTicket))
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(encTicket);
FormsIdentity id = (FormsIdentity)Context.User.Identity;
var roles = ticket.UserData.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
GenericPrincipal prin = new GenericPrincipal(id, roles);
HttpContext.Current.User = prin;
}
}
}