【问题标题】:在 dbcontext 中获取当前用户 id
【发布时间】:2022-01-22 06:31:06
【问题描述】:

我正在开发一个 ASP.NET Core Web 应用程序。此应用程序适用于 SQL Server 数据库,我使用的是 Entity Framework 6。

我想在每个实体上自动添加最后修改日期和用户 ID。

我的问题是:如何自动为每个实体设置用户 ID?

我试图覆盖DbContext 中的SaveChanges()。它工作正常,但我无法访问那里的 Microsoft 标识类...

谢谢

【问题讨论】:

  • 您是否将 EF 6 用于完整/旧版/仅限 Windows 的 .NET Framework - 或 EF Core 6 用于较新的 .NET Core平台?你有两个标签 - 请在你的标签中清晰准确!
  • 一个简单的选项是,通常当用户登录到应用程序时,您可以存储一个会话变量,其中包含有关用户的典型详细信息,例如用户 ID、显示名称和在呈现页面时使用的登录时间例如标头,并且可以进行验证以确保会话仍然“活动”或在超时后已复活。这可以提供一个 UserId 来管理 CreatedBy 类型的 SaveChanges 覆盖。要在负载平衡的 Web 服务器上运行时使用这样的方法,您需要会话状态管理器(推荐)或粘性会话。
  • 对不起,我正在使用 EF6 Core。 @StevePy 如何在 dbcontext 文件中读取会话?谢谢
  • 我添加了一个实现示例,该示例用于使用表单身份验证的 Web 应用程序。通用接口允许您为 WinForm 与 WCF 身份验证构建类。我还为 MVC 的 Forms Auth 的会话状态使用会话包装器。用于存储和获取经过身份验证的用户。未显示的一个步骤是成功登录、填充 UserDetails 对象并使用 SessionHelper 设置 CurrentUser 实例。 (应该很容易解决。:)

标签: entity-framework entity-framework-core entity-framework-6 asp.net-identity dbcontext


【解决方案1】:

(如果方法签名看起来有点奇怪,请提前道歉,我是少数使用显式接口实现来帮助避免陈旧代码灰尘兔子乱扔代码的人之一)

使用来自 Forms Authentication Web 应用程序的会话令牌访问会话状态以从 DbContext 中获取用户的示例:

[Serializable]
public class UserDetails
{
    public int UserId { get; set; }
    public string DisplayName { get; set; }
}

public interface ISessionHelper
{
    UserDetails CurrentUser { get; set; }

    void Clear();

    bool IsAuthenticated();
}

public class SessionHelper : ISessionHelper
{
    private const string UserDetailsKey = "YourUniqueSessionIdKey";

    private ISessionHelper This => this;

    UserDetails ISessionHelper.CurrentUser
    {
        get
        {
            try
            {
                var token = (UserDetails)HttpContext.Current.Session[UserDetailsKey];
                return token;
            }
            catch
            {
                throw new ApplicationException("The current session could not be resolved.");
            }
        }
        set
        {
            try
            {
                HttpContext.Current.Session[UserDetailsKey] = value;
            }
            catch
            {
                throw new ApplicationException("The current session state could not be set.");
            }
        }
    }

    /// <summary>
    /// <see cref="ISessionHelper.Clear"/>
    /// </summary>
    void ISessionHelper.Clear()
    {
        HttpContext.Current.Session.Clear();
    }

    /// <summary>
    /// <see cref="ISessionHelper.IsAuthenticated(string)"/>       
    /// </summary>
    bool ISessionHelper.IsAuthenticated()
    {
        try
        {
            var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
            var ticket = FormsAuthentication.Decrypt(cookie.Value);
            var userDetails = This.CurrentUser;

            return (userDetails != null && userDetails.UserId != 0);
        }
        catch
        {
            return false;
        }
    }
}

public interface ICurrentUserLocator
{
    bool IsUserLoggedIn { get; }
    int CurrentUserId { get; }
    string CurrentUserDisplayName { get; }
}

public sealed class FormsAuthenticationUserLocator : ICurrentUserLocator
{
    private const string UnauthorizedAccessExceptionMessage = "No user is currently logged in.";

    private readonly ISessionHelper _sessionHelper = null;

    [ExcludeFromCodeCoverage]
    private ICurrentUserLocator This => this;

    private UserDetails _userDetails = null;
    [ExcludeFromCodeCoverage]
    private UserDetails CurrentUser
    {
        get { return _userDetails ?? (_userDetails = SessionHelper.UserDetails); }
    }

    [ExcludeFromCodeCoverage]
    bool ICurrentUserLocator.IsUserLoggedIn => CurrentUser != null;

    [ExcludeFromCodeCoverage]
    int ICurrentUserLocator.CurrentUserId => CurrentUser?.UserId ?? throw new UnauthorizedAccessException(UnauthorizedAccessExceptionMessage);

    [ExcludeFromCodeCoverage]
    string ICurrentUserLocator.CurrentUserDisplayName => CurrentUser?.DisplayName ?? throw new UnauthorizedAccessException(UnauthorizedAccessExceptionMessage);

   public FormsAuthenticationUserLocator(ISessionHelper sessionHelper)
    {
        _sessionHelper = sessionHelper ?? throw new ArgumentNullException("sessioNHelper");
    }
}

假设您在容器中使用依赖注入,请在每个请求的生命周期内针对容器中各自的接口注册这两个类。然后在 DbContext 中添加对 ICurrentUserLocator 的依赖:

public class AppDbContext : DbContext
{
     private readonly ICurrentUserLocator _currentUserLocator = null;


     public AppDbContext(ICurrentUserLocator currentUserLocator)
     {
         _currentUserLocator = currentUserLocator ?? throw new ArgumentNullException("currentUserLocator");
     }
}

现在您可以在覆盖的 SaveChanges 中获取当前用户 ID:

            var currentUser = Users.Single(x => x.UserId == _currentUserLocator.CurrentUserId);
            var updatedEntities = ChangeTracker.Entries()
                .Where(x => x.State == EntityState.Modified)
                .Select(x => x.Entity)
                .Cast<EditableEntityBase>();
            foreach (var entity in updatedEntities)
            {
                entity.LastModifiedBy = currentUser;
                entity.LastModifiedAt = DateTime.Now;
            }

            var insertedEntities = ChangeTracker.Entries()
                .Where(x => x.State == EntityState.Added)
                .Select(x => x.Entity)
                .Cast<EditableEntityBase>();
            foreach (var entity in insertedEntities)
            {
                entity.CreatedBy = entity.LastModifiedBy = currentUser;
                entity.CreatedAt = entity.LastModifiedAt = DateTime.Now;
            }

如果您只公开 FK 而不是用户导航属性,那么您只需设置 ID。如果您的应用具有注销功能,请务必调用 SessionHelper.Clear() 方法。在这个例子中,我修改了我通常使用的会话助手,只关注当前用户的详细信息,以保持简单。它通常会跟踪一些其他细节,因此 Clear() 方法会擦除整个会话。它是用于处理已知会话状态的标准化包装器,而不是到处乱扔Session["somestring"] 的代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-22
    • 2023-03-06
    • 2017-04-26
    • 2015-06-01
    相关资源
    最近更新 更多