ASP.NET Identity: Additional properties to ApplicationUserRole - c#

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; }
}

Related

Identity EmailService not firing when attempting to send confirmation email

I've been modifying my implementation of ASP.Net Identity in my WebForms application. My modifications have caused my EmailService's SendAsync function to not fire and i'm not sure why. The only thing i can think of is how i am instantiating the UserManager on the register page. Before i was doing var manager = Context.GetOwinContext().GetUserManager(); and now i'm doing var manager = new DecisionLogicIdentity.ApplicationUserManager(userStore);. I am setting the EmailService in the Create function of the UserManager (manager.EmailService = new EmailService();). The SendGrid implementation was working prior to my change to how i call the UserManager. Does anyone have any idea what i am missing here?
Register.aspx.cs:
protected void CreateUser_Click(object sender, EventArgs e)
{
var context = HttpContext.Current.GetOwinContext().Get<DecisionLogicIdentity.ApplicationDbContext>();
var userStore = new DecisionLogicIdentity.UserStore<DecisionLogicIdentity.ApplicationUser>(context)
{
CompanyId = Int32.Parse(CompanyId.Text)
};
var manager = new DecisionLogicIdentity.ApplicationUserManager(userStore);
var signinManager = new DecisionLogicIdentity.ApplicationSignInManager(manager, HttpContext.Current.GetOwinContext().Authentication);
var provider = new DpapiDataProtectionProvider("SampleAppName");
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, int>(provider.Create("SampleTokenName"));
var user = new DecisionLogicIdentity.ApplicationUser()
{
CompanyId = Int32.Parse(CompanyId.Text),
UserName = Email.Text,
Email = Email.Text,
IsExpired = false,
IsDeleted = false
};
IdentityResult result = manager.Create(user, Password.Text);
if (result.Succeeded)
{
user = userStore.FindByEmailAsync(user.Email).GetAwaiter().GetResult();
string code = manager.GenerateEmailConfirmationToken(user.Id);
string callbackUrl = IdentityHelper.GetUserConfirmationRedirectUrl(code, user.Id, Request);
manager.SendEmail(user.Id, "Confirm your account", "Please confirm your account by clicking here.");
//signinManager.SignIn(user, isPersistent: false, rememberBrowser: false);
//signinManager.PasswordSignIn(Email.Text, Password.Text, true, shouldLockout: true);
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
}
else
{
ErrorMessage.Text = result.Errors.FirstOrDefault();
}
}
EmailService:
public class EmailService : IIdentityMessageService
{
public async Task SendAsync(IdentityMessage message)
{
await configSendGridasync(message);
}
// Use NuGet to install SendGrid (Basic C# client lib)
private async Task configSendGridasync(IdentityMessage message)
{
SendGridClient client = new SendGridClient(ConfigurationManager.AppSettings["SendGrid--APIKey"].ToString());
var msg = MailHelper.CreateSingleEmail(new SendGrid.Helpers.Mail.EmailAddress("someemail#somedomain.com"),
new SendGrid.Helpers.Mail.EmailAddress(message.Destination),
message.Subject,
message.Body,
message.Body);
msg.Attachments = null;
await client.SendEmailAsync(msg);
}
}
ApplicationUserManager:
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)//, IIdentityMessageService emailService)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(
new DecisionLogicIdentity.UserStore<ApplicationUser>(
context.Get<ApplicationDbContext>() as DatabaseContext));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser, int>(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.FromSeconds(Int32.Parse(ConfigurationManager.AppSettings["UserLockoutMinutes"].ToString()));
manager.MaxFailedAccessAttemptsBeforeLockout = Int32.Parse(ConfigurationManager.AppSettings["UserMaxLoginAttempts"].ToString());
// 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, int>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser, int>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
var provider = new DpapiDataProtectionProvider("SampleAppName");
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, int>(provider.Create("SampleTokenName"));
}
return manager;
}
}
In case anyone needs the answer to this, I was able to get this functioning by modifying my UserManager like so:
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
public ApplicationUserManager(IUserStore<ApplicationUser, int> store, IIdentityMessageService emailService)
: base(store)
{
this.EmailService = emailService;
}
...
And when instantiating the UserManager:
var manager = new ApplicationUserManager(userStore, new EmailService());

