How to create transaction with asp.net identity? - c#

I am doing registration on which i am asking for 5 things:
FullName,EmailId,Password,ContactNumber,Gender
Now emailid and password i am storing with register method and given in below two link:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
using var context = new MyEntities())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
var DataModel = new UserMaster();
DataModel.Gender = model.Gender.ToString();
DataModel.Name = string.Empty;
var result = await UserManager.CreateAsync(user, model.Password);//Doing entry in AspnetUser even if transaction fails
if (result.Succeeded)
{
await this.UserManager.AddToRoleAsync(user.Id, model.Role.ToString());
this.AddUser(DataModel, context);
transaction.Commit();
return View("DisplayEmail");
}
AddErrors(result);
}
catch (Exception ex)
{
transaction.Rollback();
return null;
}
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
public int AddUser(UserMaster _addUser, MyEntities _context)
{
_context.UserMaster.Add(_addUser);
_context.SaveChanges();
return 0;
}
Now with this below 2 lines:
var result = await UserManager.CreateAsync(user, model.Password);//entry is done in AspnetUsers table.
await this.UserManager.AddToRoleAsync(user.Id, model.Role.ToString());//entry is done is Aspnetuserrole table
Now this Fullname,contactno,gender i am having in another table that is UserMaster.
So when i will submit my registration form i will save this details in UserMaster and AspnetUsers,AspnetUserinrole table.
But consider if there any problem occurs while saving record in UserMaster then i dont want to save entry in Aspnetuser and Aspnetuserinrole too.
I would like to create a transaction where i would rollback if any problem occurs during saving any record in any table i.e no entry should be done in AspnetUser,AspnetUserinRole nd userMaster.
Records should be saved successfully only if there is no problem in saving record in this 3 tables otherwise whiole transaction should be role back.
I am using Microsoft.AspNet.Identity for login,Register,role management and other and following this tutorial:
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-web-app-with-email-confirmation-and-password-reset
http://www.asp.net/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity
But as await UserManager.CreateAsync and UserManager.AddToRoleAsync method are built in method how would i synchonize it to work with entity framework.
So can anybody guide me how to create such transaction or anything that would solve this?
IdentityConfig:
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this application.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}

You should not create a new db context, but use the existing one.
var context = Request.GetOwinContext().Get<MyEntities>()
It is created per request if you use default implementation.
app.CreatePerOwinContext(ApplicationDbContext.Create);
Update:
OK, since you are using two different contexts your code will look something like this:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var appDbContext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
using( var context = new MyEntities())
using (var transaction = appDbContext.Database.BeginTransaction())
{
try
{
var DataModel = new UserMaster();
DataModel.Gender = model.Gender.ToString();
DataModel.Name = string.Empty;
// Doing entry in AspnetUser even if transaction fails
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await this.UserManager.AddToRoleAsync(user.Id, model.Role.ToString());
this.AddUser(DataModel, context);
transaction.Commit();
return View("DisplayEmail");
}
AddErrors(result);
}
catch (Exception ex)
{
transaction.Rollback();
return null;
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
public int AddUser(UserMaster _addUser, MyEntities _context)
{
_context.UserMaster.Add(_addUser);
_context.SaveChanges();
return 0;
}
Here, appDbContext is the same context that is used by UserManager.

You can solve it with TransactionScope class:
using (TransactionScope scope = new TransactionScope())
{
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await this.UserManager.AddToRoleAsync(user.Id, model.Role.ToString());
string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id, "Confirm your account");
return View("DisplayEmail");
}
scope.Complete();
}
So, both actions will be done in one transaction and if method Comlete does not call, both actions will be canceled (roolback).
If you want to solve it with EF only (without TransactionScope), you need to refactor your code. I don't know implementation of class UserManager and methods CreateAsync and AddToRoleAsync, but I guess that they creates new DBContext for each operation. So, first of all, for all transactional operations you need one DBContext (for EF solution). If you add this methods, I'll modify my answer according to EF solution.

Backs alternative works for me, when i use this method: TransactionScope(TransactionScopeAsyncFlowOption.Enabled)
source: https://learn.microsoft.com/en-us/ef/ef6/saving/transactions

Related

.NET Core 2.0 AddToRoleAsync - User name is invalid, can only contain letters or digits

