ASP.NET MVC 5 - Identity. 如何获取当前的ApplicationUser。

257
我在我的项目中有一个文章实体,其中有名为"Author"的 "ApplicationUser" 属性。如何获取当前登录 "ApplicationUser" 的完整对象?在创建新文章时,我必须将 Article 中的 Author 属性设置为当前的 ApplicationUser
在旧的 Membership 机制中,这很简单,但在新的 Identity 方法中,我不知道该怎么做。我尝试以下方式:
1. 添加使用Identity扩展的语句: using Microsoft.AspNet.Identity; 2. 然后尝试获取当前用户: ApplicationUser currentUser = db.Users.FirstOrDefault(x => x.Id == User.Identity.GetUserId()); 但我得到了以下异常:'System.String GetUserId(System.Security.Principal.IIdentity)' 方法无法转换为存储表达式。
来源=EntityFramework
11个回答

473

您不应该直接查询当前ApplicationUser的数据库。

这会引入额外的依赖关系,需要一个额外的上下文开始,但随着用户数据库表的更改(在过去的两年中有三次),API保持一致。例如,Identity Framework中的users表现在被称为AspNetUsers,并且几个主键字段的名称不断更改,因此几个答案中的代码将不能直接使用。

另一个问题是基础OWIN访问数据库将使用单独的上下文,因此来自单独SQL访问的更改可能会产生无效结果(例如,未看到对数据库进行的更改)。因此,解决方案是与提供的API一起工作,而不是尝试绕过它。

访问ASP.Net身份验证中当前用户对象的正确方法(截至此日期)是:

var user = UserManager.FindById(User.Identity.GetUserId());

或者,如果你有一个异步操作,类似于:

var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

FindById需要使用以下using语句才能使非异步的UserManager方法可用(它们是UserManager的扩展方法,如果不包括这个,你只会看到FindByIdAsync):

using Microsoft.AspNet.Identity;

如果您根本不在控制器中(例如,您正在使用IOC注入),则从以下位置完整检索用户ID:
System.Web.HttpContext.Current.User.Identity.GetUserId();

如果你不在标准的Account控制器中,你需要将以下内容(例如)添加到你的控制器中:

1. 添加这两个属性:

    /// <summary>
    /// Application DB context
    /// </summary>
    protected ApplicationDbContext ApplicationDbContext { get; set; }

    /// <summary>
    /// User manager - attached to application DB context
    /// </summary>
    protected UserManager<ApplicationUser> UserManager { get; set; }

2. 在控制器的构造函数中添加以下内容:

    this.ApplicationDbContext = new ApplicationDbContext();
    this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.ApplicationDbContext));

2015年3月更新

注意:最近一次的Identity框架更新改变了用于身份验证的底层类之一。现在,您可以从当前HttpContent的Owin上下文中访问它。

ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());

附加说明:

在使用EF和Identity Framework连接Azure远程数据库时(例如本地主机测试到Azure数据库),您可能会随机遇到可怕的“错误:19 - 物理连接不可用”。由于原因位于Identity Framework中,您无法添加重试(或者看起来像是缺少.Include(x->someTable)),因此需要在项目中实现自定义的SqlAzureExecutionStrategy


7
谢谢,我后来意识到这是一个扩展方法。需要使用“Microsoft.AspNet.Identity”进行添加。再次感谢。 - Sentinel
3
无法找到类型或命名空间UserStore。我添加了using Microsft.AspNet.Identity。 - Wasfa
3
@Zapnologica: 那听起来像是一个新问题(建议您发布它)。您可以扩展ApplicationUser类(应用程序特定),并与AspNetUsers表并行使用,它们将提供任何新的字段。再次强调:不要直接连接数据库! :) - iCollect.it Ltd
3
FindById 返回的 ApplicationUser 是您自己的类,包含您自己的额外属性。请尝试一下。 - iCollect.it Ltd
2
期待您的新解决方案 :P - Anup Sharma
显示剩余24条评论

