I have recently extended my AspNetRoles table like this:
public class AspNetRoles:IdentityRole
{
public AspNetRoles() : base() { }
public String Label { get; set; }
public String ApplicationId { get; set; }
public AspNetApplications Application { get; set; }
public static readonly String SystemAdministrator = "SystemAdministrator";
}
It works fine when I create a new role. However, when I try to extract it to a list like this:
var data = dbContext.Roles.ToList();
And try to do a search like this:
data = data.Where(u => u.Id.ToString().ToLower().Contains(Input.Search.ToLower())).ToList();
I can't access the ApplicationId column. Am I missing something?
EDIT:
My dbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, AspNetRoles<string>, string>
{
public virtual DbSet<AspNetUsersExtendedDetails> AspNetUsersExtendedDetails { get; set; }
public virtual DbSet<AspNetApplications> AspNetApplications { get; set; }
public virtual DbSet<AspNetEventLogs> AspNetEventLogs { get; set; }
public virtual DbSet<AspNetRoles> AspNetRoles { get; set; }
public ApplicationDbContext() : base("AppStudio")
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
I have updated my dbContext but now it shows this error: 'IdentityDbContext<ApplicationUser, AspNetRoles<string>, string>' does not contain a constructor that takes 2 arguments
You need to tell ASP.Net Identity about the custom role table that you want to use.
Edit: since the default IdentityRole implementation uses a string as the PK, the type can be omitted. Just checking futher on ASP.Net Identity version 2, as soon as you specify a custom IdentityRole class, the class declaration needs to include all types.
That means you need to declare your ApplictionDbContext like this:
public class ApplicationDbContext: IdentityDbContext<ApplicationUser, AspNetRoles, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public ApplicationDbContext() : base("AppStudio")
{
//note: before this change, if you included the
//throwIfV1Schema parameter in the constructor,
//it needs to be removed.
}
//implementation
}
Note that this assumes that the Primary Key of the users table is a string. If this is not the case, substitute with the applicable type (e.g. a Guid).
You have to add AspNetRoles to your IdentityDbContext.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, AspNetRoles, string>
{
public virtual DbSet<AspNetUsersExtendedDetails> AspNetUsersExtendedDetails { get; set; }
public virtual DbSet<AspNetApplications> AspNetApplications { get; set; }
public virtual DbSet<AspNetEventLogs> AspNetEventLogs { get; set; }
public ApplicationDbContext() : base("AppStudio", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Related
I want to require that all entities of a particular type have a corresponding user in my ASP.NET MVC app, and that this is enforced at the database level i.e. as a non-nullable field. However, when I set the Required attribute on the IdentityUser property in my model class, like so:
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
namespace Test.Models
{
public class Foo
{
public int Id { get; set; }
[Required]
public IdentityUser User { get; set; }
}
}
the corresponding migration that gets generated sets the UserId table field to nullable:
UserId = table.Column<string>(type: "TEXT", nullable: true)
I've read that Table Per Hierarchy can cause this, but I'm not using any kind of inheritance.
What am I missing? Is there a way to achieve what I want?
Try to fix your class
public class Foo
{
[Key]
public int Id { get; set; }
[Required]
public string UserId { get; set; }
[ForeignKey(nameof(UserId))]
public IdentityUser User { get; set; }
}
Net 5 automatically creates a shadow property UserId in order you could save your Foo class. Since you used [Required], EF automatically added that it is nullable. If this property was not able to be null (for example if it was int type ( not int? ! )) you would not need a [Required] attribute.
Also what you can do is to use Fluent API to configure your contraints.
public class Foo
{
public int Id { get; set; }
public string UserId { get; set; }
public IdentityUser User { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public DbSet<Foo> Foos { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Foo>()
.HasRequired(c => c.User)
.WithMany(d => d.Foos)
.HasForeignKey(c => c.UserId);
}
}
My dbContext return null, when I want get list of user in Index View. This list are from my database AspNetUsers table, which has been generate by identity. I can get other my database table list.
There is my ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<ProductCategory> ProductCategories { get; set; }
public DbSet<ProductBrand> ProductBrands { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Address> Address { get; set; }
public DbSet<Recipe> Recipes { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Order_detail> Order_Details { get; set; }
}
There is my UserController
[Area("Admin")]
public class UserController : Controller
{
UserManager<IdentityUser> _userManager;
private ApplicationDbContext _db;
public UserController(UserManager<IdentityUser> userManager, ApplicationDbContext db)
{
_userManager = userManager;
_db = db;
}
public IActionResult Index()
{
return View(_db.ApplicationUsers.ToList());
}
}
There is my ApplicationUser.Model, which inherit IdendityUser
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public string Surname { get; set; }
public ICollection<Recipe> Products { get; set; }
public ICollection<Order> Orders { get; set; }
}
I don't know how you register ApplicationDbContext and Identity framework on your ASP.NET Core MVC application because you didn't show them on the question.
There are couple problems in your code.
First, if you have a custom IdentityUser, like the ApplicationUser you have, you would have to use the generic version of IdentityDbContext:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
...
}
You would need to use the matching generic version of IdentityDbContext if you have any of following:
Custom IdentityUser
Custom IdentityRole
Custom primary key
All 7 classes, user and role, plus IdentityUserRole, IdentityUserClaim, IdentityRoleClaim, IdentityUserLogin, and IdentityUserToken
After you register your custom class with IdentityDbContext, you don't need to put the class as one of the DbSet<> there:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
...
public DbSet<ProductCategory> ProductCategories { get; set; }
public DbSet<ProductBrand> ProductBrands { get; set; }
public DbSet<Product> Products { get; set; }
// public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Address> Address { get; set; }
public DbSet<Recipe> Recipes { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Order_detail> Order_Details { get; set; }
}
You would also need to use the generic version of AddIdentity<TUser>, AddDefaultIdentity<TUser>, or AddIdentityCore<TUser> in your Startup.cs, depending on what you need:
AddDefaultIdentity = AddIdentity + AddDefaultTokens + AddDefaultUI
You didn't specify what version of ASP.NET Core Identity you're using so I don't exactly know which one you use, but the following is how I registered it:
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.User.RequireUniqueEmail = ...;
...
})
.AddEntityFrameworkStores<ApplicationDbContext>();
I have all 7 classes customized as well as change the primary key from string to Guid.
Lastly, to use the dependency injected UserManager and SignInManager, you would need to correct generic version of them as well:
[Area("Admin")]
public class UserController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public UserController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public IActionResult Index()
{
// Get the user list
var users = _userManager.Users.ToList();
// Build your view model to define what your UI only needs, not just passing
// everything to it
var vm = new UserManagementListViewModel
{
Users = users.Select(x => new UserViewModel
{
UserId = x.Id,
UserSurname = x.Surname,
ProductCount = x.Products.Count(),
OrderCount = x.Orders.Count()
})
};
return View(vm);
}
}
When I try to register a user or create a migration I get the following error:
"A key cannot be configured on 'ApplicationUser' because it is a
derived type. The key must be configured on the root type
'IdentityUser'. If you did not intend for 'IdentityUser' to be
included in the model, ensure that it is not included in a DbSet
property on your context, referenced in a configuration call to
ModelBuilder, or referenced from a navigation property on a type that
is included in the model."
I have a BaseEntity that everything derives from like so:
public class BaseEntity
{
public int Id { get; set; }
[Required]
public DateTime DateCreated { get; set; }
[Required]
public DateTime DateModified { get; set; }
[Required]
public string CreatedById { get; set; }
[Required]
public string ModifiedById { get; set; }
public virtual IdentityUser CreatedBy { get; set; }
public virtual IdentityUser ModifiedBy { get; set; }
}
public class FilePath : BaseEntity, IAuditable
{
[StringLength(255)]
public string FileName { get; set; }
public FileType FileType { get; set; }
}
Is there a new rule or update that says you can't use IdentityUser as a navigation property? Google hasn't brought much useful information.
The entire solution is here
if required.
Update: After upgrading to 2.0.1 preview, the errors are a little more helpful:
The best match for foreign key properties {'Id' : string} are
incompatible with the principal key {'Id' : int}.
I faced the same issue. You need to replace all places in code where you use BaseEntity to FilePath.
Example:
I had inheritance like:
public class ApplicationUser : IdentityUser
{
}
and I changed ApplicationDbContext like:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
but I got the same Exception.
After I replaced IdentityUser to ApplicationUser in code the Error has gone. I reckon you should work with BaseEntity through FilePath in Data Acces Layer.
Re. "The best match for foreign key properties {'Id' : string}..."
Your ApplicationUser class doesn't specify the type of its Id field. In this case it defaults to string. What you could do -- though this would cause you to rebuild your db so perhaps too late now -- is to explicitly type the ApplicationUser:
public class ApplicationUser: IdentityUser<int>
{
// myUser.Id is now an int
}
IdentityUser docs
Is there a way to create a custom User and Role without specifying the TKey string in IdentityUser, IdentityRole, and IdentityDbContext? I ask because it seems to think I don't want the auto-generated primary key Id anymore and I absolutely do. Doing what I've done below, UserManager.Create(user, password) will fail with an EntityValidationError on Id.
public class ApplicationUser : IdentityUser<string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
[Required]
[StringLength(50)]
public string FirstName { get; set; }
[Required]
[StringLength(50)]
public string LastName { get; set; }
}
public class ApplicationUserLogin : IdentityUserLogin
{
}
public class ApplicationUserClaim : IdentityUserClaim
{
}
public class ApplicationUserRole : IdentityUserRole
{
}
public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
[Required]
[StringLength(50)]
public string ProperName { get; set; }
[Required]
public string Description { get; set; }
}
public class MyAppDb : IdentityDbContext<ApplicationUser, ApplicationRole, string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
public MyAppDb()
: base("MyAppDb")
{
}
public static MyAppDb Create()
{
return new MyAppDb();
}
}
I'm still learning this new ASP.NET Identity system but have you tried omitting generic types like this:
public class ApplicationUser : IdentityUser
{
[Required]
[StringLength(50)]
public string FirstName { get; set; }
[Required]
[StringLength(50)]
public string LastName { get; set; }
}
same goes for ApplicationRole
MyAppDb would look like this:
public class MyAppDb: IdentityDbContext<ApplicationUser>
By default the IDs will be strings with auto-generated GUIDs in the DB
It appears the answer is "NO". If you specify the TKey on User and/or Role the primary keys are no longer created for you.
It seems I was trying to over-complicate things. Thanks #dima for helping me decide to un-complicate things. Here is what I did to successfully get users and roles (both with custom properties) to successfully work, i.e., to successfully create records in the database via a controller and view:
UPDATE: You may want to look at the link I provided at the bottom for a better/simpler solution.
public class ApplicationUser : IdentityUser
{
[Required]
[StringLength(50)]
public string FirstName { get; set; }
[Required]
[StringLength(50)]
public string LastName { get; set; }
}
//public class ApplicationUserLogin : IdentityUserLogin
//{
//}
//public class ApplicationUserClaim : IdentityUserClaim
//{
//}
//public class ApplicationUserRole : IdentityUserRole
//{
//}
public class ApplicationRole : IdentityRole
{
[Required]
[StringLength(50)]
public string ProperName { get; set; }
}
public class MyAppDb : IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public MyAppDb()
: base("MyAppDb")
{
}
}
However, a new issue has arose with the UserManager. Specifically, I'm getting the error The entity type IdentityRole is not part of the model for the current context. on this line of code var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
UPDATE: Fixed this error here: Why am I getting an IdentityRole error?
I'm trying to implement new ASP.NET Identity in my old project. I have an existing table called tda_Contacts in the database. The following code works fine without table attribute and creates all new identity related tables plus TdaContacts table. But when put existing table name in the table attribute ([Table("tda_Contacts")]), then it does nothing and throws Invalid object name 'dbo.UserSecrets' exeption. Also if I put different name in that attribute it works fine and creates a correct table with exactly the same columns and types as existing tda_Contacts.
What am I doing wrong? How to force it to use my existing table?
public class IdentityUser : User
{
public int ContactID { get; set; }
[ForeignKey("ContactID ")]
public virtual TdaContact TdaContact { get; set; }
}
public class CustomUserContext : IdentityStoreContext
{
public CustomUserContext(DbContext db) : base(db)
{
Users = new UserStore<IdentityUser>(db);
}
}
public class MyDbContext : IdentityDbContext<IdentityUser, UserClaim, UserSecret, UserLogin, Role, UserRole>
{
public MyDbContext() : base("Name=MyConnectionString")
{
}
public IDbSet<TdaContact> TdaContacts { get; set; }
}
[Table("tda_Contacts")]
public class TdaContact
{
[Key]
public int ContactID { get; set; }
[Required]
[MaxLength(50)]
public string FirstName { get; set; }
[Required]
[MaxLength(50)]
public string LastName { get; set; }
[MaxLength(255)]
public string Email { get; set; }
}
P.S. Just discovered that with precreated IdentityUsers table with correct foreign key to tda_Contacts it works as expected.