環境:
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.
正如你看到的,使用異步方法不需要太多的變化,很多都是一樣的。