65

我的错误,我不应该在LINQ查询中使用一个方法。

正确的代码:

using Microsoft.AspNet.Identity;


string currentUserId = User.Identity.GetUserId();
ApplicationUser currentUser = db.Users.FirstOrDefault(x => x.Id == currentUserId);

3
User.Identiy.GetUserId对我来说不存在,这是自定义方法吗?我只能获取到User.Identity。 - Gerrie Pretorius
10
没关系……你需要添加 "using Microsoft.AspNet.Identity;" 后,才能使用那个方法。 - Gerrie Pretorius
4
请注意,User对象仅在控制器中可见。 - Miro J.
9
你应该使用 UserManager 方法而不是直接操作数据库,这样更加规范。 - iCollect.it Ltd
3
@Josh Bjelovuk:当API可用时,永远不要直接访问数据库。这会引入一个新的依赖关系,需要额外的上下文开始,但是用户数据库表在过去两年中更改了3次,而API保持一致。 - iCollect.it Ltd
显示剩余3条评论

33

答案在评论中,但没有人将其作为实际解决方案发布。

你只需要在顶部添加一个using语句:

using Microsoft.AspNet.Identity;

2
我因为那个异常而来,但是我用那个“using”解决了它。看到有15k人访问了这个问题,我觉得这是一个有帮助的答案 :) - rtpHarry
2
@TrueBlueAussie,尽管这不是对OP问题的直接回答,但我认为提到使用是一个非常有用的补充。 - StuartQ
2
为了清晰起见,这是因为.GetUserId()是一个扩展方法。 - FSCKur

11

Ellbar的代码可用!你只需要添加以下内容:

1 - using Microsoft.AspNet.Identity;

而且...Ellbar的代码:

2 - string currentUserId = User.Identity.GetUserId(); ApplicationUser currentUser = db.Users.FirstOrDefault(x => x.Id == currentUserId);

使用这个代码(在currentUser中),你可以处理已连接用户的一般数据,如果你需要额外的数据...请参考此链接


5
可能可以“行得通”,但绕过提供的API直接访问数据库肯定是不被推荐的。如果你使用API,你就不需要额外的工作来获取额外的数据,因为这些数据已经在ApplicationUser对象中了。 - iCollect.it Ltd
我同意!但是,我采取这种方式是因为我已经有一个系统在今天运行,并且需要一个简单的解决方案来解决这个问题!当然,在早期系统中,我会将对象放入它们适当的类和标识中。 - Diego Borges

6
从ASP.NET Identity 3.0.0开始,这已经被重构为:
//returns the userid claim value if present, otherwise returns null
User.GetUserId();

6
ApplicationDbContext context = new ApplicationDbContext();
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
ApplicationUser currentUser = UserManager.FindById(User.Identity.GetUserId());

string ID = currentUser.Id;
string Email = currentUser.Email;
string Username = currentUser.UserName;

3

现在,asp.mvc项目模板会创建一个账户控制器,通过以下方式获取用户管理器:

HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>()

以下内容对我有效:
ApplicationUser user = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(User.Identity.GetUserId());

3

对于MVC 5,只需查看WebApplication模板中ManageController的EnableTwoFactorAuthentication方法即可,在那里进行操作:

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> EnableTwoFactorAuthentication()
        {
            await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
            var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
            if (user != null)
            {
                await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
            }
            return RedirectToAction("Index", "Manage");
        }

答案就在这里,正如微软本身所建议的:
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

它将拥有您在ApplicationUser类中定义的所有附加属性。

6
已涵盖。请确认是否已经发布了相同的答案(或在现有的答案下添加评论):) - iCollect.it Ltd

1

如果有人在web forms中使用Identity用户,我通过以下方式使其工作:

var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var user = manager.FindById(User.Identity.GetUserId());

0

有一个简单的方法:

User.Identity.Name

它提供了当前用户的ID。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接