How to create transaction with asp.net identity?

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

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

EF6 Code First Migration Seed Method not able to create users

I have implemented Identity 2.1 with custom classes to make all of the entities to have integer keys.
I have the following seed method in my configuration.cs file in the migrations folder
protected override void Seed(ApplicationDbContext context)
{
var roleManager = new ApplicationRoleManager(new ApplicationRoleStore(context));
var userManager = new ApplicationUserManager(new ApplicationUserStore(context));
if (!roleManager.RoleExists("User"))
{
roleManager.Create(new ApplicationRole("User"));
}
if (!roleManager.RoleExists("Supervisor"))
{
roleManager.Create(new ApplicationRole("Supervisor"));
}
if (!roleManager.RoleExists("Administrator"))
{
roleManager.Create(new ApplicationRole("Administrator"));
}
if (!roleManager.RoleExists("Owner"))
{
roleManager.Create(new ApplicationRole("Owner"));
}
if (!roleManager.RoleExists("System User"))
{
roleManager.Create(new ApplicationRole("System User"));
}
if (userManager.FindByName("h2euser") == null)
{
var h2Euser = new ApplicationUser
{
UserName = "h2euser",
Email = "test"
};
var result = userManager.Create(h2Euser, "Letme1n");
if (result.Succeeded)
{
userManager.AddToRole(h2Euser.Id, "System User");
}
}
if (userManager.FindByName("owner") == null)
{
var ownerUser = new ApplicationUser
{
UserName = "owner",
Email = "test2"
};
var result = userManager.Create(ownerUser, "ChangeMe!");
if (result.Succeeded)
{
userManager.AddToRole(ownerUser.Id, "Owner");
}
}
}
When running the Update-Database method I get this error:
System.Data.Entity.Core.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. ---> MySql.Data.MySqlClient.MySqlException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROM ((SELECT CASE WHEN (`Extent2`.`UserId` IS NULL) THEN (NULL) ELSE (1) END' at line 33
My UserStore:
public class ApplicationUserStore
: UserStore<ApplicationUser, ApplicationRole, int,
ApplicationUserLogin, ApplicationUserRole,
ApplicationUserClaim>
{
public ApplicationUserStore()
: this(new IdentityDbContext())
{
base.DisposeContext = true;
}
public ApplicationUserStore(DbContext context)
: base(context)
{
}
}
And my user manager:
public class ApplicationUserManager : UserManager<ApplicationUser, int>
{
// *** ADD INT TYPE ARGUMENT TO CONSTRUCTOR CALL:
public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
// *** PASS CUSTOM APPLICATION USER STORE AS CONSTRUCTOR ARGUMENT:
var manager = new ApplicationUserManager(
new ApplicationUserStore(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
manager.UserValidator = new UserValidator<ApplicationUser, int>(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 in here.
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
manager.RegisterTwoFactorProvider("PhoneCode",
new PhoneNumberTokenProvider<ApplicationUser, int>
{
MessageFormat = "Your security code is: {0}"
});
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
manager.RegisterTwoFactorProvider("EmailCode",
new EmailTokenProvider<ApplicationUser, int>
{
Subject = "SecurityCode",
BodyFormat = "Your security code is {0}"
});
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
// *** ADD INT TYPE ARGUMENT TO METHOD CALL:
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser, int>(
dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}

Identity 2.0 default project missing default classes

According to several articles; I'll note one:
http://typecastexception.com/post/2014/04/20/ASPNET-MVC-and-Identity-20-Understanding-the-Basics.aspx#Application-User-Manager-and-Application-Role-Manager
, there should be two helper classes in IdentityConfig.cs
ApplicationUserManager
ApplicationRoleManager
I've been googling for a while now and can't seem to figure out why it is I do not have ApplicationRoleManager by default in a stock MVC 5 web app. I also am missing methods in ApplicationUserManager such as AddUserToRolesAsync and RemoveUserFromRolesAsync. The following code is all I have in my IdentityConfig.cs file.
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,
};
// 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 in here.
manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is: {0}"
});
manager.RegisterTwoFactorProvider("EmailCode", 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 EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your email service here to send an email.
return Task.FromResult(0);
}
}
public class SmsService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your sms service here to send a text message.
return Task.FromResult(0);
}
}

Categories