Im working with .NET Core 2.0 MVC and Entity Framework with Individual User Accounts. By default, usernames are the same as email address. I used the following in Startup.cs to create the roles
private async Task CreateRoles(IServiceProvider serviceProvider)
{
//adding custom roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
string[] roleNames = { "Admin", "User" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
//creating the roles and seeding them to the database
var roleExist = await RoleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
//creating a super user who could maintain the web app
var poweruser = new ApplicationUser
{
UserName = Configuration.GetSection("UserSettings")["UserEmail"],
Email = Configuration.GetSection("UserSettings")["UserEmail"]
};
string UserPassword = Configuration.GetSection("UserSettings")["UserPassword"];
var _user = await UserManager.FindByEmailAsync(Configuration.GetSection("UserSettings")["UserEmail"]);
if (_user == null)
{
var createPowerUser = await UserManager.CreateAsync(poweruser, UserPassword);
if (createPowerUser.Succeeded)
{
//here we tie the new user to the "Admin" role
await UserManager.AddToRoleAsync(poweruser, "Admin");
}
}
}
And called it from the Configure method in Startup.cs. The roles added just fine, and the role was added to the admin.
However when I try to add a role to a user programmatically using the method await _userManager.AddToRoleAsync(applicationUser, "Admin"); in my ApplicationUsersController I get the error
User name is invalid, can only contain letters or digits
The .NET Core 2.0 documentation here https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity-configuration?tabs=aspnetcore2x indicates that the "#" and/or "." that may be causing the error are included as valid characters by default in AllowedUserNameCharacters. I'm at a loss of other things to try.
Here is my ApplicationUsersController code:
[Authorize(Roles = "Admin")]
public class ApplicationUsersController : Controller
{
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
public ApplicationUsersController(UserManager<ApplicationUser> userManager, ApplicationDbContext context)
{
_userManager = userManager;
_context = context;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(string id, [Bind("Id,Name,Email,IsAdmin,ConcurrencyStamp,SecurityStamp")] ApplicationUser applicationUser)
{
if (id != applicationUser.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
applicationUser.DateUpdated = DateTime.Now;
applicationUser.NormalizedEmail = applicationUser.Email.ToUpper();
_context.Update(applicationUser);
await _context.SaveChangesAsync();
if (applicationUser.IsAdmin)
{
var x = await _userManager.AddToRoleAsync(applicationUser, "Admin");
if (!x.Succeeded)
{
string s = "";
}
}
else
{
await _userManager.AddToRoleAsync(applicationUser, "User");
}
}
catch (DbUpdateConcurrencyException)
{
if (!ApplicationUserExists(applicationUser.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(applicationUser);
}

UpdateAsync method is not work in web api?

I am create small demo for user registration using web api mvc c#.i am register Succeeded for user using 'Register' in web api.now i want to using this method also edit this user by id so how can do that i don't know any one know please let me know. i my code i will manage add/edit call in one method so i will first check the condition on id is null then add and id is not null then go for the edit but how can edit this record and role.
here this my method :
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
try
{
if (model.Id == "")
{
//here is for add user method
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email,PhoneNumber = model.PhoneNumber };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var UsersContext = new ApplicationDbContext();
var res = UsersContext.Users.Where(x => x.UserName == user.UserName).FirstOrDefault();
var UserId = res.Id;
await UserManager.AddToRoleAsync(UserId, model.UserRole);
return Ok(result);
}
return Ok(result);
}
else
{
//here i want write edit code
var UsersContext = new ApplicationDbContext();
var Team = UsersContext.Users.Find(model.Id);
Team.UserName = model.UserName;
Team.Email = model.Email;
Team.PhoneNumber = model.PhoneNumber;
IdentityResult result = await UserManager.UpdateAsync(Team); //here getting error.
return Ok(result);
}
return Ok("Done");
}
catch (Exception ex)
{
}
return Ok();
}
UPDATED:
First check whether you are passing values to all required field or not.
Keep this UserStore after your controller class calling braces {.
[Authorize]
public class AccountController : Controller
{
UserStore<IdentityUser> myUserStore = new UserStore<IdentityUser>(new
ApplicationDbContext());
//rest code
Then inside your Register() Post method, do like this bellow,
if (model.Id == "")
{
//Add new user code
}
else
{
var context = myUserStore.Context as ApplicationDbContext;
var Team = context.Users.Find(model.Id);
//change the field value what ever you want to update
context.Users.Attach(Team);
context.Entry(Team).State = EntityState.Modified;
context.Configuration.ValidateOnSaveEnabled = false;
await context.SaveChangesAsync();
return Ok("Updated");
}
Hope it helps :)

ASP.NET Identity: Additional properties to ApplicationUserRole

I have added an additional property to ApplicationUserRole as follows:
public class ApplicationUserRole : IdentityUserRole<int>
{
public string RoleAssigner { get; set; }
}
Now i am to assign a role to an user as follows:
[HttpPost]
public ActionResult Create(UserRoleViewModel userRoleViewModel)
{
if (ModelState.IsValid)
{
using (var context = new ApplicationDbContext())
{
var userRole = new ApplicationUserRole
{
UserId = userRoleViewModel.UserId,
RoleId = userRoleViewModel.RoleId,
RoleAssigner = userRoleViewModel.RoleAssigner
};
context.ApplicationUserRoles.Add(userRole);
context.SaveChanges();
return RedirectToAction("Index");
}
}
return View(userRoleViewModel);
}
This is working fine!!
Before adding additional "RoleAssigner" Property, I can assign a role to an user using AddToRoles() Method as follows:
[HttpPost]
public ActionResult Create(UserRoleViewModel userRoleViewModel)
{
if (ModelState.IsValid)
{
UserManager.AddToRoles(userRoleViewModel.Id, userRoleViewModel.RoleName);
return RedirectToAction("Index");
}
return View(userRoleViewModel);
}
My question is: After adding an additional Property like "RoleAssigner", Is there any way to assign a role to an user using AddToRoles() method which will also insert the additional "RoleAssigner" value for "RoleAssigner" column in the database also.
Edit with working example:
I think you can do that by creating an extension method at IdentityConfig.
I did something similar to find user by username or phone number
In what I'm able to understand you want to call UserManager.AddToRoles(...) and
fill the new role property.
to do that( in similar to the example before ) you need an extension to user manager. you do it like this:
public static class UserManagerExtens
{
public static IdentityResult AddToRole(this ApplicationUserManager userManager,string userId,string[] roles,string assigner)
{
try
{
ApplicationUserRole role = null;
using (ApplicationDbContext context = new ApplicationDbContext())
{
foreach (var item in roles)
{
role = new ApplicationUserRole();
role.UserId = userId;
role.RoleAssigner = assigner;
role.RoleId = item;
context.AspNetUserRoles.Add(role);
}
context.SaveChanges();
}
return new IdentityResult() { };
}
catch (Exception ex)
{
return new IdentityResult(ex.Message);
}
}
}
That is a working example, using UserManager you can call it with definded
parameters like:
string[] roles = new string[] { /*your roles here*/ };
UserManager.AddToRole(/*UserIdHere*/, roles, /*assigerId here*/);
Similar to this you can implement async or other methods of UserManager.
If you are using asp.net core application in startup.cs you should inject proper store models
services.AddIdentity<ApplicationUser, YOURROLEMODEL(ApplicationUserRole )>()
If you are using asp.net application there should be IdentityConfig.cs file You should implement your UserStore which will get you RoleModel as generic. You can see I have created AppUserStore class and it gets MyIdentityRole model as a generic Type. And changed ApplicationUserManager to use my AppUserStore class.
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new AppUserStore(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
public class AppUserStore :
UserStore<ApplicationUser, MyIdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUserStore<ApplicationUser>
{
public AppUserStore(DbContext context) : base(context)
{
}
}
public class MyIdentityRole : IdentityRole
{
public string MyProperty { get; set; }
}

UserManager.FindAsync returns null after password reset

Following the official documentation (https://github.com/rustd/AspnetIdentitySample) and NuGet package, I'm having issues with logging in after a password reset for my MVC5 application. It seems as though Entity Framework doesn't refresh its context in the process, it's only after I restart my application that I can login with the correct credentials.
As far as I can work out, I've done everything that the code samples have done as well. Only I have much more code and settings (e.g. Unity).
This is the problem area:
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
try
{
if (ModelState.IsValid)
{
ApplicationUser user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await this.SignInAsync(user, false);
return RedirectToLocal(returnUrl);
}
else
{
model.State = ViewModelState.Error;
model.Messages = new List<string>() { "No access buddy!" };
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
catch (Exception ex)
{
throw;
}
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
This part works perfectly when I log on for the first time. However, after I have reset my password, logging in with the new credentials isn't possible (it still takes the old version).
Here is my configuration:
public class ApplicationUserManager : UserManager<ApplicationUser>
{
#region Constructor
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
this.UserTokenProvider = new TotpSecurityStampBasedTokenProvider<ApplicationUser, string>();
}
#endregion Constructor
#region Methods
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
ApplicationUserManager manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<SecurityDbContext>()));
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
#endregion Methods
}
This is what I've configured during Startup:
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(SecurityDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions { });
Ultimately, after a few screens, here is where the user ultimately ends up to create a new password:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
ApplicationUser user = await UserManager.FindByEmailAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
IdentityResult result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
else
{
AddErrors(result);
return View();
}
}
No errors here either, it stores the new hashed value and security stamp in the database. I'm thinking of some caching, cookies or dbContext that isn't refreshed at the time the password is reset.
Does anyone have any ideas?
Ok so I have finally found the reason for this odd behavior. I had the following DbConfiguration:
public class Configuration : DbConfiguration
{
public Configuration()
{
CacheTransactionHandler transactionHandler = new CacheTransactionHandler(new InMemoryCache());
this.AddInterceptor(transactionHandler);
Loaded += (sender, args) =>
{
args.ReplaceService<DbProviderServices>((s, _) => new CachingProviderServices(s, transactionHandler));
};
}
}
Commenting out the callback did the trick, which sounds logical as I replaced the standard DbProviderServices with second-level caching (as provided by https://efcache.codeplex.com/)
Update:
It's not necessary to entirely remove the second-level caching. Instead, by adding a caching provider, I can choose which tables to cache (and for how long). Here is the updated code:
public class Configuration : DbConfiguration
{
public Configuration()
{
CacheTransactionHandler transactionHandler = new CacheTransactionHandler(new InMemoryCache());
this.AddInterceptor(transactionHandler);
MyCachingPolicy cachingPolicy = new MyCachingPolicy();
Loaded += (sender, args) =>
{
args.ReplaceService<DbProviderServices>((s, _) => new CachingProviderServices(s, transactionHandler, cachingPolicy));
};
}
}
internal class MyCachingPolicy : CachingPolicy
{
#region Constructor
internal MyCachingPolicy()
{
this.NonCachableTables = new List<string>()
{
"AspNetUsers",
"Resource",
"Task",
"Appointment"
};
}
#endregion Constructor
#region Properties
private List<string> NonCachableTables { get; set; }
#endregion Properties
#region Methods
#endregion Methods
protected override bool CanBeCached(ReadOnlyCollection<EntitySetBase> affectedEntitySets, string sql, IEnumerable<KeyValuePair<string, object>> parameters)
{
return !affectedEntitySets.Select(e => e.Table ?? e.Name).Any(tableName => this.NonCachableTables.Contains(tableName));
}
protected override void GetCacheableRows(ReadOnlyCollection<EntitySetBase> affectedEntitySets, out int minCacheableRows, out int maxCacheableRows)
{
base.GetCacheableRows(affectedEntitySets, out minCacheableRows, out maxCacheableRows);
}
protected override void GetExpirationTimeout(ReadOnlyCollection<EntitySetBase> affectedEntitySets, out TimeSpan slidingExpiration, out DateTimeOffset absoluteExpiration)
{
base.GetExpirationTimeout(affectedEntitySets, out slidingExpiration, out absoluteExpiration);
}
}

Creating Roles in Asp.net Identity MVC 5

There is very little documentation about using the new Asp.net Identity Security Framework.
I have pieced together what I could to try and create a new Role and add a User to it. I tried the following: Add role in ASP.NET Identity
which looks like it may have gotten the info from this blog: building a simple to-do application with asp.net identity and associating users with to-does
I have added the code to a Database Initializer that is run whenever the model changes. It fails on the RoleExists function with the following error:
System.InvalidOperationException occurred in mscorlib.dll
The entity type IdentityRole is not part of the model for the current context.
protected override void Seed (MyContext context)
{
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
// Create Admin Role
string roleName = "Admins";
IdentityResult roleResult;
// Check to see if Role Exists, if not create it
if (!RoleManager.RoleExists(roleName))
{
roleResult = RoleManager.Create(new IdentityRole(roleName));
}
}
Any help is appreciated.
Here we go:
var roleManager = new RoleManager<Microsoft.AspNet.Identity.EntityFramework.IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
if(!roleManager.RoleExists("ROLE NAME"))
{
var role = new Microsoft.AspNet.Identity.EntityFramework.IdentityRole();
role.Name = "ROLE NAME";
roleManager.Create(role);
}
Verify you have following signature of your MyContext class
public class MyContext : IdentityDbContext<MyUser>
Or
public class MyContext : IdentityDbContext
The code is working for me, without any modification!!!
Here is the complete article describing how to create roles, modify roles, delete roles and manage roles using ASP.NET Identity. This also contains the User interface, controller methods, etc.
http://www.dotnetfunda.com/articles/show/2898/working-with-roles-in-aspnet-identity-for-mvc
In ASP.NET 5 rc1-final, I did following:
Created ApplicationRoleManager (in similar manner as there is ApplicationUser created by template)
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(
IRoleStore<IdentityRole> store,
IEnumerable<IRoleValidator<IdentityRole>> roleValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
ILogger<RoleManager<IdentityRole>> logger,
IHttpContextAccessor contextAccessor)
: base(store, roleValidators, keyNormalizer, errors, logger, contextAccessor)
{
}
}
To ConfigureServices in Startup.cs, I added it as RoleManager
services.
.AddIdentity<ApplicationUser, IdentityRole>()
.AddRoleManager<ApplicationRoleManager>();
For creating new Roles, call from Configure following:
public static class RoleHelper
{
private static async Task EnsureRoleCreated(RoleManager<IdentityRole> roleManager, string roleName)
{
if (!await roleManager.RoleExistsAsync(roleName))
{
await roleManager.CreateAsync(new IdentityRole(roleName));
}
}
public static async Task EnsureRolesCreated(this RoleManager<IdentityRole> roleManager)
{
// add all roles, that should be in database, here
await EnsureRoleCreated(roleManager, "Developer");
}
}
public async void Configure(..., RoleManager<IdentityRole> roleManager, ...)
{
...
await roleManager.EnsureRolesCreated();
...
}
Now, the rules can be assigned to user
await _userManager.AddToRoleAsync(await _userManager.FindByIdAsync(User.GetUserId()), "Developer");
Or used in Authorize attribute
[Authorize(Roles = "Developer")]
public class DeveloperController : Controller
{
}
As an improvement on Peters code above you can use this:
var roleManager = new RoleManager<Microsoft.AspNet.Identity.EntityFramework.IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
if (!roleManager.RoleExists("Member"))
roleManager.Create(new IdentityRole("Member"));
My application was hanging on startup when I used Peter Stulinski & Dave Gordon's code samples with EF 6.0. I changed:
var roleManager = new RoleManager<Microsoft.AspNet.Identity.EntityFramework.IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
to
var roleManager = new RoleManager<Microsoft.AspNet.Identity.EntityFramework.IdentityRole>(new RoleStore<IdentityRole>(**context**));
Which makes sense when in the seed method you don't want instantiate another instance of the ApplicationDBContext. This might have been compounded by the fact that I had Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer()); in the constructor of ApplicationDbContext
Roles View Model
public class RoleViewModel
{
public string Id { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "RoleName")]
public string Name { get; set; }
}
Controller method
[HttpPost]
public async Task<ActionResult> Create(RoleViewModel roleViewModel)
{
if (ModelState.IsValid)
{
var role = new IdentityRole(roleViewModel.Name);
var roleresult = await RoleManager.CreateAsync(role);
if (!roleresult.Succeeded)
{
ModelState.AddModelError("", roleresult.Errors.First());
return View();
}
return RedirectToAction("some_action");
}
return View();
}
I wanted to share another solution for adding roles:
<h2>Create Role</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<span class="label label-primary">Role name:</span>
<p>
#Html.TextBox("RoleName", null, new { #class = "form-control input-lg" })
</p>
<input type="submit" value="Save" class="btn btn-primary" />
}
Controller:
[HttpGet]
public ActionResult AdminView()
{
return View();
}
[HttpPost]
public ActionResult AdminView(FormCollection collection)
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
if (roleManager.RoleExists(collection["RoleName"]) == false)
{
Guid guid = Guid.NewGuid();
roleManager.Create(new IdentityRole() { Id = guid.ToString(), Name = collection["RoleName"] });
}
return View();
}
If you are using the default template that is created when you select a new ASP.net Web application and selected Individual User accounts as Authentication and trying to create users with Roles so here is the solution. In the Account Controller's Register method which is called using [HttpPost], add the following lines in if condition.
using Microsoft.AspNet.Identity.EntityFramework;
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var roleStore = new RoleStore<IdentityRole>(new ApplicationDbContext());
var roleManager = new RoleManager<IdentityRole>(roleStore);
if(!await roleManager.RoleExistsAsync("YourRoleName"))
await roleManager.CreateAsync(new IdentityRole("YourRoleName"));
await UserManager.AddToRoleAsync(user.Id, "YourRoleName");
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
return RedirectToAction("Index", "Home");
}
This will create first create a role in your database and then add the newly created user to this role.
public static void createUserRole(string roleName)
{
if (!System.Web.Security.Roles.RoleExists(roleName))
{
System.Web.Security.Roles.CreateRole(roleName);
}
}
the method i Use for creating roles is below, assigning them to users in code is also listed. the below code does be in "configuration.cs" in the migrations folder.
string [] roleNames = { "role1", "role2", "role3" };
var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
IdentityResult roleResult;
foreach(var roleName in roleNames)
{
if(!RoleManager.RoleExists(roleName))
{
roleResult = RoleManager.Create(new IdentityRole(roleName));
}
}
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
UserManager.AddToRole("user", "role1");
UserManager.AddToRole("user", "role2");
context.SaveChanges();

Categories