环境:
1.VS2015 Community 14.0.25431.01 Update 3;
2.其他环境(具体哪一个影响不太清楚,都列在这儿)
使用的系统模板
利用系统提供的模板,并选择个人身份验证。如图:
问题:
模板提供的身份认证数据库中的AspNetUsers表,需要根据需要增加列。以下图为例,绿色框中的列都是模板默认的,我要增加一列(以Test为例)。
解决过程:
刚刚接触MVC、EF的概念,不知道如何操作。查阅了大量资料后发现,网上大部分类似内容都是基于mvc3的,最后还是在stackoverflow上找到一篇(中英文附后)。
但是一步一步按图索骥做下来,在AspNetUsers表中就是加不上Test列,其中还遇到一些错误,例如:几个地方显示使用的方法错误。
最后发现,该例子是基于asp.net 5/6的,许多需要添加的内容模板中已经有了,考虑是不是asp.net core有一些变化。
于是,直接将Test属性直接添加到ApplicationUser中去,发现问题解决了。
原来与之前考虑的一样,asp.net core已经将所需的所有内容都已经考虑好了,不需要做其他的修改。还有,这个版本与之前5/6版本几个方法发生了变化,所以出现了找不到方法的错误(已经标注到附录例子中了)。
我的具体代码如下:(Models/ApplicationUser.cs)
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; namespace Test.Models { // Add profile data for application users by adding properties to the ApplicationUser class public class ApplicationUser : IdentityUser { public string Test { get; set; }//之前该行是空白 } }
附:stackoverflow的例子
Accssing ASP.NET 5 / MVC6 Identity Custom Profile data properties
I have made a sample web app named ShoppingList using the asp.net 5 web application template (Mvc6/MVC core/Asp.net-5). I wanted to extend the user profile with a custom field name DefaultListId.
我做了一个简单的网页应用,该应用使用asp.net 5 网络应用模板(Mvc6/MVC Core/Asp.net 5)。我想扩展用户配置文件,增加客户名称字段(DefaultListID)。
The ApplicationUser.cs:
namespace ShoppingList.Models { // Add profile data for application users by adding properties to the ApplicationUser class public class ApplicationUser : IdentityUser { public int DefaultListId { get; set; } } }
In the home controller I would like to access the data stored for this property. I tried:
在home控制器中,我想使用该属性,我做了如下尝试:
namespace ShoppingList.Controllers { public class HomeController : Controller { private UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())); public IActionResult Index() { var userId = User.GetUserId(); ApplicationUser user = userManager.FindById(userId); ViewBag.UserId = userId; ViewBag.DefaultListId = user.DefaultListId; return View(); } //other actions omitted for brevity
However I get the following errors:
但是,我得到错误信息如下:
Severity Code Description Project File Line Suppression State Error CS7036 There is no argument given that corresponds to the required formal parameter ‘optionsAccessor‘ of ‘UserManager.UserManager(IUserStore, IOptions, IPasswordHasher, IEnumerable>, IEnumerable>, ILookupNormalizer, IdentityErrorDescriber, IServiceProvider, ILogger>, IHttpContextAccessor)‘ ShoppingList.DNX 4.5.1, ShoppingList.DNX Core 5.0 C:\Users\OleKristian\Documents\Programmering\ShoppingList\src\ShoppingList\Controllers\HomeController.cs 15 Active
And...
还有...
Severity Code Description Project File Line Suppression State Error CS1061 ‘UserManager‘ does not contain a definition for ‘FindById‘ and no extension method ‘FindById‘ accepting a first argument of type ‘UserManager‘ could be found (are you missing a using directive or an assembly reference?) ShoppingList.DNX 4.5.1, ShoppingList.DNX Core 5.0 C:\Users\OleKristian\Documents\Programmering\ShoppingList\src\ShoppingList\Controllers\HomeController.cs 20 Active
回答:
You should not instantiate your own UserManager
, like ever. And it’s actually pretty hard to do so, since it requires you to pass in a lot of arguments to the constructor (and most of those things are also very difficult to set up properly).
你不应该像以往一样创建你自己的UserManager。它真的很难做到,因为它需要你通过很多参数的构造函数(很多这些里面的事情要正确设置是很困难的)。
ASP.NET Core makes extensive use of dependency injection, so you should just set up your controller in a way that it receives the user manager automatically. That way, you don’t have to worry about creating the user manager:
ASP.NET Core大量使用依赖注入,所以你应该仅通过自动接收user manager来设置控制器。这样,你不必操心创建的user manager:
public class HomeController : Controller { private readonly UserManager<ApplicationUser> userManager; public HomeController (UserManager<ApplicationUser> userManager) { this.userManager = userManager; } // … }
However, in order to do that, you first need to set up ASP.NET Identity to actually know about your ApplicationUser
and make it be used for storing your user identities. To do that, you need to modify the Startup
class. In the ConfigureServices
method, you need to change the AddIdentity
call to make it reference your actual types:
但为实现这个目的,你首先需要设置ASP.NET Identity,要其知道你的ApplicationUser,再让其可储存你的用户认可信息。要做到这一点,需要修改Startup类。在ConfigureServices方法中,需要修改AddIdentity,以便让它引用所需的实际类型。
services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders();
IdentityRole
here refers to the standard role type used by ASP.NET Identity (since you don’t need a custom one). As you can see, we also reference a ApplicationDbContext
there which is the entity framework database context for your modified identity model; so we need to set up that one too. In your case, it could just look like this:
这里的IdentityRole指的是ASP.NET Identity使用的标准角色类型(因此你不需要进行自定义)。正如你看到的,我们在这里也引用了ApplicationDbContext,它是为你修改过的identity model而准备的数据库上下文;所以我们也需要进行设置。在你当前的情况下,看起来如下所示:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // here you could adjust the mapping } }
This will make sure that the ApplicationUser
entity is actually stored properly in the database. We’re almost done, but we now just need to tell Entity Framework about this database context as well. So again in the ConfigureServices
method of your Startup
class, make sure to adjust the AddEntityFramework
call to also set up the ApplicationDbContext
database context. If you have other database contexts, you can just chain these:
这将确保ApplicationUser
实体妥善保存在数据库中。我们几乎完成了,但是现在我们也只需要告诉EF这个数据库的上下文。所以再次在Startup类的Configureservices
方法中,确保调整AddEntityFramework
也设置了Applicationdbcontext
数据库上下文。如果你有其他数据库上下文,你可以反复这样做:
services.AddEntityFramework()//在Asp.Net Core中到不到这个方法,但是可以找到AddEntityFrameWorkAddSqlServer()方法,可能新版本将第一行和第二行的方法合并了 .AddSqlServer() .AddDbContext<IdentityContext>(opts => opts.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"])) .AddDbContext<DataContext>(opts => opts.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));//也找不到DataContext类
And that’s it! Now Entity Framework knows about the new user entity and properly maps it to the database (including your new property), and ASP.NET Identity also knows about your user model and will use that one for everything it does, and you can have the UserManager
injected into controllers (or services, or whatever) to do stuff.
这样,EF就知道了新user实体及其映射到数据库的属性(包括你自定义的新属性);并且ASP.NET也知道了你自定义的user model,并据其做后面所有的事情;你也可将UserManager注入到控制器中(或者服务或其他所有事情)来处理事务了。
You should not instantiate your own UserManager
, like ever. And it’s actually pretty hard to do so, since it requires you to pass in a lot of arguments to the constructor (and most of those things are also very difficult to set up properly).
不应该再像以前那样实例化UserManager,并且实际上那样做相当困难,因为这样做需要给构造器传递许多参数(并且许多配置要设置恰当是非常困难的)。
ASP.NET Core makes extensive use of dependency injection, so you should just set up your controller in a way that it receives the user manager automatically. That way, you don’t have to worry about creating the user manager:
ASP.NET Core广泛应用了注入技术,所以你应该仅给控制器设置为自动接收user manager。这样做,你就不必考虑创建user manager了。
public class HomeController : Controller { private readonly UserManager<ApplicationUser> userManager; public HomeController (UserManager<ApplicationUser> userManager) { this.userManager = userManager; } // … }
However, in order to do that, you first need to set up ASP.NET Identity to actually know about your ApplicationUser
and make it be used for storing your user identities. To do that, you need to modify the Startup
class. In the ConfigureServices
method, you need to change the AddIdentity
call to make it reference your actual types:
然而,为了这样做,你首先需要设置ASP.NET Identity,让它知道你的ApplicationUser,并且让他用于存储你的用户身份认证信息。为了实现这个目的,你需要修改Startup类。在ConfigureServices方法中,你需要修改AddIdentity,让它引用你以及的类型:
services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders();
IdentityRole
here refers to the standard role type used by ASP.NET Identity (since you don’t need a custom one). As you can see, we also reference a ApplicationDbContext
there which is the entity framework database context for your modified identity model; so we need to set up that one too. In your case, it could just look like this:
这里的IdentityRole指的是ASP.NET Identity标准的角色类型(因为你不需要自定义的角色类型)。这里,我们也引用了ApplicationDbContext,这是用于你修改过的identity模型的数据库上下文;所以,我们也需要做如此设置。针对你的情况,应该是这样:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // here you could adjust the mapping } }
This will make sure that the ApplicationUser
entity is actually stored properly in the database. We’re almost done, but we now just need to tell Entity Framework about this database context as well. So again in the ConfigureServices
method of your Startup
class, make sure to adjust the AddEntityFramework
call to also set up the ApplicationDbContext
database context. If you have other database contexts, you can just chain these:
这将确保ApplicationUser实体被适当的储存到数据库中。我们几乎完成了,但现在我们仅需要告诉EF这个数据库上下文。所以再一次在Startup类的ConfigureServices方法中,确保调整AddEntityFrameWork,设置为ApplicationDbContext。如果你还有另外的数据库上下文,你可以反复这样做:
services.AddEntityFramework() .AddSqlServer() .AddDbContext<IdentityContext>(opts => opts.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"])) .AddDbContext<DataContext>(opts => opts.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
And that’s it! Now Entity Framework knows about the new user entity and properly maps it to the database (including your new property), and ASP.NET Identity also knows about your user model and will use that one for everything it does, and you can have the UserManager
injected into controllers (or services, or whatever) to do stuff.
这样就完成了。现在EF就知道了新的user实体及其映射到数据库的属性(包含你设置的新属性);同时ASP.NET Identity也知道了你的user模型,接着就会按照该模型做他该做的事情了,这样你就能将UserManager注入到控制器中工作了。
As for your second error, you get this because the user manager does not have a
FindById
method; it only as a FindByIdAsync
method. You will see this actually in a lot of places with ASP.NET Core that there are only asynchronous methods, so embrace it and start making your method asynchronous as well.
关于第二个错误,原因是user manager没有FindById方法,而是仅仅有FindByIdAsync方法。你将在很多使用ASP.NET Core的地方看到,仅有异步方法,所以拥抱并开始使用异步方法吧。
In your case, you would need to change the Index
method like this:
在你的情况下,你需要像这样变更Index方法:
// method is async and returns a Task public async Task<IActionResult> Index() { var userId = User.GetUserId(); // call `FindByIdAsync` and await the result ApplicationUser user = await userManager.FindByIdAsync(userId); ViewBag.UserId = userId; ViewBag.DefaultListId = user.DefaultListId; return View(); }
As you can see, it does not require many changes to make the method asynchronous. Most of it stays just the same.
正如你看到的,使用异步方法不需要太多的变化,很多都是一样的。