aspnet identity using guid as key - asp.net-mvc

I am trying to use Guid's instead of strings for my primary key and have followed the following posts: How to change type of id in Microsoft.AspNet.Identity.EntityFramework.IdentityUser and
How to change type of id in Microsoft.AspNet.Identity.EntityFramework.IdentityUser
I updated to the latest prerelease packages of aspnet identity
Microsoft ASP.NET Identity Core 2.0.0-beta1
Microsoft ASP.NET Identity EntityFramework 2.0.0-beta1
and edited my User to allow for Guid's instead of the default string, I then created my own dbContext and usermanager, however every time I try to login I get the following error:
System.Data.SqlClient.SqlException: Operand type clash:
uniqueidentifier is incompatible with int
for this line:
var user = await UserManager.FindAsync(model.UserName,
model.Password);
I have checked to make sure that all the fields in the database are definitely uniqueidentifiers and I'm not sure what to try next, below is the code I am currently using:
User objects:
public class GuidRole : IdentityRole<Guid, GuidUserRole>
{
public GuidRole()
{
Id = Guid.NewGuid();
}
public GuidRole(string name) : this() { Name = name; }
}
public class GuidUserRole : IdentityUserRole<Guid> { }
public class GuidUserClaim : IdentityUserClaim<Guid> { }
public class GuidUserLogin : IdentityUserLogin<Guid> { }
public class User : IdentityUser<Guid, GuidUserLogin, GuidUserRole, GuidUserClaim>
{
public User()
{
Id = Guid.NewGuid();
}
public User(string name) : this() { UserName = name; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
dbContext:
public class newDbContext : IdentityDbContext<User, GuidRole, Guid, GuidUserLogin, GuidUserRole, GuidUserClaim>
{
public newDbContext()
: base(nameOrConnectionString: "defaultConnection") { }
public newDbContext(string connectionString)
: base(nameOrConnectionString: connectionString) { }
static newDbContext()
{
Database.SetInitializer<newDbContext>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Use singular table names
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<User>().ToTable("User").Property(p => p.Id).HasColumnName("UserID");
modelBuilder.Entity<User>().Property(p => p.Email).HasColumnName("EmailAddress");
modelBuilder.Entity<GuidUserRole>().HasKey(r => new { r.RoleId, r.UserId });
modelBuilder.Entity<GuidUserRole>().ToTable("UserRole");
modelBuilder.Entity<GuidUserRole>().Property(r => r.UserId).HasColumnName("UserID");
modelBuilder.Entity<GuidUserRole>().Property(r => r.RoleId).HasColumnName("RoleID");
modelBuilder.Entity<GuidUserLogin>().ToTable("UserLogin");
modelBuilder.Entity<GuidUserLogin>().Property(r => r.UserId).HasColumnName("UserID");
modelBuilder.Entity<GuidUserClaim>().ToTable("UserClaim");
modelBuilder.Entity<GuidUserClaim>().Property(r => r.Id).HasColumnName("UserClaimID");
modelBuilder.Entity<GuidRole>().HasKey<Guid>(r => r.Id);
modelBuilder.Entity<GuidRole>().ToTable("Role");
modelBuilder.Entity<GuidRole>().Property(r => r.Id).HasColumnName("RoleID");
Configuration.ProxyCreationEnabled = false;
Configuration.LazyLoadingEnabled = false;
}
}
and finally the user manager:
public class ApplicationUserManager : UserManager<User, Guid>
{
public ApplicationUserManager(string connectionString)
: base(new UserStore<User, GuidRole, Guid, GuidUserLogin, GuidUserRole, GuidUserClaim>(new newDbContext()))
{
UserValidator = new UserValidator<User, Guid>(this) { AllowOnlyAlphanumericUserNames = false };
}
}

Thanks to Hao Kung's comment I individually went through the table and property mappings until I got to the UserClaims table. Turns out I had the field type set to uniqueidentifier in the database, however this still needed to be an int. Changing it fixed the problem!

Related

The entity type IdentityRole is not part of the model for the current context (MVC Role Manager)

I am pretty new to MVC. I am working role manager. Before this, I changed user id datatype to int from string. Now, I would like to have roles in my website. I have created page and added code to run that part. After running website, When I click on Create New User Role, I get this below error in background.
Here is my code:
private List<SelectListItem> GetAllRolesAsSelectList()
{
List<SelectListItem> SelectRoleListItems =
new List<SelectListItem>();
var roleManager =
new RoleManager<IdentityRole>(
new RoleStore<IdentityRole>(new ApplicationDbContext()));
var colRoleSelectList = roleManager.Roles.OrderBy(x => x.Name).ToList();
SelectRoleListItems.Add(
new SelectListItem
{
Text = "Select",
Value = "0"
});
foreach (var item in colRoleSelectList)
{
SelectRoleListItems.Add(
new SelectListItem
{
Text = item.Name.ToString(),
Value = item.Name.ToString()
});
}
return SelectRoleListItems;
}
This is the line where error is thrown
var roleManager =
new RoleManager<IdentityRole>(
new RoleStore<IdentityRole>(new ApplicationDbContext()));
Here is my IdentityModel page code:
public class CustomUserRole : IdentityUserRole<int>
{
}
public class CustomUserClaim : IdentityUserClaim<int>
{
}
public class CustomUserLogin : IdentityUserLogin<int>
{
}
public class CustomRole : IdentityRole<int, CustomUserRole>, IRole<int>
{
public CustomRole() : base() { }
public CustomRole(string name)
: this()
{
this.Name = name;
}
}
public class CustomUserStore : UserStore<ApplicationUser, CustomRole, int,
CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public CustomUserStore(ApplicationDbContext context)
: base(context)
{
}
}
public class CustomRoleStore : RoleStore<CustomRole, int, CustomUserRole>
{
public CustomRoleStore(ApplicationDbContext context)
: base(context)
{
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole,
int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public ApplicationDbContext()
: base("SMSGoConnection")
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
Help would be appreciated.

Error When Seeding Users - MVC5 EF6 Identity 2.0

We're working on our seed method for our database, and have tried a variety of different things to make it work, but it keeps erroring out.
Our seed method is
protected override void Seed(WebApplication1.Models.ApplicationDbContext context)
{
if (!context.Roles.Any(r => r.Name == "Admin"))
{
var store = new RoleStore<IdentityRole>(context);
var manager = new RoleManager<IdentityRole>(store);
var role = new IdentityRole {Name = "Admin"};
manager.Create(role);
}
if (!context.Users.Any(u => u.UserName == "Admin"))
{
var store = new UserStore<ApplicationUser>(context);
var manager = new UserManager<ApplicationUser>(store);
var user = new ApplicationUser {UserName = "Admin"};
manager.Create(user, "ChangeItAsap!");
manager.AddToRole(user.Id, "Admin");
}
}
The error is
The type 'WebApplication1.Models.ApplicationUser' cannot be used as type parameter 'TUser' in the generic type or method 'Microsoft.AspNet.Identity.EntityFramework.UserStore<TUser>'. There is no implicit reference conversion from 'WebApplication1.Models.ApplicationUser' to 'Microsoft.AspNet.Identity.EntityFramework.IdentityUser'.
With the following line being the cause
var store = new UserStore<ApplicationUser>(context);
I'm not entirely sure what could be causing this, as I've never ran into this issue in the past. any help would be appreciated.
EDIT
Here is the definition
public class ApplicationUserLogin : IdentityUserLogin<string> { }
public class ApplicationUserClaim : IdentityUserClaim<string> { }
public class ApplicationUserRole : IdentityUserRole<string> { }
// Must be expressed in terms of our custom Role and other types:
public class ApplicationUser
: IdentityUser<string, ApplicationUserLogin,
ApplicationUserRole, ApplicationUserClaim>
{
public ApplicationUser()
{
this.Id = Guid.NewGuid().ToString();
// Add any custom User properties/code here
}
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(ApplicationUserManager manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
public ApplicationRole()
{
this.Id = Guid.NewGuid().ToString();
}
public ApplicationRole(string name)
: this()
{
this.Name = name;
}
// Add any custom Role properties/code here
}
public class ApplicationDbContext
: IdentityDbContext<ApplicationUser, ApplicationRole,
string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
// Most likely won't need to customize these either, but they were needed because we implemented
// custom versions of all the other types:
public class ApplicationUserStore
: UserStore<ApplicationUser, ApplicationRole, string,
ApplicationUserLogin, ApplicationUserRole,
ApplicationUserClaim>, IUserStore<ApplicationUser, string>,
IDisposable
{
public ApplicationUserStore()
: this(new IdentityDbContext())
{
base.DisposeContext = true;
}
public ApplicationUserStore(DbContext context)
: base(context)
{
}
}
public class ApplicationRoleStore
: RoleStore<ApplicationRole, string, ApplicationUserRole>,
IQueryableRoleStore<ApplicationRole, string>,
IRoleStore<ApplicationRole, string>, IDisposable
{
public ApplicationRoleStore()
: base(new IdentityDbContext())
{
base.DisposeContext = true;
}
public ApplicationRoleStore(DbContext context)
: base(context)
{
}
}

AutoMapper+xUnit: Missing type map configuration or unsupported mapping

I cannot figure this one out. I have a N-Tier ASP.MVC application and I am writing my first Unit Test and it seems to fail on my AutoMapper configuration. I have used AutoMapper a million times and never had any problems using it.
I'm sure I am missing something simple, but I have been staring at this for 24 hours now.
Class Library: APP.DOMAIN
public class User : IEntity<int>
{
public int Id { get; set; }
[StringLength(20), Required]
public string UserName { get; set; }
}
Class Library: APP.SERVICE
References App.Domain
public class UserViewModel
{
public int Id { get; set; }
public string UserName { get; set; }
}
I have my AutoMapper bootstrapper in the service layer.
public static class AutoMapperBootstrapper
{
public static void RegisterMappings()
{
Mapper.CreateMap<User, UserViewModel>();
}
}
UserService.cs
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public List<UserViewModel> GetUsers()
{
var users = _userRepository.GetAll();
if (users == null)
{
throw new Exception("No users found.");
}
return Mapper.Map<List<UserViewModel>>(users); // FAILS ON AUTOMAPPER
}
}
ASP.MVC Layer: APP.WEB
References App.Service
private void Application_Start(object sender, EventArgs e)
{
// Register AutoMapper
AutoMapperBootstrapper.RegisterMappings();
Mapper.AssertConfigurationIsValid();
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
Unit Test Layer:
public class TestUserRepository :IUserRepository
{
public IEnumerable<User> GetAll()
{
var users = new List<User>()
{
new User { Id = 1, UserName = "Mary"},
new User { Id = 2, UserName = "Joe"}
};
return users;
}
}
public class UserServiceTest
{
private IUserService _userService;
private readonly IUserRepository _userRepository;
public UserServiceTest()
{
_userRepository = new TestUserRepository();
}
[Fact]
public void GetUsers_Should_Return_Correct_Number_Of_Users()
{
// Arrange
_userService = new UserService(_userRepository);
// Act
var result = _userService.GetUsers(); // FAILS ON AUTOMAPPER
// Assert
Assert.True(result.Any(u => u.UserName == "Mary"));
}
}
Failing Test Message:
*** Failures ***
Exception
AutoMapper.AutoMapperMappingException: AutoMapper.AutoMapperMappingException : Missing type map configuration or unsupported mapping.
Mapping types:
User -> UserViewModel
App.Data.Model.User -> App.Service.ViewModels.UserViewModel
Destination path:
List`1[0]
Source value:
App.Data.Model.User
at App.Service.Services.UserService.GetUsers() in D:\Repositories\App\App.Service\Services\UserService.cs:line 36
at App.Tests.Service.Tests.UserServiceTest.GetUsers_Should_Return_Correct_Number_Of_Users() in D:\Repositories\App\App.Tests\Service.Tests\UserServiceTest.cs:line 34
A little late to the party but have you tried setting the mapping before running the test?
public class UserServiceTest
{
public UserServiceTest()
{
// register the mappings before running the test
AutoMapperBootstrapper.RegisterMappings();
}
...
}
What we would need to do is Inject Custom Mapper Mock as given below. Add all those custom profiles that you have used for that particular class that you are unit testing and inject ConfigureMapper() in the Constructor of that class which is expecting IMapper Object
public IMapper ConfigureMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<CustomProfile>();
cfg.AddProfile<UserCustomProfile>();
cfg.AddProfile<UserWorkProfile>();
});
return config.CreateMapper();
}
Hope this solves the issue.
I'm not sure what the problem is, it's been a while since I've last used AutoMapper, but I'm quite sure that the following will work:
return users.Select(Mapper.Map<UserViewModel>);
I have a problem with this line:
var authorDTO = mapper.Map<AuthorCreationDTO>(AuthorinsideDB);
So I change the version of Autormapper
from:
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
to
Version="6.0.0"
and it worked.

Intranet Application - Data Access in separate project

I have an MVC intranet application which uses EF 6. I have setup the DataAccess project in a separate class library which has EF 6 referenced. I have an entity which implements an interface:
public interface IAuditable
{
DateTime CreatedDateTime { get; set; }
string CreatedBy { get; set; }
}
public class Collection : IAuditable
{
// Properties
}
However, in the SaveChanges method I obviously don't have access to HttpContext.Current.User.Identity.Name as it is in a separate class library, so I was wondering how one would set this in SaveChanges?
public override int SaveChanges()
{
var addedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added);
foreach (var dbEntityEntry in addedEntries)
{
var entity = dbEntityEntry.Entity as IAuditable;
if (entity != null)
{
entity.CreatedDateTime = DateTime.Now;
// how do I set entity.CreatedBy = HttpContext.Current.User.Identity.Name?
}
}
return base.SaveChanges();
}
Edit
Following on from #CodeCaster solution, I have the following:
[BreezeController]
public class BreezeController : ApiController
{
private readonly BTNIntranetRepository _repository;
public BreezeController(BTNIntranetRepository repository)
{
_repository = repository;
_repository.LoggedInUser = HttpContext.Current.User.Identity.Name;
}
// Methods
}
But HttpContext.Current.User is null
This can be solved in many ways.
You're not really showing relevant code, but you can for example give the library class you expose a public string LoggedInUser (or ActingUser or give it a name) property which you set when instantiating it:
public class SomeController : Controller
{
private IDataSource _dataSource;
public SomeController(IDataSource dataSource)
{
_dataSource = dataSource;
_dataSource.LoggedInUser = HttpContext.Current.User.Identity.Name
}
}
You can then simply use that property in your IDataSource.SaveChanges() method:
public override int SaveChanges()
{
// ...
entity.CreatedBy = this.LoggedInUser;
}

Entity framework savechanges error

I have a wizard step in which a user fills in fields. I then use json to save the values into my database for each wizard step.
However, in my repository I have my savechanges(). But it wont save the changes, instead it throws an error:
Entities in 'NKImodeledmxContainer.SelectedQuestion' participate in the 'QuestionSelectedQuestion' relationship. 0 related 'Question' were found. 1 'Question' is expected.
Anyone know how to get rid of the error? Do I have to get the ID from Question and save it aswell to my database or can I change something in EF so the error message is not getting thrown?
This is my post in my controller:
[HttpPost]
public JsonResult AnswerForm(int id, SelectedQuestionViewModel model)
{
bool result = false;
var goalCardQuestionAnswer = new GoalCardQuestionAnswer();
goalCardQuestionAnswer.SelectedQuestion = new SelectedQuestion();
goalCardQuestionAnswer.SelectedQuestion.Id = model.QuestionID;
goalCardQuestionAnswer.Comment = model.Comment;
goalCardQuestionAnswer.Grade = model.Grade;
if (goalCardQuestionAnswer.Grade != null)
{
answerNKIRepository.SaveQuestionAnswer(goalCardQuestionAnswer);
answerNKIRepository.Save();
result = true;
return Json(result);
}
answerNKIRepository.SaveQuestionAnswer(goalCardQuestionAnswer);
answerNKIRepository.Save();
return Json(result);
}
My Repository
public class AnswerNKIRepository
{
private readonly NKImodeledmxContainer db = new NKImodeledmxContainer();
public List<SelectedQuestion> GetAllSelectedQuestionsByGoalCardId(int goalCardId)
{
return db.SelectedQuestion.Where(question => question.GoalCard.Id == goalCardId).ToList();
}
public void SaveQuestionAnswer(GoalCardQuestionAnswer goalCardQuestionAnswer)
{
db.GoalCardQuestionAnswer.AddObject(goalCardQuestionAnswer);
}
public void Save()
{
db.SaveChanges();
}
}
This is my ViewModel:
public class SelectedQuestionViewModel
{
public int? Grade { get; set; }
public string Comment { get; set; }
public string SelectedQuestionText { get; set; }
public int QuestionID { get; set; }
}
This is my database model:
The exception complains that SelectedQuestion.Question is a required navigation property but you don't set this property in your code. Try to load the question by Id from the repository and set it to the SelectedQuestion.Question reference: Replace this line ...
goalCardQuestionAnswer.SelectedQuestion.Id = model.QuestionID;
...by...
goalCardQuestionAnswer.SelectedQuestion.Question =
answerNKIRepository.GetQuestionById(model.QuestionID);
And in your repository add the method:
public Question GetQuestionById(int id)
{
return db.Question.Single(q => q.Id == id);
}

Resources